aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@cc.helsinki.fi>1993-12-23 11:51:31 +0000
committerNicolas Pitre <nico@cam.org>2007-08-19 14:19:23 -0400
commit76123c5282eca4effe5fbddf2beb64f30c8a8739 (patch)
tree54b100dd8bcf7cc81fcf05f649a233452b736f18
parentfb11be238769030d3e880b6cad3fc9e5e593495c (diff)
downloadarchive-76123c5282eca4effe5fbddf2beb64f30c8a8739.tar.gz
ALPHA-pl14g
-rw-r--r--Makefile2
-rw-r--r--config.in2
-rw-r--r--drivers/char/console.c31
-rw-r--r--drivers/char/serial.c433
-rw-r--r--drivers/char/tty_io.c114
-rw-r--r--drivers/char/tty_ioctl.c53
-rw-r--r--drivers/net/3c501.c3
-rw-r--r--drivers/net/ne.c13
-rw-r--r--drivers/net/smc-ultra.c29
-rw-r--r--drivers/net/wd.c4
-rw-r--r--drivers/scsi/aha1542.c122
-rw-r--r--drivers/scsi/aha1542.h3
-rw-r--r--drivers/scsi/scsi.c2
-rw-r--r--drivers/scsi/sr.c10
-rw-r--r--drivers/sound/.blurb6
-rw-r--r--drivers/sound/CHANGELOG21
-rw-r--r--drivers/sound/Makefile30
-rw-r--r--drivers/sound/Readme323
-rw-r--r--drivers/sound/Readme.linux247
-rw-r--r--drivers/sound/adlib_card.c3
-rw-r--r--drivers/sound/audio.c2
-rw-r--r--drivers/sound/configure.c85
-rw-r--r--drivers/sound/dev_table.c43
-rw-r--r--drivers/sound/dev_table.h9
-rw-r--r--drivers/sound/dmabuf.c27
-rw-r--r--drivers/sound/gus_card.c4
-rw-r--r--drivers/sound/gus_midi.c2
-rw-r--r--drivers/sound/gus_wave.c80
-rw-r--r--drivers/sound/midibuf.c2
-rw-r--r--drivers/sound/mpu401.c2
-rw-r--r--drivers/sound/opl3.c18
-rw-r--r--drivers/sound/os.h26
-rw-r--r--drivers/sound/pas.h1
-rw-r--r--drivers/sound/pas2_card.c4
-rw-r--r--drivers/sound/pas2_midi.c4
-rw-r--r--drivers/sound/pas2_mixer.c2
-rw-r--r--drivers/sound/pas2_pcm.c4
-rw-r--r--drivers/sound/patmgr.c10
-rw-r--r--drivers/sound/sb16_dsp.c597
-rw-r--r--drivers/sound/sb16_midi.c314
-rw-r--r--drivers/sound/sb_card.c3
-rw-r--r--drivers/sound/sb_dsp.c41
-rw-r--r--drivers/sound/sb_mixer.c26
-rw-r--r--drivers/sound/sb_mixer.h33
-rw-r--r--drivers/sound/sequencer.c33
-rw-r--r--drivers/sound/sound_calls.h21
-rw-r--r--drivers/sound/sound_config.h4
-rw-r--r--drivers/sound/sound_switch.c417
-rw-r--r--drivers/sound/soundcard.c342
-rw-r--r--fs/Makefile8
-rw-r--r--fs/binfmt_coff.c767
-rw-r--r--fs/binfmt_elf.c2
-rw-r--r--fs/exec.c21
-rw-r--r--fs/filesystems.c8
-rw-r--r--fs/isofs/inode.c58
-rw-r--r--fs/isofs/rock.c13
-rw-r--r--fs/nfs/proc.c2
-rw-r--r--fs/proc/net.c8
-rw-r--r--fs/sysv/INTRO189
-rw-r--r--fs/sysv/Makefile31
-rw-r--r--fs/sysv/README42
-rw-r--r--fs/sysv/balloc.c349
-rw-r--r--fs/sysv/dir.c110
-rw-r--r--fs/sysv/file.c313
-rw-r--r--fs/sysv/fsync.c200
-rw-r--r--fs/sysv/ialloc.c203
-rw-r--r--fs/sysv/inode.c808
-rw-r--r--fs/sysv/namei.c836
-rw-r--r--fs/sysv/symlink.c110
-rw-r--r--fs/sysv/truncate.c508
-rw-r--r--include/linux/binfmts.h2
-rw-r--r--include/linux/coff.h351
-rw-r--r--include/linux/fs.h4
-rw-r--r--include/linux/kernel.h2
-rw-r--r--include/linux/major.h6
-rw-r--r--include/linux/msg.h3
-rw-r--r--include/linux/serial.h7
-rw-r--r--include/linux/signal.h4
-rw-r--r--include/linux/soundcard.h10
-rw-r--r--include/linux/sysv_fs.h437
-rw-r--r--include/linux/sysv_fs_i.h20
-rw-r--r--include/linux/sysv_fs_sb.h104
-rw-r--r--include/linux/tty.h11
-rw-r--r--kernel/Makefile6
-rw-r--r--kernel/exit.c20
-rw-r--r--kernel/ksyms.S6
-rw-r--r--kernel/module.c2
-rw-r--r--mm/memory.c34
-rw-r--r--net/inet/raw.c4
-rw-r--r--net/inet/route.c4
90 files changed, 8073 insertions, 1157 deletions
diff --git a/Makefile b/Makefile
index 020b99c..467a14a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 0.99
PATCHLEVEL = 14
-ALPHA = f
+ALPHA = g
all: Version zImage
diff --git a/config.in b/config.in
index e2c431a..aed82ea 100644
--- a/config.in
+++ b/config.in
@@ -16,6 +16,7 @@ bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
* Program binary formats
*
bool 'Elf executables' CONFIG_BINFMT_ELF y
+bool 'COFF executables' CONFIG_BINFMT_COFF y
*
* SCSI support
*
@@ -98,6 +99,7 @@ bool '/proc filesystem support' CONFIG_PROC_FS y
bool 'NFS filesystem support' CONFIG_NFS_FS y
bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
+bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
*
* character devices
*
diff --git a/drivers/char/console.c b/drivers/char/console.c
index c6c3cf7..a19a7a5 100644
--- a/drivers/char/console.c
+++ b/drivers/char/console.c
@@ -1468,6 +1468,37 @@ static void get_scrmem(int currcons)
static void set_scrmem(int currcons)
{
+#ifdef CONFIG_HGA
+ /* This works with XFree86 1.2, 1.3 and 2.0
+ This code could be extended and made more generally useful if we could
+ determine the actual video mode. It appears that this should be
+ possible on a genuine Hercules card, but I (WM) haven't been able to
+ read from any of the required registers on my clone card.
+ */
+ /* This code should work with Hercules and MDA cards. */
+ if (video_type == VIDEO_TYPE_MDA)
+ {
+ if (vcmode == KD_TEXT)
+ {
+ /* Ensure that the card is in text mode. */
+ int i;
+ static char herc_txt_tbl[12] = {
+ 0x61,0x50,0x52,0x0f,0x19,6,0x19,0x19,2,0x0d,0x0b,0x0c };
+ outb_p(0, 0x3bf); /* Back to power-on defaults */
+ outb_p(0, 0x3b8); /* Blank the screen, select page 0, etc */
+ for ( i = 0 ; i < 12 ; i++ )
+ {
+ outb_p(i, 0x3b4);
+ outb_p(herc_txt_tbl[i], 0x3b5);
+ }
+ }
+#define HGA_BLINKER_ON 0x20
+#define HGA_SCREEN_ON 8
+ /* Make sure that the hardware is not blanked */
+ outb_p(HGA_BLINKER_ON | HGA_SCREEN_ON, 0x3b8);
+ }
+#endif CONFIG_HGA
+
video_mem_start = video_mem_base;
video_mem_end = video_mem_term;
origin = video_mem_start;
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
index d2b6286..172d8db 100644
--- a/drivers/char/serial.c
+++ b/drivers/char/serial.c
@@ -62,7 +62,11 @@
/*
* rs_event - Bitfield of serial lines that events pending
* to be processed at the next clock tick.
- *
+ * IRQ_timeout - How long the timeout should be for each IRQ
+ * should be after the IRQ has been active.
+ * IRQ_timer - Array of timeout values for each interrupt IRQ.
+ * This is based on jiffies; not offsets.
+ *
* We assume here that int's are 32 bits, so an array of two gives us
* 64 lines, which is the maximum we can support.
*/
@@ -77,6 +81,7 @@ static volatile int rs_triggered;
static int rs_wild_int_mask;
static void autoconfig(struct async_struct * info);
+static void change_speed(unsigned int line);
/*
* This assumes you have a 1.8432 MHz clock for your UART.
@@ -350,6 +355,8 @@ static inline void receive_chars(struct async_struct *info,
queue->buf[head++]= TTY_PARITY;
else if (*status & UART_LSR_FE)
queue->buf[head++]= TTY_FRAME;
+ else if (*status & UART_LSR_OE)
+ queue->buf[head++]= TTY_OVERRUN;
head &= TTY_BUF_SIZE-1;
}
queue->buf[head++] = ch;
@@ -411,22 +418,27 @@ static inline int check_modem_status(struct async_struct *info)
status = serial_in(info, UART_MSR);
if ((status & UART_MSR_DDCD) && !C_LOCAL(info->tty)) {
-#ifdef SERIAL_DEBUG_INTR
- printk("DDCD...");
-#endif
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+ printk("ttys%d CD now %s...", info->line,
+ (status & UART_MSR_DCD) ? "on" : "off");
+#endif
if (status & UART_MSR_DCD)
rs_sched_event(info, RS_EVENT_OPEN_WAKEUP);
else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
- (info->flags & ASYNC_CALLOUT_NOHUP)))
+ (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("scheduling hangup...");
+#endif
rs_sched_event(info, RS_EVENT_HANGUP);
+ }
}
if (C_RTSCTS(info->tty)) {
- if (info->tty->stopped) {
+ if (info->tty->hw_stopped) {
if (status & UART_MSR_CTS) {
#ifdef SERIAL_DEBUG_INTR
printk("CTS tx start...");
#endif
- info->tty->stopped = 0;
+ info->tty->hw_stopped = 0;
rs_start(info->tty);
return 1;
}
@@ -435,7 +447,7 @@ static inline int check_modem_status(struct async_struct *info)
#ifdef SERIAL_DEBUG_INTR
printk("CTS tx stop...");
#endif
- info->tty->stopped = 1;
+ info->tty->hw_stopped = 1;
rs_stop(info->tty);
}
}
@@ -489,9 +501,11 @@ static void rs_interrupt(int irq)
}
recheck_count = 0;
recheck_write:
- if ((status & UART_LSR_THRE) &&
- !info->tty->stopped) {
- transmit_chars(info, &done_work);
+ if (status & UART_LSR_THRE) {
+ wake_up_interruptible(&info->xmit_wait);
+ if (!info->tty->stopped &&
+ !info->tty->hw_stopped)
+ transmit_chars(info, &done_work);
}
if (check_modem_status(info) &&
(recheck_count++ <= 64))
@@ -609,6 +623,9 @@ static void rs_timer(void)
if ((mask & IRQ_active) && (IRQ_timer[i] <= jiffies)) {
IRQ_active &= ~mask;
cli();
+#ifdef SERIAL_DEBUG_TIMER
+ printk("rs_timer: rs_interrupt(%d)...", i);
+#endif
rs_interrupt(i);
sti();
}
@@ -695,50 +712,56 @@ static void figure_IRQ_timeout(int irq)
IRQ_timeout[irq] = timeout ? timeout : 1;
}
-static inline void unlink_port(struct async_struct *info)
-{
-#ifdef SERIAL_DEBUG_OPEN
- printk("unlinking serial port %d from irq %d....", info->line,
- info->irq);
-#endif
- if (info->next_port)
- info->next_port->prev_port = info->prev_port;
- if (info->prev_port)
- info->prev_port->next_port = info->next_port;
- else
- IRQ_ports[info->irq] = info->next_port;
- figure_IRQ_timeout(info->irq);
-}
-
-static inline void link_port(struct async_struct *info)
-{
-#ifdef SERIAL_DEBUG_OPEN
- printk("linking serial port %d into irq %d...", info->line,
- info->irq);
-#endif
- info->prev_port = 0;
- info->next_port = IRQ_ports[info->irq];
- if (info->next_port)
- info->next_port->prev_port = info;
- IRQ_ports[info->irq] = info;
- figure_IRQ_timeout(info->irq);
-}
-
-static void startup(struct async_struct * info)
+static int startup(struct async_struct * info, int get_irq)
{
unsigned short ICP;
unsigned long flags;
+ struct sigaction sa;
+ int retval;
+
+ if (info->flags & ASYNC_INITIALIZED)
+ return 0;
+
+ if (!info->port || !info->type) {
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+ return 0;
+ }
save_flags(flags); cli();
+#ifdef SERIAL_DEBUG_OPEN
+ printk("starting up ttys%d (irq %d)...", info->line, info->irq);
+#endif
+
/*
- * First, clear the FIFO buffers and disable them
+ * Allocate the IRQ if necessary
*/
- if (info->type == PORT_16550A)
- serial_outp(info, UART_FCR, UART_FCR_CLEAR_CMD);
+ if (get_irq && info->irq && !IRQ_ports[info->irq]) {
+ sa.sa_handler = rs_interrupt;
+ sa.sa_flags = (SA_INTERRUPT);
+ sa.sa_mask = 0;
+ sa.sa_restorer = NULL;
+ retval = irqaction(info->irq,&sa);
+ if (retval) {
+ restore_flags(flags);
+ return retval;
+ }
+ }
/*
- * Next, clear the interrupt registers.
+ * Clear the FIFO buffers and disable them
+ * (they will be reenabled in change_speed())
+ */
+ if (info->type == PORT_16550A) {
+ serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT));
+ info->xmit_fifo_size = 16;
+ } else
+ info->xmit_fifo_size = 1;
+
+ /*
+ * Clear the interrupt registers.
*/
(void)serial_inp(info, UART_LSR);
(void)serial_inp(info, UART_RX);
@@ -756,16 +779,6 @@ static void startup(struct async_struct * info)
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
/*
- * Enable FIFO's if necessary
- */
- if (info->type == PORT_16550A) {
- serial_outp(info, UART_FCR, UART_FCR_SETUP_CMD);
- info->xmit_fifo_size = 16;
- } else {
- info->xmit_fifo_size = 1;
- }
-
- /*
* Finally, enable interrupts
*/
#ifdef ISR_HACK
@@ -791,7 +804,6 @@ static void startup(struct async_struct * info)
(void)serial_inp(info, UART_IIR);
(void)serial_inp(info, UART_MSR);
- info->flags |= ASYNC_INITIALIZED;
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
/*
@@ -803,18 +815,68 @@ static void startup(struct async_struct * info)
else
info->read_status_mask = (UART_LSR_OE | UART_LSR_BI |
UART_LSR_FE);
+
+ /*
+ * Insert serial port into IRQ chain.
+ */
+ info->prev_port = 0;
+ info->next_port = IRQ_ports[info->irq];
+ if (info->next_port)
+ info->next_port->prev_port = info;
+ IRQ_ports[info->irq] = info;
+ figure_IRQ_timeout(info->irq);
+
+ /*
+ * Set up serial timers...
+ */
+ IRQ_active |= 1 << info->irq;
+ figure_RS_timer();
+
+ /*
+ * and set the speed of the serial port
+ */
+ change_speed(info->line);
+
+ info->flags |= ASYNC_INITIALIZED;
restore_flags(flags);
+ return 0;
}
/*
- * This routine shutsdown a serial port; interrupts are disabled, and
+ * This routine will shutdown a serial port; interrupts are disabled, and
* DTR is dropped if the hangup on close termio flag is on.
*/
-static void shutdown(struct async_struct * info)
+static void shutdown(struct async_struct * info, int do_free_irq)
{
unsigned long flags;
- save_flags(flags); cli();
+ if (!(info->flags & ASYNC_INITIALIZED))
+ return;
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("Shutting down serial port %d (irq %d)....", info->line,
+ info->irq);
+#endif
+
+ save_flags(flags); cli(); /* Disable interrupts */
+
+ /*
+ * First unlink the serial port from the IRQ chain...
+ */
+ if (info->next_port)
+ info->next_port->prev_port = info->prev_port;
+ if (info->prev_port)
+ info->prev_port->next_port = info->next_port;
+ else
+ IRQ_ports[info->irq] = info->next_port;
+ figure_IRQ_timeout(info->irq);
+
+ /*
+ * Free the IRQ, if necessary
+ */
+ if (do_free_irq && info->irq && !IRQ_ports[info->irq])
+ free_irq(info->irq);
+
info->IER = 0;
serial_outp(info, UART_IER, 0x00); /* disable all intrs */
if (info->flags & ASYNC_FOURPORT) {
@@ -826,11 +888,16 @@ static void shutdown(struct async_struct * info)
else
/* reset DTR,RTS,OUT_2 */
serial_outp(info, UART_MCR, 0x00);
- serial_outp(info, UART_FCR, UART_FCR_CLEAR_CMD); /* disable FIFO's */
+
+ /* disable FIFO's */
+ serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT));
(void)serial_in(info, UART_RX); /* read data port to reset things */
- info->flags &= ~ASYNC_INITIALIZED;
+
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ASYNC_INITIALIZED;
restore_flags(flags);
}
@@ -843,7 +910,7 @@ static void change_speed(unsigned int line)
struct async_struct * info;
unsigned short port;
int quot = 0;
- unsigned cflag,cval,mcr;
+ unsigned cflag,cval,mcr,fcr;
int i;
if (line >= NR_PORTS)
@@ -878,9 +945,9 @@ static void change_speed(unsigned int line)
}
cli();
mcr = serial_in(info, UART_MCR);
- if (quot)
+ if (quot) {
serial_out(info, UART_MCR, mcr | UART_MCR_DTR);
- else {
+ } else {
serial_out(info, UART_MCR, mcr & ~UART_MCR_DTR);
sti();
return;
@@ -893,11 +960,20 @@ static void change_speed(unsigned int line)
cval |= UART_LCR_PARITY;
if (!(cflag & PARODD))
cval |= UART_LCR_EPAR;
+ if (info->type == PORT_16550A) {
+ if ((info->baud_base / quot) < 2400)
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+ else
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+ } else
+ fcr = 0;
+
cli();
serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */
serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */
serial_outp(info, UART_LCR, cval); /* reset DLAB */
+ serial_outp(info, UART_FCR, fcr); /* set fcr */
sti();
}
@@ -950,10 +1026,10 @@ void rs_write(struct tty_struct * tty)
{
struct async_struct *info;
- if (!tty || tty->stopped)
+ if (!tty || tty->stopped || tty->hw_stopped)
return;
info = rs_table + DEV_TO_SL(tty->line);
- if (!info)
+ if (!info || !info->tty || !(info->flags & ASYNC_INITIALIZED))
return;
cli();
restart_port(info);
@@ -1068,7 +1144,6 @@ static int set_serial_info(struct async_struct * info,
info->flags = ((info->flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK));
info->custom_divisor = new_serial.custom_divisor;
- new_serial.port = 0; /* Prevent initialization below */
goto check_and_exit;
}
@@ -1124,31 +1199,22 @@ static int set_serial_info(struct async_struct * info,
* We need to shutdown the serial port at the old
* port/irq combination.
*/
- if (info->flags & ASYNC_INITIALIZED) {
- shutdown(info);
- unlink_port(info);
- if (change_irq && info->irq && !IRQ_ports[info->irq])
- free_irq(info->irq);
- }
+ shutdown(info, change_irq);
info->irq = new_serial.irq;
info->port = new_serial.port;
info->hub6 = new_serial.hub6;
}
check_and_exit:
- if (info->port && info->type &&
- !(info->flags & ASYNC_INITIALIZED)) {
- /*
- * Link the port into the new interrupt chain.
- */
- link_port(info);
- startup(info);
- change_speed(info->line);
- } else if (((old_info.flags & ASYNC_SPD_MASK) !=
- (info->flags & ASYNC_SPD_MASK)) ||
- (old_info.custom_divisor != info->custom_divisor))
- change_speed(info->line);
-
+ if (!info->port || !info->type)
+ return 0;
+ if (info->flags & ASYNC_INITIALIZED) {
+ if (((old_info.flags & ASYNC_SPD_MASK) !=
+ (info->flags & ASYNC_SPD_MASK)) ||
+ (old_info.custom_divisor != info->custom_divisor))
+ change_speed(info->line);
+ } else
+ (void) startup(info, 0);
return 0;
}
@@ -1210,7 +1276,6 @@ static int set_modem_info(struct async_struct * info, unsigned int cmd,
static int do_autoconfig(struct async_struct * info)
{
- struct sigaction sa;
int retval;
if (!suser())
@@ -1219,31 +1284,15 @@ static int do_autoconfig(struct async_struct * info)
if (info->count > 1)
return -EBUSY;
- if (info->flags & ASYNC_INITIALIZED) {
- shutdown(info);
- unlink_port(info);
- if (info->irq && !IRQ_ports[info->irq])
- free_irq(info->irq);
- }
+ shutdown(info, 1);
cli();
autoconfig(info);
sti();
- if (info->port && info->type) {
- if (info->irq && !IRQ_ports[info->irq]) {
- sa.sa_handler = rs_interrupt;
- sa.sa_flags = (SA_INTERRUPT);
- sa.sa_mask = 0;
- sa.sa_restorer = NULL;
- retval = irqaction(info->irq,&sa);
- if (retval)
- return retval;
- }
- link_port(info);
- startup(info);
- change_speed(info->line);
- }
+ retval = startup(info, 1);
+ if (retval)
+ return retval;
return 0;
}
@@ -1398,7 +1447,7 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS)) {
- tty->stopped = 0;
+ tty->hw_stopped = 0;
rs_write(tty);
}
@@ -1429,6 +1478,9 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
struct async_struct * info;
int line;
+ if (tty_hung_up_p(filp))
+ return;
+
line = DEV_TO_SL(tty->line);
if ((line < 0) || (line >= NR_PORTS))
return;
@@ -1438,14 +1490,35 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
#endif
if (--info->count > 0)
return;
+ info->flags |= ASYNC_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ info->normal_termios = *tty->termios;
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ info->callout_termios = *tty->termios;
tty->stopped = 0; /* Force flush to succeed */
+ tty->hw_stopped = 0;
rs_start(tty);
wait_until_sent(tty);
+ cli();
+ /*
+ * Make sure the UART transmitter has completely drained; this
+ * is especially important if there is a transmit FIFO!
+ */
+ if (!(serial_inp(info, UART_LSR) & UART_LSR_THRE)) {
+ rs_start(tty); /* Make sure THRI interrupt enabled */
+ interruptible_sleep_on(&info->xmit_wait);
+ }
+ sti();
+ shutdown(info, 1);
clear_bit(line, rs_event);
info->event = 0;
info->count = 0;
+ info->tty = 0;
if (info->blocked_open) {
- shutdown(info);
if (info->close_delay) {
tty->count++; /* avoid race condition */
current->state = TASK_INTERRUPTIBLE;
@@ -1453,20 +1526,33 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
schedule();
tty->count--;
}
- startup(info);
- info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
- if (tty->termios->c_cflag & CLOCAL)
- wake_up_interruptible(&info->open_wait);
- return;
- }
- if (info->flags & ASYNC_INITIALIZED) {
- shutdown(info);
- unlink_port(info);
- if (info->irq && !IRQ_ports[info->irq])
- free_irq(info->irq);
+ wake_up_interruptible(&info->open_wait);
}
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+ ASYNC_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void rs_hangup(struct tty_struct *tty)
+{
+ struct async_struct * info;
+ int line;
+
+ line = DEV_TO_SL(tty->line);
+ if ((line < 0) || (line >= NR_PORTS))
+ return;
+ info = rs_table + line;
+
+ shutdown(info, 1);
+ clear_bit(line, rs_event);
+ info->event = 0;
+ info->count = 0;
info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
info->tty = 0;
+ wake_up_interruptible(&info->open_wait);
}
/*
@@ -1480,9 +1566,16 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
struct wait_queue wait = { current, NULL };
int retval;
int do_clocal = C_LOCAL(tty);
- struct termios orig_termios;
- int tty_line = tty->line;
-
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (info->flags & ASYNC_CLOSING) {
+ interruptible_sleep_on(&info->close_wait);
+ return -ERESTARTNOINTR;
+ }
+
/*
* If this is a callout device, then just make sure the normal
* device isn't being used.
@@ -1528,27 +1621,27 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
#endif
info->count--;
info->blocked_open++;
- memset(&orig_termios, 0, sizeof(orig_termios));
- if (tty_termios[tty_line])
- orig_termios = *tty_termios[tty_line];
while (1) {
cli();
if (!(info->flags & ASYNC_CALLOUT_ACTIVE))
serial_out(info, UART_MCR,
- serial_inp(info, UART_MCR) | UART_MCR_DTR);
+ serial_inp(info, UART_MCR) |
+ (UART_MCR_DTR | UART_MCR_RTS));
sti();
current->state = TASK_INTERRUPTIBLE;
- if (tty_hung_up_p(filp) && (info->flags & ASYNC_HUP_NOTIFY)) {
- retval = -EAGAIN;
+ if (tty_hung_up_p(filp) ||
+ !(info->flags & ASYNC_INITIALIZED)) {
+ if (info->flags & ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTNOINTR;
break;
}
if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ !(info->flags & ASYNC_CLOSING) &&
(do_clocal || (serial_in(info, UART_MSR) &
- UART_MSR_DCD))) {
- if (tty_hung_up_p(filp))
- retval = -ERESTARTNOINTR;
+ UART_MSR_DCD)))
break;
- }
if (current->signal & ~current->blocked) {
retval = -ERESTARTSYS;
break;
@@ -1561,7 +1654,8 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
}
current->state = TASK_RUNNING;
remove_wait_queue(&info->open_wait, &wait);
- info->count++;
+ if (!tty_hung_up_p(filp))
+ info->count++;
info->blocked_open--;
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready after blocking: ttys%d, count = %d\n",
@@ -1570,9 +1664,6 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
if (retval)
return retval;
info->flags |= ASYNC_NORMAL_ACTIVE;
- if ((info->flags & ASYNC_TERMIOS_RESTORE) &&
- tty_termios[tty_line])
- *tty_termios[tty_line] = orig_termios;
return 0;
}
@@ -1586,7 +1677,6 @@ int rs_open(struct tty_struct *tty, struct file * filp)
{
struct async_struct *info;
int retval, line;
- struct sigaction sa;
line = DEV_TO_SL(tty->line);
if ((line < 0) || (line >= NR_PORTS))
@@ -1605,42 +1695,35 @@ int rs_open(struct tty_struct *tty, struct file * filp)
tty->set_termios = rs_set_termios;
tty->stop = rs_stop;
tty->start = rs_start;
-
- if (!(info->flags & ASYNC_INITIALIZED)) {
- if (!info->port || !info->type) {
- set_bit(TTY_IO_ERROR, &tty->flags);
- return 0;
- }
- if (info->irq && !IRQ_ports[info->irq]) {
- sa.sa_handler = rs_interrupt;
- sa.sa_flags = (SA_INTERRUPT);
- sa.sa_mask = 0;
- sa.sa_restorer = NULL;
- retval = irqaction(info->irq,&sa);
- if (retval)
- return retval;
- }
- /*
- * Link in port to IRQ chain
- */
- link_port(info);
- startup(info);
- change_speed(info->line);
- if (!info->irq) {
- IRQ_active |= info->line;
- cli();
- figure_RS_timer();
- sti();
- }
+ tty->hangup = rs_hangup;
+ if (info->flags & ASYNC_SPLIT_TERMIOS) {
+ if (info->flags & ASYNC_NORMAL_ACTIVE)
+ *tty->termios = info->normal_termios;
+ if (info->flags & ASYNC_CALLOUT_ACTIVE)
+ *tty->termios = info->callout_termios;
}
+ /*
+ * Start up serial port
+ */
+ retval = startup(info, 1);
+ if (retval)
+ return retval;
retval = block_til_ready(tty, filp, info);
- if (retval)
+ if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open returning after block_til_ready with %d\n",
+ retval);
+#endif
return retval;
+ }
info->session = current->session;
info->pgrp = current->pgrp;
-
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open ttys%d successful...", info->line);
+#endif
return 0;
}
@@ -1659,7 +1742,7 @@ int rs_open(struct tty_struct *tty, struct file * filp)
*/
static void show_serial_version(void)
{
- printk("Serial driver version 3.96 with");
+ printk("Serial driver version 3.99a with");
#ifdef CONFIG_AST_FOURPORT
printk(" AST_FOURPORT");
#define SERIAL_OPT
@@ -1868,7 +1951,15 @@ static void autoconfig(struct async_struct * info)
if ((status1 != 0xa5) || (status2 != 0x5a))
info->type = PORT_8250;
}
- shutdown(info);
+
+ /*
+ * Reset the UART.
+ */
+ serial_outp(info, UART_MCR, 0x00);
+ serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT));
+ (void)serial_in(info, UART_RX);
+
restore_flags(flags);
}
@@ -1905,7 +1996,11 @@ long rs_init(long kmem_start)
info->event = 0;
info->count = 0;
info->blocked_open = 0;
+ memset(&info->callout_termios, 0, sizeof(struct termios));
+ memset(&info->normal_termios, 0, sizeof(struct termios));
info->open_wait = 0;
+ info->xmit_wait = 0;
+ info->close_wait = 0;
info->next_port = 0;
info->prev_port = 0;
if (info->irq == 2)
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 9a4478b..1a2ac8b 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -224,36 +224,24 @@ static struct file_operations hung_up_tty_fops = {
hung_up_tty_write,
NULL, /* hung_up_tty_readdir */
hung_up_tty_select,
- tty_ioctl,
- NULL, /* hung_up_tty_mmap */
- tty_open,
- tty_release
-};
-
-static struct file_operations vhung_up_tty_fops = {
- tty_lseek,
- hung_up_tty_read,
- hung_up_tty_write,
- NULL, /* hung_up_tty_readdir */
- hung_up_tty_select,
hung_up_tty_ioctl,
NULL, /* hung_up_tty_mmap */
- tty_open,
- tty_release
+ NULL, /* hung_up_tty_open */
+ tty_release /* hung_up_tty_release */
};
void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
{
int i;
struct file * filp;
- struct task_struct **p;
+ struct task_struct *p;
int dev;
if (!tty)
return;
dev = 0x0400 + tty->line;
for (filp = first_file, i=0; i<nr_files; i++, filp = filp->f_next) {
- if (!filp->f_count)
+ if (!filp->f_count)
continue;
if (filp->f_rdev != dev)
continue;
@@ -263,38 +251,71 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
continue;
filp->f_op = fops;
}
+ flush_input(tty);
+ flush_output(tty);
wake_up_interruptible(&tty->secondary.proc_list);
- wake_up_interruptible(&tty->read_q.proc_list);
- wake_up_interruptible(&tty->write_q.proc_list);
if (tty->session > 0)
kill_sl(tty->session,SIGHUP,1);
tty->session = 0;
tty->pgrp = -1;
- for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
- if ((*p) && (*p)->tty == tty->line)
- (*p)->tty = -1;
+ for_each_task(p) {
+ if (p->tty == tty->line)
+ p->tty = -1;
}
+ if (tty->hangup)
+ (tty->hangup)(tty);
}
void tty_hangup(struct tty_struct * tty)
{
+#ifdef TTY_DEBUG_HANGUP
+ printk("tty%d hangup...\n", tty->line);
+#endif
do_tty_hangup(tty, &hung_up_tty_fops);
}
void tty_vhangup(struct tty_struct * tty)
{
- do_tty_hangup(tty, &vhung_up_tty_fops);
+#ifdef TTY_DEBUG_HANGUP
+ printk("tty%d vhangup...\n", tty->line);
+#endif
+ do_tty_hangup(tty, &hung_up_tty_fops);
}
-void tty_unhangup(struct file *filp)
+int tty_hung_up_p(struct file * filp)
{
- filp->f_op = &tty_fops;
+ return (filp->f_op == &hung_up_tty_fops);
}
-int tty_hung_up_p(struct file * filp)
+/*
+ * This function is typically called only by the session leader, when
+ * it wants to dissassociate itself from its controlling tty.
+ *
+ * It performs the following functions:
+ * (1) Sends a SIGHUP to the foreground process group
+ * (2) Clears the tty from being controlling the session
+ * (3) Clears the controlling tty for all processes in the
+ * session group.
+ */
+void disassociate_ctty(int priv)
{
- return ((filp->f_op == &hung_up_tty_fops) ||
- (filp->f_op == &vhung_up_tty_fops));
+ struct tty_struct *tty;
+ struct task_struct *p;
+
+ if (current->tty >= 0) {
+ tty = tty_table[current->tty];
+ if (tty) {
+ if (tty->pgrp > 0)
+ kill_pg(tty->pgrp, SIGHUP, priv);
+ tty->session = 0;
+ tty->pgrp = -1;
+ } else
+ printk("disassociate_ctty: ctty is NULL?!?");
+ }
+
+ for_each_task(p)
+ if (p->session == current->session)
+ p->tty = -1;
}
/*
@@ -514,7 +535,7 @@ void copy_to_cooked(struct tty_struct * tty)
break;
}
if (special_flag) {
- tty->char_error = c & 3;
+ tty->char_error = c & 7;
continue;
}
if (tty->char_error) {
@@ -529,7 +550,12 @@ void copy_to_cooked(struct tty_struct * tty)
put_tty_queue('\0', &tty->secondary);
continue;
}
- /* If not a break, then a parity or frame error */
+ if (tty->char_error == TTY_OVERRUN) {
+ tty->char_error = 0;
+ printk("tty%d: input overrun\n", tty->line);
+ continue;
+ }
+ /* Must be a parity or frame error */
tty->char_error = 0;
if (I_IGNPAR(tty)) {
continue;
@@ -1204,6 +1230,9 @@ static void release_dev(int dev, struct file * filp)
printk("release_dev: tty_termios[%d] was NULL\n", dev);
return;
}
+#ifdef TTY_DEBUG_HANGUP
+ printk("release_dev of tty%d (tty count=%d)...", dev, tty->count);
+#endif
if (IS_A_PTY(dev)) {
o_tty = tty_table[PTY_OTHER(dev)];
o_tp = tty_termios[PTY_OTHER(dev)];
@@ -1237,6 +1266,10 @@ static void release_dev(int dev, struct file * filp)
}
if (tty->count)
return;
+
+#ifdef TTY_DEBUG_HANGUP
+ printk("freeing tty structure...");
+#endif
/*
* Make sure there aren't any processes that still think this
@@ -1247,9 +1280,15 @@ static void release_dev(int dev, struct file * filp)
(*p)->tty = -1;
}
+ /*
+ * Shutdown the current line discipline, and reset it to
+ * N_TTY.
+ */
if (ldiscs[tty->disc].close != NULL)
ldiscs[tty->disc].close(tty);
-
+ tty->disc = N_TTY;
+ tty->termios->c_line = N_TTY;
+
if (o_tty) {
if (o_tty->count)
return;
@@ -1298,7 +1337,7 @@ static int tty_open(struct inode * inode, struct file * filp)
major = TTY_MAJOR;
minor = current->tty;
}
- noctty = 1;
+ /* noctty = 1; */
} else if (major == TTY_MAJOR) {
if (!minor) {
minor = fg_console + 1;
@@ -1317,6 +1356,11 @@ static int tty_open(struct inode * inode, struct file * filp)
if (retval)
return retval;
tty = tty_table[minor];
+#ifdef TTY_DEBUG_HANGUP
+ printk("opening tty%d...", tty->line);
+#endif
+ if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
+ return -EBUSY;
/* clean up the packet stuff. */
/*
@@ -1337,6 +1381,10 @@ static int tty_open(struct inode * inode, struct file * filp)
retval = -ENODEV;
}
if (retval) {
+#ifdef TTY_DEBUG_HANGUP
+ printk("error %d in opening tty%d...", retval, tty->line);
+#endif
+
release_dev(minor, filp);
return retval;
}
@@ -1453,6 +1501,9 @@ static int tty_select(struct inode * inode, struct file * filp, int sel_type, se
*/
void do_SAK( struct tty_struct *tty)
{
+#ifdef TTY_SOFT_SAK
+ tty_hangup(tty);
+#else
struct task_struct **p;
int line = tty->line;
int session = tty->session;
@@ -1478,6 +1529,7 @@ void do_SAK( struct tty_struct *tty)
}
}
}
+#endif
}
/*
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index e231354..2e86bac 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -19,6 +19,7 @@
#include <linux/fcntl.h>
#include <asm/io.h>
+#include <asm/bitops.h>
#include <asm/segment.h>
#include <asm/system.h>
@@ -493,19 +494,42 @@ int tty_ioctl(struct inode * inode, struct file * file,
return -EINVAL;
return 0;
case TIOCEXCL:
- return -EINVAL; /* not implemented */
+ set_bit(TTY_EXCLUSIVE, &tty->flags);
+ return 0;
case TIOCNXCL:
- return -EINVAL; /* not implemented */
+ clear_bit(TTY_EXCLUSIVE, &tty->flags);
+ return 0;
case TIOCSCTTY:
- if ((current->leader && current->tty < 0 &&
- tty->session == 0) ||
- (arg == 1 && suser())) {
- current->tty = dev;
- tty->session = current->session;
- tty->pgrp = current->pgrp;
+ if (current->leader &&
+ (current->session == tty->session))
return 0;
+ /*
+ * The process must be a session leader and
+ * not have a controlling tty already.
+ */
+ if (!current->leader || (current->tty >= 0))
+ return -EPERM;
+ if (tty->session > 0) {
+ /*
+ * This tty is already the controlling
+ * tty for another session group!
+ */
+ if ((arg == 1) && suser()) {
+ /*
+ * Steal it away
+ */
+ struct task_struct *p;
+
+ for_each_task(p)
+ if (p->tty == dev)
+ p->tty = -1;
+ } else
+ return -EPERM;
}
- return -EPERM;
+ current->tty = dev;
+ tty->session = current->session;
+ tty->pgrp = current->pgrp;
+ return 0;
case TIOCGPGRP:
retval = verify_area(VERIFY_WRITE, (void *) arg,4);
if (!retval)
@@ -540,7 +564,8 @@ int tty_ioctl(struct inode * inode, struct file * file,
(unsigned long *) arg);
return 0;
case TIOCSTI:
- return -EINVAL; /* not implemented */
+ put_tty_queue(get_fs_byte((char *) arg), &tty->read_q);
+ return 0;
case TIOCGWINSZ:
return get_window_size(tty,(struct winsize *) arg);
case TIOCSWINSZ:
@@ -595,13 +620,9 @@ int tty_ioctl(struct inode * inode, struct file * file,
case TIOCNOTTY:
if (MINOR(file->f_rdev) != current->tty)
return -EINVAL;
+ if (current->leader)
+ disassociate_ctty(0);
current->tty = -1;
- if (current->leader) {
- if (tty->pgrp > 0)
- kill_pg(tty->pgrp, SIGHUP, 0);
- tty->pgrp = -1;
- tty->session = 0;
- }
return 0;
case TIOCGETD:
retval = verify_area(VERIFY_WRITE, (void *) arg,4);
diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c
index 043010c..44c5430 100644
--- a/drivers/net/3c501.c
+++ b/drivers/net/3c501.c
@@ -2,9 +2,6 @@
/*
Copyright (C) 1992,1993 Donald Becker
- This is alpha test code. No redistribution is permitted.
-
- If this code is distributed, it will carry the following:
Copyright 1993 United States Government as represented by the
Director, National Security Agency. This software may be used and
distributed according to the terms of the GNU Public License,
diff --git a/drivers/net/ne.c b/drivers/net/ne.c
index 95d4231..0542a82 100644
--- a/drivers/net/ne.c
+++ b/drivers/net/ne.c
@@ -17,7 +17,7 @@
/* Routines for the NatSemi-based designs (NE[12]000). */
static char *version =
- "ne.c:v0.99-13s 11/17/93 Donald Becker (becker@super.org)\n";
+ "ne.c:v0.99-14g 12/21/93 Donald Becker (becker@super.org)\n";
#include <linux/config.h>
#include <linux/kernel.h>
@@ -104,7 +104,7 @@ static int neprobe1(int ioaddr, struct device *dev, int verbose)
int wordlength = 2;
char *name;
int start_page, stop_page;
- int neX000, ctron, dlink;
+ int neX000, ctron, dlink, dfi;
int reg0 = inb(ioaddr);
if ( reg0 == 0xFF)
@@ -151,8 +151,8 @@ static int neprobe1(int ioaddr, struct device *dev, int verbose)
outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
}
for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
- SA_prom[i] = inb_p(ioaddr + NE_DATAPORT);
- SA_prom[i+1] = inb_p(ioaddr + NE_DATAPORT);
+ SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+ SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
if (SA_prom[i] != SA_prom[i+1])
wordlength = 1;
}
@@ -179,12 +179,13 @@ static int neprobe1(int ioaddr, struct device *dev, int verbose)
}
#endif
- neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
+ neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
dlink = (SA_prom[0] == 0x00 && SA_prom[1] == 0xDE && SA_prom[2] == 0x01);
+ dfi = (SA_prom[0] == 'D' && SA_prom[1] == 'F' && SA_prom[2] == 'I');
/* Set up the rest of the parameters. */
- if (neX000 || dlink) {
+ if (neX000 || dlink || dfi) {
if (wordlength == 2) {
name = dlink ? "DE200" : "NE2000";
start_page = NESM_START_PG;
diff --git a/drivers/net/smc-ultra.c b/drivers/net/smc-ultra.c
index 99ed464..5816b83 100644
--- a/drivers/net/smc-ultra.c
+++ b/drivers/net/smc-ultra.c
@@ -14,7 +14,7 @@
*/
static char *version =
- "smc-ultra.c:v0.04 11/30/93 Donald Becker (becker@super.org)\n";
+ "smc-ultra.c:v0.05 12/21/93 Donald Becker (becker@super.org)\n";
#include <linux/config.h>
#include <linux/kernel.h>
@@ -59,7 +59,7 @@ static int ultra_close_card(struct device *dev);
int ultra_probe(struct device *dev)
{
- int *port, ports[] = {0x200, 0x220, 0x240, 0x280, 0x300, 0x380, 0};
+ int *port, ports[] = {0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0};
unsigned short ioaddr = dev->base_addr;
if (ioaddr > 0x1ff)
@@ -70,7 +70,7 @@ int ultra_probe(struct device *dev)
for (port = &ports[0]; *port; port++) {
if (check_region(*port, 32))
continue;
- if ((inb(*port + 7) & 0xF0) == 0x20 /* Check chip ID nibble. */
+ if ((inb(*port + 7) & 0xF0) == 0x20 /* Check chip ID nibble. */
&& ultraprobe1(*port, dev) == 0)
return 0;
}
@@ -84,12 +84,13 @@ int ultraprobe1(int ioaddr, struct device *dev)
unsigned char *station_addr = dev->dev_addr;
int checksum = 0;
char *model_name;
- int num_pages;
unsigned char eeprom_irq = 0;
+ /* Values from various config regs. */
+ unsigned char num_pages, irqreg, addr, reg4 = inb(ioaddr + 4) & 0x7f;
/* Select the station address register set. */
- outb(0x7f & inb(ioaddr + 4), ioaddr + 4);
+ outb(reg4, ioaddr + 4);
for (i = 0; i < 8; i++)
checksum += inb(ioaddr + 8 + i);
@@ -100,14 +101,23 @@ int ultraprobe1(int ioaddr, struct device *dev)
for (i = 0; i < 6; i++)
printk(" %2.2X", station_addr[i] = inb(ioaddr + 8 + i));
- /* Switch from the station address to the alternate register set. */
- outb(0x80 | inb(ioaddr + 4), ioaddr + 4);
+ /* Switch from the station address to the alternate register set and
+ read the useful registers there. */
+ outb(0x80 | reg4, ioaddr + 4);
+
+ /* Enabled FINE16 mode to avoid BIOS ROM width mismatches during reboot. */
+ outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+ irqreg = inb(ioaddr + 0xd);
+ addr = inb(ioaddr + 0xb);
+
+ /* Switch back to the station address register set so that the MS-DOS driver
+ can find the card after a warm boot. */
+ outb(reg4, ioaddr + 4);
model_name = "SMC Ultra";
if (dev->irq < 2) {
unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
- int irqreg = inb(ioaddr + 0xd);
int irq;
/* The IRQ bits are split. */
@@ -129,7 +139,6 @@ int ultraprobe1(int ioaddr, struct device *dev)
dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
{
- int addr = inb(ioaddr + 0xb);
int addr_tbl[4] = {0x0C0000, 0x0D0000, 0xFC0000, 0xFD0000};
short num_pages_tbl[4] = {0x20, 0x40, 0x80, 0xff};
@@ -172,8 +181,6 @@ ultra_open(struct device *dev)
if (irqaction(dev->irq, &ei_sigaction))
return -EAGAIN;
- /* Enabled FINE16 mode. */
- outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
outb(ULTRA_MEMENB, ioaddr); /* Enable memory, 16 bit mode. */
outb(0x80, ioaddr + 5);
outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */
diff --git a/drivers/net/wd.c b/drivers/net/wd.c
index f88ec98..c94068f 100644
--- a/drivers/net/wd.c
+++ b/drivers/net/wd.c
@@ -30,8 +30,8 @@ static char *version =
/* Compatibility definitions for earlier kernel versions. */
#ifndef HAVE_PORTRESERVE
-#define check_region(ioaddr, size) 0
-#define snarf_region(ioaddr, size); do ; while (0)
+#define check_region(ioaddr, size) 0
+#define snarf_region(ioaddr, size) do ; while (0)
#endif
int wd_probe(struct device *dev);
diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c
index f31d73d..d57b352 100644
--- a/drivers/scsi/aha1542.c
+++ b/drivers/scsi/aha1542.c
@@ -16,6 +16,7 @@
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ioport.h>
+#include <linux/config.h>
#include <linux/sched.h>
#include <asm/dma.h>
@@ -50,8 +51,13 @@ static unsigned int bases[]={0x330, 0x334};
#define DMA_MASK_REG 0xd4
#define CASCADE 0xc0
+#define BIOS_TRANSLATION_1632 0 /* Used by some old 1542A boards */
+#define BIOS_TRANSLATION_6432 1 /* Default case these days */
+#define BIOS_TRANSLATION_25563 2 /* Big disk case */
+
struct aha1542_hostdata{
/* This will effectively start both of them at the first mailbox */
+ int bios_translation; /* Mapping bios uses - for compatibility */
int aha1542_last_mbi_used;
int aha1542_last_mbo_used;
Scsi_Cmnd * SCint[AHA1542_MAILBOXES];
@@ -174,6 +180,10 @@ static int makecode(unsigned hosterr, unsigned scsierr)
static int aha1542_test_port(int bse, struct Scsi_Host * shpnt)
{
int i;
+ unchar inquiry_cmd[] = {CMD_INQUIRY };
+ unchar inquiry_result[4];
+ unchar *cmdp;
+ int len;
volatile int debug = 0;
/* Quick and dirty test for presence of the card. */
@@ -195,27 +205,22 @@ static int aha1542_test_port(int bse, struct Scsi_Host * shpnt)
debug = 2;
/* Shouldn't have generated any interrupts during reset */
if (inb(INTRFLAGS(bse))&INTRMASK) goto fail;
- setup_mailboxes(bse, shpnt);
-
+
+
+ /* Perform a host adapter inquiry instead so we do not need to set
+ up the mailboxes ahead of time */
+
+ aha1542_out(bse, inquiry_cmd, 1);
+
debug = 3;
- /* Test the basic ECHO command */
- outb(CMD_ECHO, DATA(bse));
-
- debug = 4;
- /* Wait for CDF=0. If any of the others are set, it's bad */
- WAIT(STATUS(bse), STATMASK, 0, STST|DIAGF|INVDCMD|DF|CDF);
-
- debug = 5;
- /* The meaning of life */
- outb(42, DATA(bse));
-
- debug = 6;
- /* Expect only DF, that is, data ready */
- WAIT(STATUS(bse), STATMASK, DF, STST|DIAGF|CDF|INVDCMD);
-
- debug = 7;
- /* Is the answer correct? */
- if (inb(DATA(bse)) != 42) goto fail;
+ len = 4;
+ cmdp = &inquiry_result[0];
+
+ while (len--)
+ {
+ WAIT(STATUS(bse), DF, DF, 0);
+ *cmdp++ = inb(DATA(bse));
+ }
debug = 8;
/* Reading port should reset DF */
@@ -298,10 +303,7 @@ static void aha1542_intr_handle(int foo)
sti();
/* Hmm, no mail. Must have read it the last time around */
if (number_serviced) return;
- /* Virtually all of the time, this turns out to be the problem */
- printk("aha1542.c: Unsupported BIOS options enabled."
- " Please turn off.\n");
-/* printk("aha1542.c: interrupt received, but no mail.\n"); */
+ printk("aha1542.c: interrupt received, but no mail.\n");
return;
};
@@ -656,8 +658,41 @@ static int aha1542_getconfig(int base_io, unsigned char * irq_level, unsigned ch
return 0;
}
+/* This function should only be called for 1542C boards - we can detect
+ the special firmware settings and unlock the board */
+
+static int aha1542_mbenable(int base)
+{
+ static unchar mbenable_cmd[3];
+ static unchar mbenable_result[2];
+ int retval;
+
+ retval = BIOS_TRANSLATION_6432;
+
+ mbenable_cmd[0]=CMD_EXTBIOS;
+ aha1542_out(base,mbenable_cmd,1);
+ aha1542_in(base,mbenable_result,2);
+ WAIT(INTRFLAGS(base),INTRMASK,HACC,0);
+ aha1542_intr_reset(base);
+
+ if (mbenable_result[0] & 0x08) {
+ mbenable_cmd[0]=CMD_MBENABLE;
+ mbenable_cmd[1]=0;
+ mbenable_cmd[2]=mbenable_result[1];
+ if(mbenable_result[1] & 1) retval = BIOS_TRANSLATION_25563;
+ aha1542_out(base,mbenable_cmd,3);
+ WAIT(INTRFLAGS(base),INTRMASK,HACC,0);
+ };
+ while(0) {
+fail:
+ printk("aha1542_mbenable: Mailbox init failed\n");
+ }
+aha1542_intr_reset(base);
+return retval;
+}
+
/* Query the board to find out if it is a 1542 or a 1740, or whatever. */
-static int aha1542_query(int base_io)
+static int aha1542_query(int base_io, int * transl)
{
unchar inquiry_cmd[] = {CMD_INQUIRY };
unchar inquiry_result[4];
@@ -675,6 +710,8 @@ static int aha1542_query(int base_io)
}
aha1542_intr_reset(base_io);
+ *transl = BIOS_TRANSLATION_6432; /* Default case */
+
/* For an AHA1740 series board, we ignore the board since there is a
hardware bug which can lead to wrong blocks being returned if the board
is operating in the 1542 emulation mode. Since there is an extended mode
@@ -685,15 +722,20 @@ static int aha1542_query(int base_io)
printk("aha1542.c: Emulation mode not supported for AHA 174N hardware.\n");
return 1;
};
+ if (inquiry_result[0] == 0x44) { /* Detect 1542C */
+ *transl = aha1542_mbenable(base_io);
+ };
return 0;
}
+
/* return non-zero on detection */
int aha1542_detect(int hostnum)
{
unsigned char dma_chan;
unsigned char irq_level;
unsigned int base_io;
+ int trans;
struct Scsi_Host * shpnt = NULL;
int count = 0;
int indx;
@@ -727,7 +769,7 @@ int aha1542_detect(int hostnum)
}
aha1542_intr_reset(base_io);
}
- if(aha1542_query(base_io)) goto unregister;
+ if(aha1542_query(base_io, &trans)) goto unregister;
if (aha1542_getconfig(base_io, &irq_level, &dma_chan) == -1) goto unregister;
@@ -764,6 +806,9 @@ int aha1542_detect(int hostnum)
shpnt->io_port = base_io;
shpnt->dma_channel = dma_chan;
shpnt->irq = irq_level;
+ HOSTDATA(shpnt)->bios_translation = trans;
+ if(trans == 2)
+ printk("aha1542.c: Using extended bios translation\n");
HOSTDATA(shpnt)->aha1542_last_mbi_used = (2*AHA1542_MAILBOXES - 1);
HOSTDATA(shpnt)->aha1542_last_mbo_used = (AHA1542_MAILBOXES - 1);
memset(HOSTDATA(shpnt)->SCint, 0, sizeof(HOSTDATA(shpnt)->SCint));
@@ -847,11 +892,30 @@ int aha1542_reset(Scsi_Cmnd * SCpnt)
return 0;
}
+#ifdef CONFIG_BLK_DEV_SD
+#include "sd.h"
+#endif
+
int aha1542_biosparam(int size, int dev, int * ip)
{
- ip[0] = 64;
- ip[1] = 32;
- ip[2] = size >> 11;
+ int translation_algorithm;
+#ifdef CONFIG_BLK_DEV_SD
+ Scsi_Device *disk;
+
+ disk = rscsi_disks[MINOR(dev) >> 4].device;
+ translation_algorithm = HOSTDATA(disk->host)->bios_translation;
+ /* Should this be > 1024, or >= 1024? Enquiring minds want to know. */
+ if((size>>11) > 1024 && translation_algorithm == 2) {
+ /* Please verify that this is the same as what DOS returns */
+ ip[0] = 255;
+ ip[1] = 63;
+ ip[2] = size /255/63;
+ } else {
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ };
/* if (ip[2] >= 1024) ip[2] = 1024; */
+#endif
return 0;
}
diff --git a/drivers/scsi/aha1542.h b/drivers/scsi/aha1542.h
index f906107..e6c547e 100644
--- a/drivers/scsi/aha1542.h
+++ b/drivers/scsi/aha1542.h
@@ -74,6 +74,9 @@
#define CMD_RETSETUP 0x0d /* Return Setup Data */
#define CMD_ECHO 0x1f /* ECHO Command Data */
+#define CMD_EXTBIOS 0x28 /* Return extend bios information only 1542C */
+#define CMD_MBENABLE 0x29 /* Set Mailbox Interface enable only 1542C */
+
/* Mailbox Definition 5.2.1 and 5.2.2 */
struct mailbox {
unchar status; /* Command/Status */
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 9bf092a..630c01e 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -362,7 +362,7 @@ static void scan_scsis (void)
break;
case TYPE_DISK:
case TYPE_MOD:
- printk("Detected scsi disk sd%d at scsi%d, id %d, lun %d\n", MAX_SD,
+ printk("Detected scsi disk sd%c at scsi%d, id %d, lun %d\n", 'a'+MAX_SD,
shpnt->host_no , dev, lun);
if(NR_SD != -1) ++MAX_SD;
break;
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 59a0cd2..380cede 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -101,7 +101,12 @@ int check_cdrom_media_change(int full_dev, int flag){
};
retval = scsi_CDs[target].device->changed;
- if(!flag) scsi_CDs[target].device->changed = 0;
+ if(!flag) {
+ scsi_CDs[target].device->changed = 0;
+ /* If the disk changed, the capacity will now be different,
+ so we force a re-read of this information */
+ if (retval) scsi_CDs[target].needs_sector_size = 1;
+ };
return retval;
}
@@ -654,6 +659,8 @@ static void get_sectorsize(int i){
memset ((void *) &cmd[2], 0, 8);
SCpnt->request.dev = 0xffff; /* Mark as really busy */
+ memset(buffer, 0, 8);
+
scsi_do_cmd (SCpnt,
(void *) cmd, (void *) buffer,
512, sr_init_done, SR_TIMEOUT,
@@ -692,6 +699,7 @@ static void get_sectorsize(int i){
printk ("scd%d : unsupported sector size %d.\n",
i, scsi_CDs[i].sector_size);
scsi_CDs[i].capacity = 0;
+ scsi_CDs[i].needs_sector_size = 1;
};
if(scsi_CDs[i].sector_size == 2048)
scsi_CDs[i].capacity *= 4;
diff --git a/drivers/sound/.blurb b/drivers/sound/.blurb
index 1b2fa63..92aebcc 100644
--- a/drivers/sound/.blurb
+++ b/drivers/sound/.blurb
@@ -1,8 +1,12 @@
NOTE! This driver version is not compatible with the version 1.0c.
This means you have to use the latest version of the snd-util
- package. The earlier ones (from 1.0) will not work. If you have
+ package (2.0). The earlier ones (from 1.0) will not work. If you have
other programs using ioctl calls of the driver, they must be
recompiled. Most of them will not work without some source
modifications.
+ ******** LINUX VERSION 0.99.14 OR LATER IS REQUIRED *******
+
+ See sound/Readme for more details
+
Hannu
diff --git a/drivers/sound/CHANGELOG b/drivers/sound/CHANGELOG
index 99c5dc6..6839b1e 100644
--- a/drivers/sound/CHANGELOG
+++ b/drivers/sound/CHANGELOG
@@ -1,9 +1,28 @@
-Changelog for version 2.2b
+Changelog for version 2.3
-------------------------
+Since 2.2b
+- Full SB16 DSP support. 8/16 bit, mono/stereo
+- The SCO and FreeBSD versions should be in sync now. There are some
+ problems with SB16 and GUS in the freebsd versions.
+ The DMA buffer allocation of the SCO version has been polished but
+ there could still be some problems. At least it hogs memory.
+ The DMA channel
+ configuration method used in the sco/System is a hack.
+- Support for the MPU emulation of the SB16.
+- Some big arrays are now allocated boot time. This makes the bss segment
+ smaller which makes it possible to use the full driver with
+ NetBSD. These arrays are not allocated if no suitable soundcard is available.
+- Fixed a bug in the compute_and_set_volume in gus_wave.c
+- Fixed the too fast mono playback problem of SB Pro and PAS16.
+
Since 2.2
+- Stereo recording for SB Pro. Somehow it was missing and nobody
+ had noticed it earlier.
- Minor polishing.
- Interpreting of boot time arguments (sound=) for Linux.
+- Breakup of sb_dsp.c. Parts of the code has been moved to
+ sb_mixer.c and sb_midi.c
Since 2.1
- Preliminary support for SB16.
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index a6dcbf4..422ab50 100644
--- a/drivers/sound/Makefile
+++ b/drivers/sound/Makefile
@@ -5,9 +5,8 @@
#
#
-VERSION = 2.2b
+VERSION = 2.3
TARGET_OS = linux
-USRINCDIR = /usr/include
.c.s:
$(CC) $(CFLAGS) -S $<
@@ -19,10 +18,21 @@ USRINCDIR = /usr/include
OBJS = soundcard.o audio.o dmabuf.o sb_dsp.o dev_table.o \
opl3.o sequencer.o midibuf.o sb_card.o pas2_card.o adlib_card.o \
pas2_pcm.o pas2_mixer.o pas2_midi.o gus_card.o gus_wave.o mpu401.o \
- gus_midi.o gus_vol.o patmgr.o sb_mixer.o sb16_dsp.o sb_midi.o
+ gus_midi.o gus_vol.o patmgr.o sb_mixer.o sb16_dsp.o sb_midi.o \
+ sb16_midi.o sound_switch.o
all: local.h sound.a
+/usr/include/sys/soundcard.h:
+ @echo "WARNING! Your /usr/include/sys/soundcard.h not found."
+ @echo "Please make a new /usr/include/sys/soundcard.h containing
+ @echo "just a line #include <linux/soundcard.h>"
+
+os.h:
+ @$(MAKE) setup-$(TARGET_OS)
+ @$(MAKE) config
+ @$(MAKE) dep
+
sound.a: $(OBJS)
-rm -f sound.a
$(AR) rcs sound.a $(OBJS)
@@ -38,10 +48,16 @@ indent:
local.h:
$(MAKE) clean
+ $(MAKE) setup-$(TARGET_OS)
$(MAKE) config
$(MAKE) dep
-config: configure
+soundcard.c: $(TARGET_OS)/soundcard.c
+ ln -sf $(TARGET_OS)/soundcard.c soundcard.c
+
+config: configure /usr/include/sys/soundcard.h
+ @$(MAKE) setup-$(TARGET_OS)
+ @echo Compiling Sound Driver v $(VERSION) for Linux
@./configure > local.h
@echo \#define SOUND_VERSION_STRING \"$(VERSION)\" >> local.h
@echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h
@@ -49,11 +65,15 @@ config: configure
@echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h
@echo \#define SOUND_CONFIG_DOMAIN \"`domainname`\" >> local.h
-configure: configure.c
+clrconf:
+ rm -f local.h .depend os.h soundcard.c
+
+configure: configure.c /usr/include/sys/soundcard.h
$(HOSTCC) -o configure configure.c
@cat .blurb
dep:
+ $(MAKE) /usr/include/sys/soundcard.h
$(CPP) -M *.c > .depend
#
diff --git a/drivers/sound/Readme b/drivers/sound/Readme
index b03baad..452733f 100644
--- a/drivers/sound/Readme
+++ b/drivers/sound/Readme
@@ -1,34 +1,43 @@
-Release notes for the Linux Sound Driver 2.2b
+Release notes for the Linux Sound Driver 2.3
-----------------------------------------------
-This version is just version 2.0 with some kind of SB16
-support. The SB16 works now in 8 bit mono mode up to speed
-44100 Hz. Stereo recording and playback is not supported with
-the SB16 yet. Also the 16 bit mode is missing.
-In addition this has capability to read the configuration
-parameters at boot time (only in the Linux version - See linux/Readme).
+This version has finally the SB16 support. It seems to work
+well but there is some problems with recording. If the recording
+process cannot read the data from the device fast enough the
+driver will return EIO. Usually this happens when there is no more
+memory for the disk buffers.
+The SB16 DSP support is by Joerg Schubert (jsb@sth.ruhr-uni-bochum.de).
+Currently the SB16 support is an ALPHA test version since it has been
+tested just by me.
+
+The SB16 driver requires separate DMA channels for the 8 and 16 bit
+modes. There should be a way to share the 8 bit DMA channels between
+these modes but this feature is not supported yet.
+
+The SB16 driver has also the Midi input capability even at the same
+time with the /dev/dsp. Also the WaveBlaster daughter board is supported.
+No support for the ASP chip yet (the ASP chip can be installed but it's
+not used by the driver).
You will need the snd-util-2.0.tar.gz and snd-data-0.1.tar.Z
packages to use this driver. They should be in the same
ftp site or BBS from where you got this driver. For
example at nic.funet.fi:pub/OS/Linux/*.
+There is a new version of the tracker program available (tracker-3.19) but
+I don't know how to find it. The tracker 3.10 has bugs and it don't work
+without some fixes. Look at the README of the snd-util-2.0.
+
If you are looking for the installation instructions, please
look at linux/Readme.
-Welcome to use the Gravis UltraSound driver for Linux. This
-driver still supports the same cards than version 1.0c
-(SoundBlaster, SB Pro, Pro Audio Spectrum 16 and AdLib).
-In addition there is rather limited support for MPU-401
+This version supports the following soundcards:
+GUS, SoundBlaster, SB Pro, SB16, Pro Audio Spectrum 16 and AdLib.
+In addition there is rather limited support for MPU-401.
(and compatible) midi cards. Also the OPL-3 synthesizer
-of the SB Pro and PAS16 cards is now supported in the 4 OP
-modes.
Most of the features of the /dev/sequencer device file are
available just for GUS owners.
-The SoundBlaster 16 and SB 16 ASP cards are now supported
-but just in 8 bit mono mode (up to 44.1 kHz).
-
NOTE! There are separate driver for CD-ROMS supported by
some soundcards. The driver for CDU31A (Fusion 16) is
called cdu31a-0.6.diff.z. It will be contained in the
@@ -45,6 +54,10 @@ NOTE! There are separate driver for CD-ROMS supported by
Compatibility with the earlier versions
---------------------------------------
+In this version the ultrasound.h no longer includes the sys/soundcard.h
+You have to change the gmod.c of the snd-util-2.0 package and to add an
+include for it.
+
IMPORTANT!!!!!!!!!!!!!!!!!!!!!!
This version is not binary or source compatible with the version 1.0c.
@@ -96,28 +109,18 @@ the snd-util-2.0 package for further info.
This version is almost 100% compatible with the alpha test version (1.99.9). The
difference is in the installation procedure.
-Using the driver under other environments than Linux
-----------------------------------------------------
-
-**** There is some ISC and SCO stuff in this driver. Please stay away ****
-This stuff is here just because I want to be in sync with the porters. This
-ports don't work yet.
-The ISC port is by Andy Warner (andy@harris.nl).
+Using this driver with other operating systems than Linux
+---------------------------------------------------------
-There FreeBSD port is by
-There is a FreeBSD port by Jim Lowe (james@blatz.cs.uwm.edu) in the directory
-freebsd. (Based on my 386bsd port which was in the previous release (1.99.9).
-It's should work but it requires still some testing. It should work
-with the NetBSD 0.9 also but propably not with the 386bsd 0.1.
-There has been some problems with GUS in SCO and NetBSD.
-In addition I'm preparing a SCO port myself. Yet again, these ports
-are little incomplete and untested. It could be possible to get them to work
-with finite amount of hacking but be careful.
+This package contains just the Linux version. The version 2.3
+for SCO is available at nic.funet.fi:pub/OS/Linux/ALPHA/sound.
+The version 2.3 doesn't work well with xxxxxBSD. Use the version
+2.3 for them.
/dev/sndstat
------------
-NOTE! This device file is available only for Linux at this time.
+The /dev/sndstat is now available in the SCO and BSD versions also.
This is a new devicefile for debugging purposes. A better place for
it is in the /proc -directory but I was just too lazy to implement it
@@ -133,7 +136,6 @@ Note! This device file is currently present only in the Linux version
Sound Driver:1.99.7 (Fri Jul 9 17:01:47 GMT 1993 root@lucifer.savolai.fi)
Config options: 0x00000d4b
-Major number: 14
HW config:
Type 4: Gravis Ultrasound at 0x210 irq 15 drq 6
Type 3: ProAudioSpectrum at 0x388 irq 10 drq 3
@@ -159,6 +161,7 @@ Mixer(s) installed
Known bugs/limitations
----------------------
+- Midi input doesn't work with SB and SB Pro (SB16 works somehow).
- It's not possible to open /dev/dsp (or /dev/audio) while the
/dev/sequencer is open for output and GUS is the only soundcard
installed. It's possible if /dev/dsp is opened before /dev/sequencer
@@ -167,10 +170,17 @@ Known bugs/limitations
- MPU-401 driver hangs the computer on boot if there is no MPU-401 installed.
It uses by default the I/O port 0x330 whic is used by Adaptec 1542 SCSI
adapter.
+- There are some problems in midi input with MPU-401 and the SB16 midi
+ (MPU-401 emulation). This makes it impossible to read long sysex dumps
+ using these devices.
- The /dev/sequencer playback to GUS sounds sometimes rather weird. Hitting
^C and playing again should solve this problem. This is propably caused by
- incompatibilities between GUS and certain VLB motherboards. Try to avoid
+ incompatibilities between GUS and certain VLB motherboards (like mine).
+ Try to avoid
switching between VTs while patches are being loaded to the GUS.
+ This problem disappears completely if you define GUS_PATCH_NO_DMA in the
+ local.h (after make config in linux). The drawback is that patch loading
+ without DMA takes several times longer than with DMA.
- There is a skeleton of the patch manager support. It don't work in
this version.
@@ -178,8 +188,6 @@ Known bugs/limitations
Future development
------------------
-- The SB16 card should be fully supported some day. I have the SDK for it
- now so it should not take infinite time to implement it.
- Since this driver is no longer just the Linux Sound Driver, it's time
to give it a new name. I have planned to use name VoxWare.
- I'm writing a Hacker's guide to the VoxWare sound driver. Should
@@ -216,6 +224,8 @@ contributors. (I could have forgotten some names.)
lot's of valuable suggestions.
Andy Warner ISC port
Jim Lowe FreeBSD port
+ Anders Baekgaard Bughunting and valuable suggestions.
+ Joerg Schubert SB16 DSP support.
Regards,
@@ -226,242 +236,3 @@ Snail mail: Hannu Savolainen
Pallaksentie 4 A 2
00970 Helsinki
Finland
-
----------- linux/Readme:
-
-Sound Card Driver version 2.2
-
-This directory contains the driver for various PC soundcards.
-The following cards are supported:
-
-AdLib
-SoundBlaster (1.0-2.0) and compatibles, including
- ThunderBoard and Ati Stereo F/X.
-SoundBlaster Pro and SB Pro 2
-SoundBlaster 16 (8 bit mono only)
-ProAudioSpectrum 16
- (The original ProAudioSpectrum and the PAS+ are not supported
- (and propably will remain unsupported)).
-
-If you have any problems, please contact me.
-
-Installation
-------------
-
-- Since this driver is a part of the Linux kernel distribution, no
- special steps are required to build the driver itself.
-
-- To build the device files you need to run the enclosed shell scrip
- (see below).
-
-- Copy the sound/ultrasound.h and sound/soundcard.h to /usr/include/sys.
- (Remove the old ones from /usr/include/sys /usr/include/linux first).
-
-Boot time configuration (using lilo)
-------------------------------------
-
-This version of the sound driver has capability to accept the configuration
-parameters from the boot loader (for example lilo). By default the
-driver is booted using the parameters given before compiling the driver
-('make config' or 'make soundconf'). If the kernel is booted using lilo and
-the boot command is given manually, it's possible to give the configuration
-parameters on the command line. Just hold down the <Alt> key when lilo
-starts. Then give the boot command manually and append a sound= argument
-to the boot command line. For example:
-
-lilo boot: linux sound=0x222071,0x138800
-
-The sound= argument could contain several configuration entries separated by a
-comma. Each option gives the configuration for one sound device.
-Give the options in the order given below. Other order of use is undefined.
-Each option is encoded as the following:
-
- 0xTaaaId, where
- || ||
- || |+---- d = DMA channel (0, 1, 3, 5, 6 or 7)
- || +----- I = IRQ (HEXADECIMAL!!! 1=1, ..., 9=9, 10=a, ..., 15=f)
- |+-------- aaa = I/O address (hexadecimal)
- +---------- T = device type 1=FM Synth (YM3812 or OPL3)
- 2=SoundBlaster (1.0 to 2.0, Pro, 16)
- 3=ProAudioSpectrum16
- 4=Gravis UltraSound
- 5=MPU-401 UART midi
-
-These are the configuration templates for various soundcards:
-
- 0) Disable the sound driver
-
- sound=0
-
- 1) AdLib
-
- sound=0x138800
-
- 2) SoundBlaster family and compatibles
-
- sound=0x2220Id,0x138800 (remember to set the IRQ and DMA)
-
- 3) ProAudioSpectrum16, ProAudioStudio16, Logitech Soundman16 etc.
-
- sound=0x3388Id,0x2220Id,0x138800 (set the DMAs and IRQs)
-
- 4) Gravis UltraSound
-
- sound=0x42X0Id (X is 1, 2, 3 or 4. Set the DMA and IRQ)
-
- 5) MPU-401
-
- sound=0x5aaaI0
-
-If you have more than one soundcards, you have to concatenate the options
-for each of the cards. There cannot be more than one sound= argument in the
-command line. For example use "sound=0x5aaaI0,0x138800" if you have AdLib
-and MPU-401 on your system.
-If there are two or more sound= arguments
-in the boot command line, just the last one takes effect. The earlier ones
-will be ignored silently.
-
-The boot time configuration feature is intended mainly for distributors of
-precompiled kernels. When this feature is used, drivers for all of the
-cards have to be enabled before compiling the driver. The configurator program
-doesn't enable MPU-401 when the full driver option is selected. It must be
-enabled by uncommenting "#define EXCLUDE_MPU401" in the sound/local.h.
-
-Important note!
-
-The sound driver is enabled by default. If the kernel is booted without
-using the sound=0 option, the sound driver is initialized using the compile
-time parameters. This could be dangerous (specially if the MPU-401 driver
-is enabled with I/O address 0x330 (used by AHA-1542 also)). If you want to
-compile the driver to be inactive by default, you have to append a
-#define SND_DEFAULT_ENABLE 0
-to the sound/local.h before compiling the driver.
-
-Remember to check that the sound setup routine is included in the
-bootparams structure in linux/init/main.c. It should contain the following
-lines:
-
-#ifdef CONFIG_SOUND
- { "sound=", sound_setup },
-#endif
-
-In case these lines were not there, you have to insert them (the driver works
-without them but it's not possible to give the boot time parameters for the
-sound driver). Add also the following line somewhere near the beginning of
-linux/init/main.c:
-
-extern void sound_setup(char *str, int *ints);
-
-Problems
---------
-
-If you have any kind of problems, there is a debugging feature which
-could help you to solve the problem. To use it, just execute the
-command:
-
- cat /dev/sndstat
-
-and look at the output. It should display some usefull info about the
-driver configuration. If there is no /dev/sndstat
-(/dev/sndstat: No such file or directory), ensure that you have executed the
-soundinstall script (at the end of this file). The message:
-/dev/dsp: No such device means that you don't have the sound driver installed
-on your kernel or the driver version is earlier than 1.99.6.
-
-
-- /dev/???????: No such file or directory.
-Run the script at the end of this file.
-
-- /dev/???????: No such device.
-You have not booted with a kernel containing the driver or the I/O address
-configuration doesn't match your hardaware.
-
-- The module player (str) plays just a second and then stops completely.
-You have incorrect IRQ settings (usual with SB cards).
-
-- There is pauses in the playback of the module player (str).
-The str program requires more than 40% of the speed of a 486/50 to play
-without pauses at 44 kHz speed. A 386/25 can hardly play faster than 22 kHz.
-You should use lower speed (-s speed), buy a faster computer or a Gravis
-UltraSound card. (If you already have GUS, you should use gmod and not the
-str). If the DSP_BUFFSIZE in the sound/local.h is less than (nr_channels*
-speed_in_Hz * (bits/8))/2, it could explain the pausing problem. Also check
-that the turbo swich is on and don't run applications like weather forecasting
-on background. Sometimes (very rarely) an IRQ conflict can cause similar
-problems with SB cards.
-If you want to play modules on a 386sx while recompiling the world, buy a GUS.
-It runs without burning your CPU.
-
-Hannu Savolainen
-hsavolai@cs.helsinki.fi
-
------------------ cut here ------------------------------
-#!/bin/sh
-#
-# soudinstall
-#
-# by Craig Metz - cmetz@thor.tjhsst.edu
-#
-# Create the devices
-#
-# Mixer (14, 0)
-#
-if [ -e /dev/mixer ]; then
- rm -f /dev/mixer
-fi
-mknod -m 666 /dev/mixer c 14 0
-#
-# Sequencer (14, 1)
-#
-if [ -e /dev/sequencer ]; then
- rm -f /dev/sequencer
-fi
-mknod -m 666 /dev/sequencer c 14 1
-#
-# MIDI (14, 2) [ Not implemented ]
-#
-if [ -e /dev/midi ]; then
- rm -f /dev/midi
-fi
-mknod -m 666 /dev/midi c 14 2
-#
-# DSP (14, 3)
-#
-if [ -e /dev/dsp ]; then
- rm -f /dev/dsp
-fi
-mknod -m 666 /dev/dsp c 14 3
-#
-# SPARC audio (14, 4) [ Not fully implemented ]
-#
-if [ -e /dev/audio ]; then
- rm -f /dev/audio
-fi
-mknod -m 666 /dev/audio c 14 4
-#
-# DSP2 (14, 19) /dev/dsp for the second soundcard.
-# Also the SB emulation part of the
-# PAS16 card.
-#
-if [ -e /dev/dsp1 ]; then
- rm -f /dev/dsp1
-fi
-mknod -m 666 /dev/dsp1 c 14 19
-#
-# SPARC audio1 (14, 20) [ Not fully implemented ]
-# /dev/audio for the second soundcard.
-# Also the SB emulation part of the
-# PAS16 card.
-#
-if [ -e /dev/audio1 ]; then
- rm -f /dev/audio1
-fi
-mknod -m 666 /dev/audio1 c 14 20
-#
-# /dev/sndstat (14,6) For debugging purposes
-#
-if [ -e /dev/sndstat ]; then
- rm -f /dev/sndstat
-fi
-mknod -m 666 /dev/sndstat c 14 6
-exit 0
diff --git a/drivers/sound/Readme.linux b/drivers/sound/Readme.linux
new file mode 100644
index 0000000..0bf8d92
--- /dev/null
+++ b/drivers/sound/Readme.linux
@@ -0,0 +1,247 @@
+Sound Card Driver version 2.2
+
+This directory contains the driver for various PC soundcards.
+The following cards are supported:
+
+AdLib
+SoundBlaster (1.0-2.0) and compatibles, including
+ ThunderBoard and Ati Stereo F/X.
+SoundBlaster Pro and SB Pro 2
+SoundBlaster 16
+ProAudioSpectrum 16
+ (The original ProAudioSpectrum and the PAS+ are not supported
+ (and propably will remain unsupported)).
+
+If you have any problems, please contact me.
+
+Installation
+------------
+
+- Since this driver is a part of the Linux kernel distribution, no
+ special steps are required to build the driver itself.
+
+- To build the device files you need to run the enclosed shell scrip
+ (see below).
+
+- Copy the sound/ultrasound.h to /usr/include/sys
+ (Remove the old one from /usr/include/sys /usr/include/linux first).
+
+- Ensure you have the following symlink:
+ ln -s /usr/include/linux/soundcard.h /usr/include/sys/soundcard.h
+
+Boot time configuration (using lilo)
+------------------------------------
+
+This version of the sound driver has capability to accept the configuration
+parameters from the boot loader (for example lilo). By default the
+driver is booted using the parameters given before compiling the driver
+('make config' or 'make soundconf'). If the kernel is booted using lilo and
+the boot command is given manually, it's possible to give the configuration
+parameters on the command line. Just hold down the <Alt> key when lilo
+starts. Then give the boot command manually and append a sound= argument
+to the boot command line. For example:
+
+lilo boot: linux sound=0x222071,0x138800
+
+The sound= argument could contain several configuration entries separated by a
+comma. Each option gives the configuration for one sound device.
+Give the options in the order given below. Other order of use is undefined.
+Each option is encoded as the following:
+
+ 0xTaaaId, where
+ || ||
+ || |+---- d = DMA channel (0, 1, 3, 5, 6 or 7)
+ || +----- I = IRQ (HEXADECIMAL!!! 1=1, ..., 9=9, 10=a, ..., 15=f)
+ |+-------- aaa = I/O address (hexadecimal)
+ +---------- T = device type 1=FM Synth (YM3812 or OPL3)
+ 2=SoundBlaster (1.0 to 2.0, Pro, 16)
+ 3=ProAudioSpectrum16
+ 4=Gravis UltraSound
+ 5=MPU-401 UART midi
+ 6=SB16 (16 bit DMA number)
+ 7=SB16 Midi (MPU-401 emulation)
+
+These are the configuration templates for various soundcards:
+
+ 0) Disable the sound driver
+
+ sound=0
+
+ 1) AdLib
+
+ sound=0x138800
+
+ 2) SoundBlaster family and compatibles
+
+ sound=0x2220Id,0x138800 (remember to set the IRQ and DMA)
+ or if you have SB16 or SB16ASP, you have to use the following:
+ (use the same IRQ (the I colums) in all three places. The
+ the D is the 16 bit DMA channel (5 to 7) and the d is
+ the 8 bit one (1 or 3). The X is the 2nd digit of the
+ midi IO address (3 or 0)).
+ sound=0x2220Id,0x6220ID,0x73X0I0,0x138800
+
+ 3) ProAudioSpectrum16, ProAudioStudio16, Logitech Soundman16 etc.
+
+ sound=0x3388Id,0x2220Id,0x138800 (set the DMAs and IRQs)
+
+ 4) Gravis UltraSound
+
+ sound=0x42X0Id (X is 1, 2, 3 or 4. Set the DMA and IRQ)
+
+ 5) MPU-401
+
+ sound=0x5aaaI0
+
+If you have more than one soundcards, you have to concatenate the options
+for each of the cards. There cannot be more than one sound= argument in the
+command line. For example use "sound=0x5aaaI0,0x138800" if you have AdLib
+and MPU-401 on your system.
+If there are two or more sound= arguments
+in the boot command line, just the last one takes effect. The earlier ones
+will be ignored silently.
+
+The boot time configuration feature is intended mainly for distributors of
+precompiled kernels. When this feature is used, drivers for all of the
+cards have to be enabled before compiling the driver. The configurator program
+doesn't enable MPU-401 when the full driver option is selected. It must be
+enabled by uncommenting "#define EXCLUDE_MPU401" in the sound/local.h.
+
+Important note!
+
+The sound driver is enabled by default. If the kernel is booted without
+using the sound=0 option, the sound driver is initialized using the compile
+time parameters. This could be dangerous (specially if the MPU-401 driver
+is enabled with I/O address 0x330 (used by AHA-1542 also)). If you want to
+compile the driver to be inactive by default, you have to append a
+#define SND_DEFAULT_ENABLE 0
+to the sound/local.h before compiling the driver.
+
+Remember to check that the sound setup routine is included in the
+bootparams structure in linux/init/main.c. It should contain the following
+lines:
+
+#ifdef CONFIG_SOUND
+ { "sound=", sound_setup },
+#endif
+
+In case these lines were not there, you have to insert them (the driver works
+without them but it's not possible to give the boot time parameters for the
+sound driver). Add also the following line somewhere near the beginning of
+linux/init/main.c:
+
+extern void sound_setup(char *str, int *ints);
+
+Problems
+--------
+
+If you have any kind of problems, there is a debugging feature which
+could help you to solve the problem. To use it, just execute the
+command:
+
+ cat /dev/sndstat
+
+and look at the output. It should display some usefull info about the
+driver configuration. If there is no /dev/sndstat
+(/dev/sndstat: No such file or directory), ensure that you have executed the
+soundinstall script (at the end of this file). The message:
+/dev/dsp: No such device means that you don't have the sound driver installed
+on your kernel or the driver version is earlier than 1.99.6.
+
+
+- /dev/???????: No such file or directory.
+Run the script at the end of this file.
+
+- /dev/???????: No such device.
+You have not booted with a kernel containing the driver or the I/O address
+configuration doesn't match your hardaware.
+
+- The module player (str) plays just a second and then stops completely.
+You have incorrect IRQ settings (usual with SB cards).
+
+- There is pauses in the playback of the module player (str).
+The str program requires more than 40% of the speed of a 486/50 to play
+without pauses at 44 kHz speed. A 386/25 can hardly play faster than 22 kHz.
+You should use lower speed (-s speed), buy a faster computer or a Gravis
+UltraSound card. (If you already have GUS, you should use gmod and not the
+str). If the DSP_BUFFSIZE in the sound/local.h is less than (nr_channels*
+speed_in_Hz * (bits/8))/2, it could explain the pausing problem. Also check
+that the turbo swich is on and don't run applications like weather forecasting
+on background. Sometimes (very rarely) an IRQ conflict can cause similar
+problems with SB cards.
+If you want to play modules on a 386sx while recompiling the world, buy a GUS.
+It runs without burning your CPU.
+
+Hannu Savolainen
+hsavolai@cs.helsinki.fi
+
+----------------- cut here ------------------------------
+#!/bin/sh
+#
+# soudinstall
+#
+# by Craig Metz - cmetz@thor.tjhsst.edu
+#
+# Create the devices
+#
+# Mixer (14, 0)
+#
+if [ -e /dev/mixer ]; then
+ rm -f /dev/mixer
+fi
+mknod -m 666 /dev/mixer c 14 0
+#
+# Sequencer (14, 1)
+#
+if [ -e /dev/sequencer ]; then
+ rm -f /dev/sequencer
+fi
+mknod -m 666 /dev/sequencer c 14 1
+#
+# MIDI (14, 2) [ Not implemented ]
+#
+if [ -e /dev/midi ]; then
+ rm -f /dev/midi
+fi
+mknod -m 666 /dev/midi c 14 2
+#
+# DSP (14, 3)
+#
+if [ -e /dev/dsp ]; then
+ rm -f /dev/dsp
+fi
+mknod -m 666 /dev/dsp c 14 3
+#
+# SPARC audio (14, 4) [ Not fully implemented ]
+#
+if [ -e /dev/audio ]; then
+ rm -f /dev/audio
+fi
+mknod -m 666 /dev/audio c 14 4
+#
+# DSP2 (14, 19) /dev/dsp for the second soundcard.
+# Also the SB emulation part of the
+# PAS16 card.
+#
+if [ -e /dev/dsp1 ]; then
+ rm -f /dev/dsp1
+fi
+mknod -m 666 /dev/dsp1 c 14 19
+#
+# SPARC audio1 (14, 20) [ Not fully implemented ]
+# /dev/audio for the second soundcard.
+# Also the SB emulation part of the
+# PAS16 card.
+#
+if [ -e /dev/audio1 ]; then
+ rm -f /dev/audio1
+fi
+mknod -m 666 /dev/audio1 c 14 20
+#
+# /dev/sndstat (14,6) For debugging purposes
+#
+if [ -e /dev/sndstat ]; then
+ rm -f /dev/sndstat
+fi
+mknod -m 666 /dev/sndstat c 14 6
+exit 0
diff --git a/drivers/sound/adlib_card.c b/drivers/sound/adlib_card.c
index 29e521e..3b7df5e 100644
--- a/drivers/sound/adlib_card.c
+++ b/drivers/sound/adlib_card.c
@@ -1,6 +1,5 @@
-
/*
- * linux/kernel/chr_drv/sound/adlib_card.c
+ * sound/adlib_card.c
*
* Detection routine for the AdLib card.
*
diff --git a/drivers/sound/audio.c b/drivers/sound/audio.c
index 0e9887b..03d6b7f 100644
--- a/drivers/sound/audio.c
+++ b/drivers/sound/audio.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/chr_drv/sound/audio.c
+ * sound/audio.c
*
* Device file manager for /dev/audio
*
diff --git a/drivers/sound/configure.c b/drivers/sound/configure.c
index c5252a4..f6e4cb6 100644
--- a/drivers/sound/configure.c
+++ b/drivers/sound/configure.c
@@ -65,6 +65,7 @@ typedef struct
char macro[20];
int verify;
int alias;
+ int default_answ;
}
hw_entry;
@@ -87,23 +88,23 @@ hw_entry;
hw_entry hw_table[] =
{
/* 0 */
- {0, 0, "PAS", 1, 0},
- {0, 0, "SB", 1, 0},
- {0, B (OPT_PAS) | B (OPT_SB), "ADLIB", 1, 0},
+ {0, 0, "PAS", 1, 0, 0},
+ {0, 0, "SB", 1, 0, 0},
+ {0, B (OPT_PAS) | B (OPT_SB), "ADLIB", 1, 0, 0},
/* 3 */
- {0, 0, "GUS", 1, 0},
- {0, 0, "MPU401", 1, 0},
- {B (OPT_SB), B (OPT_PAS), "SBPRO", 1, 0},
- {B (OPT_SB) | B (OPT_SBPRO), B (OPT_PAS), "SB16", 1, 0},
- {B (OPT_SB) | B (OPT_PAS) | B (OPT_GUS), 0, "AUDIO", 1, 0},
- {B (OPT_MPU401), 0, "MIDI_AUTO", 0, OPT_MIDI},
- {B (OPT_SB) | B (OPT_PAS) | B (OPT_MPU401) | B (OPT_GUS), 0, "MIDI", 1, 0},
- {B (OPT_ADLIB), 0, "YM3812_AUTO", 0, OPT_YM3812},
- {B (OPT_SB) | B (OPT_PAS) | B (OPT_ADLIB), B (OPT_YM3812_AUTO), "YM3812", 1, 0},
+ {0, 0, "GUS", 1, 0, 0},
+ {0, 0, "MPU401", 1, 0, 0},
+ {B (OPT_SB), B (OPT_PAS), "SBPRO", 1, 0, 1},
+ {B (OPT_SB) | B (OPT_SBPRO), B (OPT_PAS), "SB16", 1, 0, 1},
+ {B (OPT_SB) | B (OPT_PAS) | B (OPT_GUS), 0, "AUDIO", 1, 0, 1},
+ {B (OPT_MPU401), 0, "MIDI_AUTO", 0, OPT_MIDI, 0},
+ {B (OPT_SB) | B (OPT_PAS) | B (OPT_MPU401) | B (OPT_GUS), 0, "MIDI", 1, 0, 1},
+ {B (OPT_ADLIB), 0, "YM3812_AUTO", 0, OPT_YM3812, 0},
+ {B (OPT_SB) | B (OPT_PAS) | B (OPT_ADLIB), B (OPT_YM3812_AUTO), "YM3812", 1, 0, 1},
/* 10 */
- {B (OPT_MIDI) | B (OPT_YM3812) | B (OPT_YM3812_AUTO) | B (OPT_GUS), 0, "SEQUENCER", 0, 0},
- {0, 0, "CHIP_MIDI", 1, 0}
+ {B (OPT_MIDI) | B (OPT_YM3812) | B (OPT_YM3812_AUTO) | B (OPT_GUS), 0, "SEQUENCER", 0, 0, 1},
+ {0, 0, "CHIP_MIDI", 1, 0, 0}
};
char *questions[] =
@@ -112,20 +113,21 @@ char *questions[] =
"SoundBlaster support",
"AdLib support",
"Gravis Ultrasound support",
- "MPU-401 support",
+ "MPU-401 support (NOT for SB16)",
- "SoundBlaster Pro support (required for SB16 also)",
+ "SoundBlaster Pro support",
"SoundBlaster 16 support",
"digitized voice support",
"This should not be asked",
"MIDI interface support",
"This should not be asked",
- "Internal synthesizer (FM/GUS) support",
+ "FM synthesizer (YM3812/OPL-3) support",
"/dev/sequencer support",
"MIDI on CHIP support"
};
unsigned long selected_options = 0;
+int sb_dma = 0;
int
can_select_option (int nr)
@@ -168,7 +170,7 @@ can_select_option (int nr)
}
int
-think_positively (void)
+think_positively (int def_answ)
{
char answ[512];
int len;
@@ -184,7 +186,7 @@ think_positively (void)
}
if (len < 2) /* There is an additional LF at the end */
- return 0;
+ return def_answ;
answ[len - 1] = 0;
@@ -238,7 +240,7 @@ main (int argc, char *argv[])
fprintf (stderr, "Do you want to include full version of the sound driver (n/y) ? ");
- if (think_positively ())
+ if (think_positively (0))
{
selected_options = 0xffffffff & ~B (OPT_MPU401);
fprintf (stderr, "Note! MPU-401 driver was not enabled\n");
@@ -247,7 +249,7 @@ main (int argc, char *argv[])
else
{
fprintf (stderr, "Do you want to DISABLE the Sound Driver (n/y) ?");
- if (think_positively ())
+ if (think_positively (0))
{
printf ("#undef CONFIGURE_SOUNDCARD\n");
printf ("#undef KERNEL_SOUNDCARD\n");
@@ -270,8 +272,12 @@ main (int argc, char *argv[])
}
else
{
- fprintf (stderr, " %s (n/y) ? ", questions[i]);
- if (think_positively ())
+ int def_answ = hw_table[i].default_answ;
+
+ fprintf (stderr,
+ def_answ ? " %s (y/n) ? " : " %s (n/y) ? ",
+ questions[i]);
+ if (think_positively (def_answ))
if (hw_table[i].alias)
selected_options |= B (hw_table[i].alias);
else
@@ -280,6 +286,9 @@ main (int argc, char *argv[])
}
}
+ if (selected_options & B(OPT_SB16))
+ selected_options |= B(OPT_SBPRO);
+
if (!(selected_options & ANY_DEVS))
{
printf ("#undef CONFIGURE_SOUNDCARD\n");
@@ -306,7 +315,7 @@ main (int argc, char *argv[])
*/
printf ("\n");
-#ifdef linux
+#if defined(linux)
if (selected_options & B (OPT_SB) && selected_options & (B (OPT_AUDIO) | B (OPT_MIDI)))
{
fprintf (stderr, "\nIRQ number for SoundBlaster?\n"
@@ -343,6 +352,7 @@ main (int argc, char *argv[])
}
fprintf (stderr, "SoundBlaster DMA set to %d\n", num);
printf ("#define SBC_DMA %d\n", num);
+ sb_dma = num;
}
if (selected_options & B (OPT_SB16))
@@ -354,7 +364,7 @@ main (int argc, char *argv[])
"Enter the value: ");
num = ask_value ("%d", 6);
- if (num < 5 || num > 7)
+ if ((num < 5 || num > 7) && (num != sb_dma))
{
fprintf (stderr, "*** Illegal input! ***\n");
@@ -362,6 +372,15 @@ main (int argc, char *argv[])
}
fprintf (stderr, "SoundBlaster DMA set to %d\n", num);
printf ("#define SB16_DMA %d\n", num);
+
+ fprintf (stderr, "\nI/O base for SB16 Midi?\n"
+ "Possible values are 300 and 330\n"
+ "The factory default is 330\n"
+ "Enter the SB16 Midi I/O base: ");
+
+ num = ask_value ("%x", 0x330);
+ fprintf (stderr, "SB16 Midi I/O base set to %03x\n", num);
+ printf ("#define SB16MIDI_BASE 0x%03x\n", num);
}
}
@@ -476,7 +495,7 @@ main (int argc, char *argv[])
printf ("#define MPU_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for MPU-401?\n"
- "Valid numbers are: 3, 4, 5, 7 and 9.\n"
+ "Valid numbers are: 3, 4, 5, 7 and 9(=2).\n"
"The default value is 5.\n"
"Enter the value: ");
@@ -496,10 +515,14 @@ main (int argc, char *argv[])
{
def_size = 16384;
- if (selected_options & (B (OPT_SBPRO) | B (OPT_PAS)))
+ if (selected_options & (B (OPT_SBPRO) | B (OPT_PAS) | B(OPT_SB16)))
def_size = 32768;
- if ((selected_options & B (OPT_PAS)) && !full_driver)
- def_size = 65536; /* PAS16 alone */
+
+#ifndef __386BSD__
+ if (((selected_options & B (OPT_PAS)) || (selected_options & B (OPT_SB16))) &&
+ !full_driver)
+ def_size = 65536; /* PAS16 or SB16 */
+#endif
fprintf (stderr, "\nSelect the DMA buffer size (4096, 16384, 32768 or 65536 bytes)\n"
"%d is recommended value for this configuration.\n"
@@ -519,5 +542,9 @@ main (int argc, char *argv[])
printf ("#define SELECTED_SOUND_OPTIONS\t0x%08x\n", selected_options);
fprintf (stderr, "The sound driver is now configured.\n");
+#if defined(SCO) || defined(ISC) || defined(SYSV)
+ fprintf(stderr, "Rember to update the System file\n");
+#endif
+
exit (0);
}
diff --git a/drivers/sound/dev_table.c b/drivers/sound/dev_table.c
index fb995e8..ca0e514 100644
--- a/drivers/sound/dev_table.c
+++ b/drivers/sound/dev_table.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/chr_drv/sound/dev_table.c
+ * sound/dev_table.c
*
* Device call tables.
*
@@ -48,12 +48,14 @@ sndtable_init (long mem_start)
mem_start = supported_drivers[i].attach (mem_start, &supported_drivers[i].config);
#ifndef SHORT_BANNERS
- printk (" at 0x%3x irq %d drq %d\n",
+ printk (" at 0x%x irq %d drq %d\n",
supported_drivers[i].config.io_base,
supported_drivers[i].config.irq,
supported_drivers[i].config.dma);
#endif
}
+ else
+ supported_drivers[i].enabled=0; /* Mark as not detected */
return mem_start;
}
@@ -67,7 +69,14 @@ sndtable_probe (int unit, struct address_info *hw_config)
for (i = 0; i < (n - 1); i++)
if (supported_drivers[i].card_type == unit)
- return supported_drivers[i].probe (hw_config);
+ {
+ supported_drivers[i].config.io_base = hw_config->io_base;
+ supported_drivers[i].config.irq = hw_config->irq;
+ supported_drivers[i].config.dma = hw_config->dma;
+ if (supported_drivers[i].probe (hw_config)) return 1;
+ supported_drivers[i].enabled=0; /* Mark as not detected */
+ return 0;
+ }
return FALSE;
}
@@ -87,6 +96,10 @@ sndtable_init_card (int unit, struct address_info *hw_config)
for (i = 0; i < (n - 1); i++)
if (supported_drivers[i].card_type == unit)
{
+ supported_drivers[i].config.io_base = hw_config->io_base;
+ supported_drivers[i].config.irq = hw_config->irq;
+ supported_drivers[i].config.dma = hw_config->dma;
+
if (supported_drivers[i].attach (0, hw_config) != 0)
panic ("snd#: Invalid memory allocation\n");
return TRUE;
@@ -101,12 +114,11 @@ sndtable_get_cardcount (void)
return num_dspdevs + num_mixers + num_synths + num_midis;
}
+#ifdef linux
void sound_setup(char *str, int *ints)
{
int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
- printk("sound_setup(%d) called\n", ints[0]);
-
/*
* First disable all drivers
*/
@@ -154,6 +166,27 @@ void sound_setup(char *str, int *ints)
}
}
}
+#else
+void sound_chconf(int card_type, int ioaddr, int irq, int dma)
+{
+ int i, n = sizeof (supported_drivers) / sizeof (struct card_info);
+
+ int ptr, j;
+
+ ptr = -1;
+ for (j=0;j<n && ptr == -1;j++)
+ if (supported_drivers[j].card_type == card_type)
+ ptr = j;
+
+ if (ptr != -1)
+ {
+ supported_drivers[ptr].enabled = 1;
+ if (ioaddr) supported_drivers[ptr].config.io_base = ioaddr;
+ if (irq) supported_drivers[ptr].config.irq = irq;
+ if (dma) supported_drivers[ptr].config.dma = dma;
+ }
+}
+#endif
struct address_info *sound_getconf(int card_type)
{
diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h
index c4aad02..d95137b 100644
--- a/drivers/sound/dev_table.h
+++ b/drivers/sound/dev_table.h
@@ -182,10 +182,16 @@ struct generic_midi_operations {
{SBC_BASE, SBC_IRQ, SBC_DMA}, SND_DEFAULT_ENABLE},
#endif
-#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_AUDIO)
+#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16)
+#ifndef EXCLUDE_AUDIO
{SNDCARD_SB16, "SoundBlaster16", sb16_dsp_init, sb16_dsp_detect,
{SBC_BASE, SBC_IRQ, SB16_DMA}, SND_DEFAULT_ENABLE},
#endif
+#ifndef EXCLUDE_MIDI
+ {SNDCARD_SB16MIDI,"SB16 MPU-401", attach_sb16midi, probe_sb16midi,
+ {SB16MIDI_BASE, SBC_IRQ, 0}, SND_DEFAULT_ENABLE},
+#endif
+#endif
#ifndef EXCLUDE_YM3812
{SNDCARD_ADLIB, "AdLib", attach_adlib_card, probe_adlib,
@@ -229,6 +235,7 @@ long sndtable_init(long mem_start);
int sndtable_get_cardcount (void);
long CMIDI_init(long mem_start); /* */
struct address_info *sound_getconf(int card_type);
+void sound_chconf(int card_type, int ioaddr, int irq, int dma);
#endif
#endif
diff --git a/drivers/sound/dmabuf.c b/drivers/sound/dmabuf.c
index c67ce32..82f427a 100644
--- a/drivers/sound/dmabuf.c
+++ b/drivers/sound/dmabuf.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/chr_drv/sound/dmabuf.c
+ * sound/dmabuf.c
*
* The DMA buffer manager for digitized voice applications
*
@@ -103,8 +103,8 @@ reorganize_buffers (int dev)
* This routine breaks the physical device buffers to logical ones.
*/
- unsigned long i, p, n;
- unsigned long sr, nc, sz, bsz;
+ unsigned i, p, n;
+ unsigned sr, nc, sz, bsz;
sr = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1);
nc = dsp_devs[dev]->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1);
@@ -112,8 +112,7 @@ reorganize_buffers (int dev)
if (sr < 1 || nc < 1 || sz < 1)
{
- printk ("SOUND: Invalid PCM parameters[%d] sr=%lu, nc=%lu, sz=%lu\n",
- dev, sr, nc, sz);
+ printk ("SOUND: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);
sr = DSP_DEFAULT_SPEED;
nc = 1;
sz = 8;
@@ -210,6 +209,10 @@ DMAbuf_open (int dev, int mode)
return RET_ERROR (ENXIO);
}
+#ifdef USE_RUNTIME_DMAMEM
+ sound_dma_malloc(dev);
+#endif
+
if (snd_raw_buf[dev][0] == NULL)
return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */
@@ -300,6 +303,10 @@ DMAbuf_release (int dev, int mode)
dma_sync (dev);
}
+#ifdef USE_RUNTIME_DMAMEM
+ sound_dma_free(dev);
+#endif
+
dsp_devs[dev]->reset (dev);
dsp_devs[dev]->close (dev);
@@ -342,6 +349,7 @@ DMAbuf_getrdbuffer (int dev, char **buf, int *len)
if (dev_needs_restart[dev])
{
dma_reset(dev);
+ dev_needs_restart[dev] = 0;
}
if (!dev_active[dev])
@@ -606,9 +614,11 @@ DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
chan);
#else
#if defined(ISC) || defined(SCO)
+#ifndef DMAMODE_AUTO
printk ("sound: Invalid DMA mode for device %d\n", dev);
+#endif
dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode)
-#ifdef ISC
+#ifdef DMAMODE_AUTO
| DMAMODE_AUTO
#endif
,
@@ -703,7 +713,7 @@ DMAbuf_outputintr (int dev, int underrun_flag)
}
DISABLE_INTR (flags);
- if (SOMEONE_WAITING (dev_sleep_flag[dev]))
+ if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
{
WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
}
@@ -721,6 +731,7 @@ DMAbuf_inputintr (int dev)
}
else if (dev_qlen[dev] == (dev_nbufs[dev] - 1))
{
+ printk("Sound: Recording overrun\n");
dev_underrun[dev]++;
dsp_devs[dev]->halt_xfer (dev);
dev_active[dev] = 0;
@@ -738,7 +749,7 @@ DMAbuf_inputintr (int dev)
}
DISABLE_INTR (flags);
- if (SOMEONE_WAITING (dev_sleep_flag[dev]))
+ if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev]))
{
WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]);
}
diff --git a/drivers/sound/gus_card.c b/drivers/sound/gus_card.c
index 69df61c..e7967e8 100644
--- a/drivers/sound/gus_card.c
+++ b/drivers/sound/gus_card.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/chr_drv/sound/gus_card.c
+ * sound/gus_card.c
*
* Detection routine for the Gravis Ultrasound.
*
@@ -63,7 +63,7 @@ attach_gus_card (long mem_start, struct address_info *hw_config)
if (io_addr != hw_config->io_base) /* Already tested */
if (gus_wave_detect (io_addr))
{
- printk (" WARNING! GUS found at %03x, config was %03x ", io_addr, hw_config->io_base);
+ printk (" WARNING! GUS found at %x, config was %x ", io_addr, hw_config->io_base);
mem_start = gus_wave_init (mem_start, hw_config->irq, hw_config->dma);
#ifndef EXCLUDE_MIDI
mem_start = gus_midi_init (mem_start);
diff --git a/drivers/sound/gus_midi.c b/drivers/sound/gus_midi.c
index 3ba8ad3..5e06f7f 100644
--- a/drivers/sound/gus_midi.c
+++ b/drivers/sound/gus_midi.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/chr_drv/sound/gus2_midi.c
+ * sound/gus2_midi.c
*
* The low level driver for the GUS Midi Interface.
*
diff --git a/drivers/sound/gus_wave.c b/drivers/sound/gus_wave.c
index f773ddd..51186d3 100644
--- a/drivers/sound/gus_wave.c
+++ b/drivers/sound/gus_wave.c
@@ -1,6 +1,5 @@
-
/*
- * linux/kernel/chr_drv/sound/gus_wave.c
+ * sound/gus_wave.c
*
* Driver for the Gravis UltraSound wave table synth.
*
@@ -36,7 +35,7 @@
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_GUS)
-#define MAX_SAMPLE 256
+#define MAX_SAMPLE 128
#define MAX_PATCH 256
struct voice_info
@@ -144,7 +143,7 @@ static int freq_div_table[] =
19293 /* 32 */
};
-static struct patch_info samples[MAX_SAMPLE + 1];
+static struct patch_info *samples;
static long sample_ptrs[MAX_SAMPLE + 1];
static int sample_map[32];
static int free_sample;
@@ -231,14 +230,14 @@ gus_peek (long addr)
}
void
-gus_write8 (int reg, unsigned char data)
+gus_write8 (int reg, unsigned int data)
{
unsigned long flags;
DISABLE_INTR (flags);
OUTB (reg, u_Command);
- OUTB (data, u_DataHi);
+ OUTB ((unsigned char)(data & 0xff), u_DataHi);
RESTORE_INTR (flags);
}
@@ -272,7 +271,7 @@ gus_look8 (int reg)
}
void
-gus_write16 (int reg, unsigned short data)
+gus_write16 (int reg, unsigned int data)
{
unsigned long flags;
@@ -280,8 +279,8 @@ gus_write16 (int reg, unsigned short data)
OUTB (reg, u_Command);
- OUTB (data & 0xff, u_DataLo);
- OUTB ((data >> 8) & 0xff, u_DataHi);
+ OUTB ((unsigned char)(data & 0xff), u_DataLo);
+ OUTB ((unsigned char)((data >> 8) & 0xff), u_DataHi);
RESTORE_INTR (flags);
}
@@ -348,11 +347,11 @@ gus_select_max_voices (int nvoices)
}
static void
-gus_voice_on (unsigned char mode)
+gus_voice_on (unsigned int mode)
{
- gus_write8 (0x00, mode & 0xfc);
+ gus_write8 (0x00, (unsigned char)(mode & 0xfc));
gus_delay ();
- gus_write8 (0x00, mode & 0xfc);
+ gus_write8 (0x00, (unsigned char)(mode & 0xfc));
}
static void
@@ -362,8 +361,9 @@ gus_voice_off (void)
}
static void
-gus_voice_mode (unsigned char mode)
+gus_voice_mode (unsigned int m)
{
+ unsigned char mode = (unsigned char)(m & 0xff);
gus_write8 (0x00, (gus_read8 (0x00) & 0x03) | (mode & 0xfc)); /* Don't start or stop
* voice */
gus_delay ();
@@ -383,42 +383,46 @@ gus_voice_freq (unsigned long freq)
}
static void
-gus_voice_volume (unsigned short vol)
+gus_voice_volume (unsigned int vol)
{
gus_write8 (0x0d, 0x03); /* Stop ramp before setting volume */
- gus_write16 (0x09, vol << 4);
+ gus_write16 (0x09, (unsigned short)(vol << 4));
}
static void
-gus_voice_balance (unsigned char balance)
+gus_voice_balance (unsigned int balance)
{
- gus_write8 (0x0c, balance);
+ gus_write8 (0x0c, (unsigned char)(balance & 0xff));
}
static void
-gus_ramp_range (unsigned short low, unsigned short high)
+gus_ramp_range (unsigned int low, unsigned int high)
{
- gus_write8 (0x07, (low >> 4) & 0xff);
- gus_write8 (0x08, (high >> 4) & 0xff);
+ gus_write8 (0x07, (unsigned char)((low >> 4) & 0xff));
+ gus_write8 (0x08, (unsigned char)((high >> 4) & 0xff));
}
static void
-gus_ramp_rate (unsigned char scale, unsigned char rate)
+gus_ramp_rate (unsigned int scale, unsigned int rate)
{
- gus_write8 (0x06, ((scale & 0x03) << 6) | (rate & 0x3f));
+ gus_write8 (0x06, (unsigned char)(((scale & 0x03) << 6) | (rate & 0x3f)));
}
static void
-gus_rampon (unsigned char mode)
+gus_rampon (unsigned int m)
{
+ unsigned char mode = (unsigned char)(m & 0xff);
+
gus_write8 (0x0d, mode & 0xfc);
gus_delay ();
gus_write8 (0x0d, mode & 0xfc);
}
static void
-gus_ramp_mode (unsigned char mode)
+gus_ramp_mode (unsigned int m)
{
+ unsigned char mode = (unsigned char)(m & 0xff);
+
gus_write8 (0x0d, (gus_read8 (0x0d) & 0x03) | (mode & 0xfc)); /* Don't start or stop
* ramping */
gus_delay ();
@@ -911,6 +915,14 @@ static void
compute_and_set_volume (int voice, int volume, int ramp_time)
{
int current, target, rate;
+ unsigned long flags;
+
+ DISABLE_INTR(flags);
+/*
+ * CAUTION! Interrupts disabled. Enable them before returning
+ */
+
+ gus_select_voice(voice);
compute_volume (voice, volume);
voices[voice].current_volume = voices[voice].initial_volume;
@@ -922,6 +934,7 @@ compute_and_set_volume (int voice, int volume, int ramp_time)
{
gus_rampoff ();
gus_voice_volume (target);
+ RESTORE_INTR(flags);
return;
}
@@ -935,6 +948,7 @@ compute_and_set_volume (int voice, int volume, int ramp_time)
{
gus_rampoff ();
gus_voice_volume (target);
+ RESTORE_INTR(flags);
return;
}
@@ -953,6 +967,7 @@ compute_and_set_volume (int voice, int volume, int ramp_time)
gus_ramp_range (target, current);
gus_rampon (0x40); /* Ramp down, once, no irq */
}
+ RESTORE_INTR(flags);
}
static void
@@ -1274,7 +1289,7 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
if (format != GUS_PATCH)
{
- printk ("GUS Error: Invalid patch format (key) 0x%04x\n", format);
+ printk ("GUS Error: Invalid patch format (key) 0x%x\n", format);
return RET_ERROR (EINVAL);
}
@@ -1309,14 +1324,14 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
if (count < patch.len)
{
- printk ("GUS Warning: Patch record too short (%d<%lu)\n",
- count, patch.len);
+ printk ("GUS Warning: Patch record too short (%d<%d)\n",
+ count, (int)patch.len);
patch.len = count;
}
if (patch.len <= 0 || patch.len > gus_mem_size)
{
- printk ("GUS: Invalid sample length %lu\n", patch.len);
+ printk ("GUS: Invalid sample length %d\n", (int)patch.len);
return RET_ERROR (EINVAL);
}
@@ -1346,7 +1361,7 @@ guswave_load_patch (int dev, int format, snd_rw_buf * addr,
*/
if (patch.len >= GUS_BANK_SIZE)
{
- printk ("GUS: Sample (16 bit) too long %lu\n", patch.len);
+ printk ("GUS: Sample (16 bit) too long %d\n", (int)patch.len);
return RET_ERROR (ENOSPC);
}
@@ -2317,7 +2332,7 @@ static struct synth_operations guswave_operations =
long
gus_wave_init (long mem_start, int irq, int dma)
{
- printk (" <Gravis UltraSound %luk>", gus_mem_size / 1024);
+ printk (" <Gravis UltraSound %dk>", (int)gus_mem_size / 1024);
if (irq < 0 || irq > 15)
{
@@ -2339,6 +2354,9 @@ gus_wave_init (long mem_start, int irq, int dma)
else
synth_devs[num_synths++] = &guswave_operations;
+ PERMANENT_MALLOC(struct patch_info*, samples,
+ (MAX_SAMPLE+1)*sizeof(*samples), mem_start);
+
reset_sample_memory ();
gus_initialize ();
@@ -2505,7 +2523,7 @@ guswave_dma_irq (void)
switch (active_device)
{
case GUS_DEV_WAVE:
- if (SOMEONE_WAITING (dram_sleep_flag))
+ if (SOMEONE_WAITING (dram_sleeper, dram_sleep_flag))
WAKE_UP (dram_sleeper, dram_sleep_flag);
break;
diff --git a/drivers/sound/midibuf.c b/drivers/sound/midibuf.c
index 0196f84..54b7266 100644
--- a/drivers/sound/midibuf.c
+++ b/drivers/sound/midibuf.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/chr_drv/sound/midibuf.c
+ * sound/midibuf.c
*
* Device file manager for /dev/midi
*
diff --git a/drivers/sound/mpu401.c b/drivers/sound/mpu401.c
index 053fa8a..8be8fee 100644
--- a/drivers/sound/mpu401.c
+++ b/drivers/sound/mpu401.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/chr_drv/sound/mpu401.c
+ * sound/mpu401.c
*
* The low level driver for Roland MPU-401 compatible Midi cards.
*
diff --git a/drivers/sound/opl3.c b/drivers/sound/opl3.c
index 3a70f8b..09e2765 100644
--- a/drivers/sound/opl3.c
+++ b/drivers/sound/opl3.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/chr_drv/sound/opl3.c
+ * sound/opl3.c
*
* A low level driver for Yamaha YM3812 and OPL-3 -chips
*
@@ -59,8 +59,7 @@ struct voice_info
static struct voice_info voices[MAX_VOICE];
-typedef struct sbi_instrument instr_array[SBFM_MAXINSTR];
-static instr_array instrmap;
+static struct sbi_instrument *instrmap;
static struct sbi_instrument *active_instrument[MAX_VOICE] =
{NULL};
@@ -75,7 +74,7 @@ static int fm_model = 0; /* 0=no fm, 1=mono, 2=SB Pro 1, 3=SB Pro 2 */
static int store_instr (int instr_no, struct sbi_instrument *instr);
static void freq_to_fnum (int freq, int *block, int *fnum);
-static void opl3_command (int io_addr, const unsigned char addr, const unsigned char val);
+static void opl3_command (int io_addr, unsigned int addr, unsigned int val);
static int opl3_kill_note (int dev, int voice, int velocity);
static unsigned char connection_mask = 0x00;
@@ -273,7 +272,7 @@ store_instr (int instr_no, struct sbi_instrument *instr)
{
if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || !opl3_enabled))
- printk ("FM warning: Invalid patch format field (key) 0x%04x\n", instr->key);
+ printk ("FM warning: Invalid patch format field (key) 0x%x\n", instr->key);
memcpy ((char *) &(instrmap[instr_no]), (char *) instr, sizeof (*instr));
return 0;
@@ -619,7 +618,7 @@ freq_to_fnum (int freq, int *block, int *fnum)
}
static void
-opl3_command (int io_addr, const unsigned char addr, const unsigned char val)
+opl3_command (int io_addr, unsigned int addr, unsigned int val)
{
int i;
@@ -628,7 +627,7 @@ opl3_command (int io_addr, const unsigned char addr, const unsigned char val)
* register. The OPL-3 survives with just two INBs
*/
- OUTB (addr, io_addr); /* Select register */
+ OUTB ((unsigned char)(addr & 0xff), io_addr); /* Select register */
if (!opl3_enabled)
tenmicrosec ();
@@ -636,7 +635,7 @@ opl3_command (int io_addr, const unsigned char addr, const unsigned char val)
for (i = 0; i < 2; i++)
INB (io_addr);
- OUTB (val, io_addr + 1); /* Write to register */
+ OUTB ((unsigned char)(val & 0xff), io_addr + 1); /* Write to register */
if (!opl3_enabled)
{
@@ -895,6 +894,9 @@ opl3_init (long mem_start)
{
int i;
+ PERMANENT_MALLOC(struct sbi_instrument*, instrmap,
+ SBFM_MAXINSTR*sizeof(*instrmap), mem_start);
+
synth_devs[num_synths++] = &opl3_operations;
fm_model = 0;
opl3_ok = 1;
diff --git a/drivers/sound/os.h b/drivers/sound/os.h
index c7ba783..bff4a43 100644
--- a/drivers/sound/os.h
+++ b/drivers/sound/os.h
@@ -41,7 +41,7 @@
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/dma.h>
-#include <linux/kd.h>
+#include <sys/kd.h>
#include <linux/wait.h>
#include <linux/malloc.h>
#include <linux/soundcard.h>
@@ -63,15 +63,6 @@ typedef char snd_rw_buf;
#define IOCTL_IN(arg) get_fs_long((long *)(arg))
#define IOCTL_OUT(arg, ret) snd_ioctl_return((int *)arg, ret)
-/*
-#define DEFINE_WAIT_QUEUE(name, flag) static struct wait_queue *name = NULL; static int flag = 0
-#define DEFINE_WAIT_QUEUES(name, flag) static struct wait_queue *name = {NULL}; static int flag = {0}
-#define PROCESS_ABORTING(wqueue, flags) (current->signal & ~current->blocked)
-#define REQUEST_TIMEOUT(nticks, wqueue) current->timeout = jiffies + (nticks);
-#define INTERRUPTIBLE_SLEEP_ON(q, f) \
- {f = 1;interruptible_sleep_on(&q);f=0;}
-*/
-
struct snd_wait {
int mode; int aborting;
};
@@ -98,7 +89,7 @@ struct snd_wait {
} \
f.mode &= ~WK_SLEEP; \
}
-#define SOMEONE_WAITING(f) (f.mode & WK_SLEEP)
+#define SOMEONE_WAITING(q, f) (f.mode & WK_SLEEP)
#define WAKE_UP(q, f) {f.mode = WK_WAKEUP;wake_up(&q);}
#define ALLOC_DMA_CHN(chn) request_dma(chn)
@@ -129,6 +120,19 @@ struct snd_wait {
#define KERNEL_FREE(addr) kfree(addr)
/*
+ * The macro PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr)
+ * returns size bytes of
+ * (kernel virtual) memory which will never get freed by the driver.
+ * This macro is called only during boot. The linux_ptr is a linux specific
+ * parameter which should be ignored in other operating systems.
+ * The mem_ptr is a pointer variable where the macro assigns pointer to the
+ * memory area. The type is the type of the mem_ptr.
+ */
+#define PERMANENT_MALLOC(typecast, mem_ptr, size, linux_ptr) \
+ {mem_ptr = (typecast)linux_ptr; \
+ linux_ptr += (size);}
+
+/*
* The macro DEFINE_TIMER defines variables for the ACTIVATE_TIMER if
* required. The name is the variable/name to be used and the proc is
* the procedure to be called when the timer expires.
diff --git a/drivers/sound/pas.h b/drivers/sound/pas.h
index f431c14..797c8fd 100644
--- a/drivers/sound/pas.h
+++ b/drivers/sound/pas.h
@@ -132,6 +132,7 @@
#define PAS_PLUS 1
#define PAS_CDPC 2
#define PAS_16 3
+#define PAS_16D 4
#ifdef DEFINE_TRANSLATIONS
char I_C_2_PCM_DMA_translate[] = /* R W PCM PCM DMA channel value translations */
diff --git a/drivers/sound/pas2_card.c b/drivers/sound/pas2_card.c
index 61ea6dd..98a2f92 100644
--- a/drivers/sound/pas2_card.c
+++ b/drivers/sound/pas2_card.c
@@ -1,7 +1,7 @@
#define _PAS2_CARD_C_
#define SND_SA_INTERRUPT
/*
- * linux/kernel/chr_drv/sound/pas2_card.c
+ * sound/pas2_card.c
*
* Detection routine for the Pro Audio Spectrum cards.
*
@@ -218,7 +218,7 @@ config_pas_hw (struct address_info *hw_config)
* selects filter rate
* of 17.897 kHz */
- if (pas_model == PAS_16)
+ if (pas_model == PAS_16 || pas_model == PAS_16D)
pas_write (8, PRESCALE_DIVIDER);
else
pas_write (0, PRESCALE_DIVIDER);
diff --git a/drivers/sound/pas2_midi.c b/drivers/sound/pas2_midi.c
index 3de7217..13cf97d 100644
--- a/drivers/sound/pas2_midi.c
+++ b/drivers/sound/pas2_midi.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/chr_drv/sound/pas2_midi.c
+ * sound/pas2_midi.c
*
* The low level driver for the PAS Midi Interface.
*
@@ -283,7 +283,7 @@ pas_midi_interrupt (void)
if (stat & M_S_OUTPUT_OVERRUN)
{
- printk ("MIDI output overrun %02x,%02x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes);
+ printk ("MIDI output overrun %x,%x,%d \n", pas_read (MIDI_FIFO_STATUS), stat, ofifo_bytes);
ofifo_bytes = 100;
}
diff --git a/drivers/sound/pas2_mixer.c b/drivers/sound/pas2_mixer.c
index 2ee38c9..63be9e5 100644
--- a/drivers/sound/pas2_mixer.c
+++ b/drivers/sound/pas2_mixer.c
@@ -1,7 +1,7 @@
#define _PAS2_MIXER_C_
/*
- * linux/kernel/chr_drv/sound/pas2_mixer.c
+ * sound/pas2_mixer.c
*
* Mixer routines for the Pro Audio Spectrum cards.
*
diff --git a/drivers/sound/pas2_pcm.c b/drivers/sound/pas2_pcm.c
index ddd061c..b396f61 100644
--- a/drivers/sound/pas2_pcm.c
+++ b/drivers/sound/pas2_pcm.c
@@ -1,6 +1,6 @@
#define _PAS2_PCM_C_
/*
- * linux/kernel/chr_drv/sound/pas2_pcm.c
+ * sound/pas2_pcm.c
*
* The low level driver for the Pro Audio Spectrum ADC/DAC.
*
@@ -148,6 +148,8 @@ pas_pcm_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
break;
case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return pcm_set_channels (arg);
return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg)));
break;
diff --git a/drivers/sound/patmgr.c b/drivers/sound/patmgr.c
index e0cc157..f5697ae 100644
--- a/drivers/sound/patmgr.c
+++ b/drivers/sound/patmgr.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/chr_drv/sound/patmgr.c
+ * sound/patmgr.c
*
* The patch maneger interface for the /dev/sequencer
*
@@ -73,7 +73,7 @@ pmgr_release (int dev)
mbox[dev]->key = PM_ERROR;
mbox[dev]->parm1 = RET_ERROR (EIO);
- if (SOMEONE_WAITING (appl_wait_flag))
+ if (SOMEONE_WAITING (appl_proc, appl_wait_flag))
WAKE_UP (appl_proc, appl_wait_flag);
}
@@ -161,7 +161,7 @@ pmgr_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
COPY_FROM_USER (&((char *) mbox[dev])[4], buf, 4, count - 4);
msg_direction[dev] = S_TO_A;
- if (SOMEONE_WAITING (appl_wait_flag))
+ if (SOMEONE_WAITING (appl_proc, appl_wait_flag))
{
WAKE_UP (appl_proc, appl_wait_flag);
}
@@ -188,7 +188,7 @@ pmgr_access (int dev, struct patmgr_info *rec)
mbox[dev] = rec;
msg_direction[dev] = A_TO_S;
- if (SOMEONE_WAITING (server_wait_flag[dev]))
+ if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev]))
{
WAKE_UP (server_procs[dev], server_wait_flag[dev]);
}
@@ -242,7 +242,7 @@ pmgr_inform (int dev, int event, unsigned long p1, unsigned long p2,
mbox[dev]->parm3 = p3;
msg_direction[dev] = A_TO_S;
- if (SOMEONE_WAITING (server_wait_flag[dev]))
+ if (SOMEONE_WAITING (server_procs[dev], server_wait_flag[dev]))
{
WAKE_UP (server_procs[dev], server_wait_flag[dev]);
}
diff --git a/drivers/sound/sb16_dsp.c b/drivers/sound/sb16_dsp.c
index 060a8d3..4c799c0 100644
--- a/drivers/sound/sb16_dsp.c
+++ b/drivers/sound/sb16_dsp.c
@@ -1,17 +1,600 @@
-/* Sorry! */
+/*
+ * sound/sb16_dsp.c
+ *
+ * The low level driver for the SoundBlaster DSP chip.
+ *
+ * (C) 1993 J. Schubert (jsb@sth.ruhr-uni-bochum.de)
+ *
+ * based on SB-driver by (C) Hannu Savolainen
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#define DEB(x)
+#define DEB1(x)
+/*
+#define DEB_DMARES
+*/
#include "sound_config.h"
+#include "sb.h"
+#include "sb_mixer.h"
-void
-sb16_dsp_interrupt (int unused)
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_AUDIO) && !defined(EXCLUDE_SBPRO)
+
+extern int sbc_base;
+
+static int sb16_dsp_ok = 0; /* Set to 1 after successful initialization */
+static int dsp_16bit = 0;
+static int dsp_stereo = 0;
+static int dsp_current_speed = 8000;/*DSP_DEFAULT_SPEED;*/
+static int dsp_busy = 0;
+static int dma16, dma8;
+static unsigned long dsp_count = 0;
+
+static int irq_mode = IMODE_NONE; /* IMODE_INPUT, IMODE_OUTPUT or
+ IMODE_NONE */
+static int my_dev = 0;
+
+static volatile int intr_active = 0;
+
+static int sb16_dsp_open (int dev, int mode);
+static void sb16_dsp_close (int dev);
+static void sb16_dsp_output_block (int dev, unsigned long buf, int count,int intrflag, int dma_restart);
+static void sb16_dsp_start_input (int dev, unsigned long buf, int count,int intrflag, int dma_restart);
+static int sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg,int local);
+static int sb16_dsp_prepare_for_input (int dev, int bsize, int bcount);
+static int sb16_dsp_prepare_for_output (int dev, int bsize, int bcount);
+static void sb16_dsp_reset (int dev);
+static void sb16_dsp_halt (int dev);
+static int dsp_set_speed (int);
+static int dsp_set_stereo (int);
+static void dsp_cleanup (void);
+int sb_reset_dsp (void);
+
+static struct audio_operations sb16_dsp_operations =
+{
+ "SoundBlaster 16",
+ sb16_dsp_open,
+ sb16_dsp_close,
+ sb16_dsp_output_block,
+ sb16_dsp_start_input,
+ sb16_dsp_ioctl,
+ sb16_dsp_prepare_for_input,
+ sb16_dsp_prepare_for_output,
+ sb16_dsp_reset,
+ sb16_dsp_halt,
+ NULL,
+ NULL
+};
+
+static int sb_dsp_command01 (unsigned char val)
+{
+ int i=1<<16;
+
+ while(--i & (!INB (DSP_STATUS) & 0x80));
+ if(!i)
+ printk("SB16 sb_dsp_command01 Timeout\n");
+ return sb_dsp_command(val);
+}
+
+static int wait_data_avail(int t)
+{
+ int loopc=5000000;
+ t+=GET_TIME();
+ do {
+ if(INB(DSP_DATA_AVAIL) & 0x80)
+ return 1;
+ } while(--loopc && GET_TIME()<t);
+ printk("!data_avail l=%d\n",loopc);
+ return 0;
+}
+
+static int read_dsp(int t)
+{
+ if(!wait_data_avail(t))
+ return -1;
+ else
+ return INB(DSP_READ);
+}
+
+static int dsp_ini2(void)
+{
+#if 0
+ /* sb_setmixer(0x83, sb_getmixer(0x83) | 0x03); */
+ sb_dsp_command(0xe2);
+ sb_dsp_command(0x76); /* E0 ??? */
+ sb_dsp_command(0xe2);
+ sb_dsp_command(0x30); /* A0 ??? */
+ sb_dsp_command(0xe4);
+ sb_dsp_command(0xaa);
+ sb_dsp_command(0xe8);
+ if(read_dsp(100)!=0xaa)
+ printk("Error dsp_ini2\n");
+#endif
+ return 0;
+}
+/*
+static char *dsp_getmessage(unsigned char command,int maxn)
+{
+ static char buff[100];
+ int n=0;
+
+ sb_dsp_command(command);
+ while(n<maxn && wait_data_avail(2)) {
+ buff[++n]=INB(DSP_READ);
+ if(!buff[n])
+ break;
+ }
+ buff[0]=n;
+ return buff;
+}
+
+static void dsp_showmessage(unsigned char command,int len)
+{
+ int n;
+ unsigned char *c;
+ c=dsp_getmessage(command,len);
+ printk("DSP C=%x l=%d,lr=%d b=",command,len,c[0]);
+ for(n=1;n<=c[0];n++)
+ if(c[n]>=' ' & c[n]<='z')
+ printk("%c",c[n]);
+ else
+ printk("|%x|",c[n]);
+ printk("\n");
+}
+*/
+static int dsp_set_speed(int mode)
+{
+DEB(printk("dsp_set_speed(%d)\n",mode));
+ if (mode)
+ {
+ if (mode < 5000) mode = 5000;
+ if (mode > 44100) mode = 44100;
+ dsp_current_speed=mode;
+ }
+ return mode;
+}
+
+static int dsp_set_stereo(int mode)
+{
+DEB(printk("dsp_set_stereo(%d)\n",mode));
+
+ if (mode) dsp_stereo=mode;
+
+ return mode;
+}
+
+static int dsp_set_bits(int arg) {
+DEB(printk("dsp_set_bits(%d)\n",arg));
+
+ if (arg)
+ switch(arg) {
+ case 8:
+ dsp_16bit=0; break;
+ case 16:
+ dsp_16bit=1; break;
+ default:
+ return RET_ERROR(EINVAL);
+ }
+ return dsp_16bit? 16:8;
+}
+
+static int
+sb16_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg,int local)
+{
+ switch (cmd) {
+ case SOUND_PCM_WRITE_RATE:
+ if(local)
+ return dsp_set_speed(arg);
+ return IOCTL_OUT (arg, dsp_set_speed (IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_RATE:
+ if(local)
+ return dsp_current_speed;
+ return IOCTL_OUT (arg, dsp_current_speed);
+
+ case SNDCTL_DSP_STEREO:
+ if (local)
+ return dsp_set_stereo(arg);
+ return IOCTL_OUT (arg, dsp_set_stereo(IOCTL_IN(arg)));
+
+ case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return dsp_set_stereo(arg-1)+1;
+ return IOCTL_OUT (arg, dsp_set_stereo(IOCTL_IN (arg)-1))+1;
+
+ case SOUND_PCM_READ_CHANNELS:
+ if (local)
+ return dsp_stereo+1;
+ return IOCTL_OUT (arg, dsp_stereo+1);
+
+ case SNDCTL_DSP_SAMPLESIZE:
+ if (local)
+ return dsp_set_bits (arg);
+ return IOCTL_OUT (arg, dsp_set_bits (IOCTL_IN (arg)));
+
+ case SOUND_PCM_READ_BITS:
+ if (local)
+ return dsp_16bit?16:8;
+ return IOCTL_OUT (arg, dsp_16bit?16:8);
+
+ case SOUND_PCM_WRITE_FILTER: /* NOT YET IMPLEMENTED */
+ if (IOCTL_IN (arg) > 1)
+ return IOCTL_OUT (arg, RET_ERROR (EINVAL));
+ default:
+ return RET_ERROR (EINVAL);
+ }
+
+ return RET_ERROR (EINVAL);
+}
+
+static int
+sb16_dsp_open (int dev, int mode)
+{
+ int retval;
+
+DEB(printk("sb16_dsp_open()\n"));
+ if (!sb16_dsp_ok)
+ {
+ printk ("SB16 Error: SoundBlaster board not installed\n");
+ return RET_ERROR(ENODEV);
+ }
+
+ if (intr_active)
+ return RET_ERROR(EBUSY);
+
+ retval = sb_get_irq ();
+ if (retval<0)
+ return retval;
+
+ if (ALLOC_DMA_CHN (dma8))
+ {
+ printk ("SB16: Unable to grab DMA%d\n", dma8);
+ sb_free_irq();
+ return RET_ERROR(EBUSY);
+ }
+
+ if (dma16 != dma8)
+ if (ALLOC_DMA_CHN (dma16))
+ {
+ printk ("SB16: Unable to grab DMA%d\n", dma16);
+ sb_free_irq();
+ RELEASE_DMA_CHN (dma8);
+ return RET_ERROR(EBUSY);
+ }
+
+ dsp_ini2();
+
+ irq_mode = IMODE_NONE;
+ dsp_busy = 1;
+
+ return 0;
+}
+
+static void
+sb16_dsp_close (int dev)
+{
+ unsigned long flags;
+DEB(printk("sb16_dsp_close()\n"));
+ sb_dsp_command01(0xd9);
+ sb_dsp_command01(0xd5);
+
+ DISABLE_INTR (flags);
+ RELEASE_DMA_CHN (dma8);
+
+ if (dma16 != dma8)
+ RELEASE_DMA_CHN (dma16);
+ sb_free_irq ();
+ dsp_cleanup ();
+ dsp_busy = 0;
+ RESTORE_INTR (flags);
+}
+
+static void
+sb16_dsp_output_block (int dev, unsigned long buf, int count,int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+
+ cnt = count;
+ if (dsp_16bit)
+ cnt >>= 1;
+ cnt--;
+
+#ifdef DEB_DMARES
+ printk("output_block: %x %d %d\n",buf,count,intrflag);
+ if(intrflag) {
+ int pos,chan=sound_dsp_dmachan[dev];
+ DISABLE_INTR (flags);
+ clear_dma_ff(chan);
+ disable_dma(chan);
+ pos=get_dma_residue(chan);
+ enable_dma(chan);
+ RESTORE_INTR (flags);
+ printk("dmapos=%d %x\n",pos,pos);
+ }
+#endif
+ if (sound_dma_automode[dev] &&
+ intrflag &&
+ cnt == dsp_count) {
+ irq_mode = IMODE_OUTPUT;
+ intr_active = 1;
+ return; /* Auto mode on. No need to react */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ sb16_dsp_halt(dev);
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+ }
+ sb_dsp_command (0x41);
+ sb_dsp_command ((unsigned char)((dsp_current_speed >> 8) & 0xff));
+ sb_dsp_command ((unsigned char)(dsp_current_speed & 0xff));
+ sb_dsp_command ((unsigned char)(dsp_16bit ? 0xb6 : 0xc6));
+ sb_dsp_command ((unsigned char)((dsp_stereo ? 0x20 : 0) +
+ (dsp_16bit ? 0x10:0)));
+ sb_dsp_command01 ((unsigned char)(cnt&0xff));
+ sb_dsp_command ((unsigned char)(cnt>>8));
+ /* sb_dsp_command (0);
+ sb_dsp_command (0); */
+
+ RESTORE_INTR (flags);
+ dsp_count=cnt;
+ irq_mode = IMODE_OUTPUT;
+ intr_active = 1;
+}
+
+static void
+sb16_dsp_start_input (int dev, unsigned long buf, int count,int intrflag, int dma_restart)
+{
+ unsigned long flags, cnt;
+
+ cnt = count;
+ if (dsp_16bit)
+ cnt >>= 1;
+ cnt--;
+
+#ifdef DEB_DMARES
+printk("start_input: %x %d %d\n",buf,count,intrflag);
+ if(intrflag) {
+ int pos,chan=sound_dsp_dmachan[dev];
+ DISABLE_INTR (flags);
+ clear_dma_ff(chan);
+ disable_dma(chan);
+ pos=get_dma_residue(chan);
+ enable_dma(chan);
+ RESTORE_INTR (flags);
+ printk("dmapos=%d %x\n",pos,pos);
+ }
+#endif
+ if (sound_dma_automode[dev] &&
+ intrflag &&
+ cnt == dsp_count) {
+ irq_mode = IMODE_INPUT;
+ intr_active = 1;
+ return; /* Auto mode on. No need to react */
+ }
+ DISABLE_INTR (flags);
+
+ if (dma_restart)
+ {
+ sb16_dsp_halt(dev);
+ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+ }
+
+ sb_dsp_command (0x42);
+ sb_dsp_command ((unsigned char)((dsp_current_speed >> 8) & 0xff));
+ sb_dsp_command ((unsigned char)(dsp_current_speed & 0xff));
+ sb_dsp_command ((unsigned char)(dsp_16bit ? 0xbe : 0xce));
+ sb_dsp_command ((unsigned char)((dsp_stereo ? 0x20 : 0) +
+ (dsp_16bit ? 0x10:0)));
+ sb_dsp_command01 ((unsigned char)(cnt&0xff));
+ sb_dsp_command ((unsigned char)(cnt>>8));
+
+ /* sb_dsp_command (0);
+ sb_dsp_command (0); */
+ RESTORE_INTR (flags);
+ dsp_count=cnt;
+ irq_mode = IMODE_INPUT;
+ intr_active = 1;
+}
+
+static int
+sb16_dsp_prepare_for_input (int dev, int bsize, int bcount)
{
+ sound_dsp_dmachan[my_dev] = dsp_16bit? dma16:dma8;
+ dsp_count = 0;
+ dsp_cleanup ();
+ return 0;
}
-long sb16_dsp_init(long mem_start, struct address_info *hw_config)
+static int
+sb16_dsp_prepare_for_output (int dev, int bsize, int bcount)
{
- return mem_start;
+ sound_dsp_dmachan[my_dev] = dsp_16bit? dma16:dma8;
+ dsp_count = 0;
+ dsp_cleanup ();
+ return 0;
}
-int sb16_dsp_detect(struct address_info *hw_config)
+static void
+dsp_cleanup (void)
{
- return 0;
+ irq_mode = IMODE_NONE;
+ intr_active = 0;
+}
+
+static void
+sb16_dsp_reset (int dev)
+{
+ unsigned long flags;
+
+ DISABLE_INTR (flags);
+
+ sb_reset_dsp ();
+ dsp_cleanup ();
+
+ RESTORE_INTR (flags);
+}
+
+static void
+sb16_dsp_halt (int dev)
+{
+ sb_dsp_command01(0xd9);
+ sb_dsp_command01(0xd5);
+}
+
+static void
+set_irq_hw(int level) {
+ int ival;
+ switch(level) {
+ case 5:
+ ival=2; break;
+ case 7:
+ ival=4; break;
+ case 10:
+ ival=8; break;
+ default:
+ printk("SB16_IRQ_LEVEL %d does not exist\n",level);
+ return;
+ }
+ sb_setmixer(IRQ_NR,ival);
+}
+
+long
+sb16_dsp_init (long mem_start, struct address_info *hw_config)
+{
+ int i, major, minor;
+
+ major = minor = 0;
+ sb_dsp_command (0xe1); /* Get version */
+
+ for (i = 1000; i; i--) {
+ if (INB (DSP_DATA_AVAIL) & 0x80)
+ { /* wait for Data Ready */
+ if (major == 0)
+ major = INB (DSP_READ);
+ else
+ {
+ minor = INB (DSP_READ);
+ break;
+ }
+ }
+ }
+
+#ifndef SCO
+ sprintf (sb16_dsp_operations.name, "SoundBlaster 16 %d.%d", major, minor);
+#endif
+
+ printk (" <%s>", sb16_dsp_operations.name);
+
+ if (num_dspdevs < MAX_DSP_DEV)
+ {
+ dsp_devs[my_dev = num_dspdevs++] = &sb16_dsp_operations;
+ sound_dsp_dmachan[my_dev] = hw_config->dma;
+ sound_buffcounts[my_dev] = 1;
+ sound_buffsizes[my_dev] = DSP_BUFFSIZE;
+ sound_dma_automode[my_dev] = 1;
+ }
+ else
+ printk ("SB: Too many DSP devices available\n");
+ sb16_dsp_ok = 1;
+ return mem_start;
+}
+
+int
+sb16_dsp_detect (struct address_info *hw_config)
+{
+ struct address_info *sb_config;
+
+ if (sb16_dsp_ok)
+ return 1; /* Already initialized */
+
+ if (!(sb_config=sound_getconf(SNDCARD_SB)))
+ {
+ printk("SB16 Error: Plain SB not configured\n");
+ return 0;
+ }
+
+ if (sbc_base != hw_config->io_base)
+ printk("Warning! SB16 I/O != SB I/O\n");
+
+ /* sb_setmixer(OPSW,0xf);
+ if(sb_getmixer(OPSW)!=0xf)
+ return 0; */
+
+ if (!sb_reset_dsp ())
+ return 0;
+
+ if (hw_config->irq != sb_config->irq)
+ {
+ printk("SB16 Error: Invalid IRQ number %d/%d\n",
+ sb_config->irq, hw_config->irq);
+ return 0;
+ }
+
+ if (hw_config->dma < 4)
+ if (hw_config->dma != sb_config->dma)
+ {
+ printk("SB16 Error: Invalid DMA channel %d/%d\n",
+ sb_config->dma, hw_config->dma);
+ return 0;
+ }
+
+ dma16 = hw_config->dma;
+ dma8 = sb_config->dma;
+ set_irq_hw(hw_config->irq);
+ sb_setmixer(DMA_NR, (1<<hw_config->dma) | (1<<sb_config->dma));
+
+ DEB(printk ("SoundBlaster 16: IRQ %d DMA %d OK\n",hw_config->irq,hw_config->dma));
+
+/*
+ dsp_showmessage(0xe3,99);
+*/
+ sb16_dsp_ok = 1;
+ return 1;
+}
+
+void
+sb16_dsp_interrupt (int unused)
+{
+ int data;
+ data = INB (DSP_DATA_AVL16); /* Interrupt acknowledge */
+
+ if (intr_active)
+ switch (irq_mode)
+ {
+ case IMODE_OUTPUT:
+ intr_active = 0;
+ DMAbuf_outputintr (my_dev, 1);
+ break;
+
+ case IMODE_INPUT:
+ intr_active = 0;
+ DMAbuf_inputintr (my_dev);
+ break;
+
+ default:
+ printk ("SoundBlaster: Unexpected interrupt\n");
+ }
}
+#endif
diff --git a/drivers/sound/sb16_midi.c b/drivers/sound/sb16_midi.c
new file mode 100644
index 0000000..2fc475a
--- /dev/null
+++ b/drivers/sound/sb16_midi.c
@@ -0,0 +1,314 @@
+/*
+ * sound/sb16_midi.c
+ *
+ * The low level driver for the MPU-401 UART emulation of the SB16.
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+#if !defined(EXCLUDE_SB) && !defined(EXCLUDE_SB16) && !defined(EXCLUDE_MIDI)
+
+#define DATAPORT (sb16midi_base) /* MPU-401 Data I/O Port on IBM */
+#define COMDPORT (sb16midi_base+1) /* MPU-401 Command Port on IBM */
+#define STATPORT (sb16midi_base+1) /* MPU-401 Status Port on IBM */
+
+#define sb16midi_status() INB(STATPORT)
+#define input_avail() (!(sb16midi_status()&INPUT_AVAIL))
+#define output_ready() (!(sb16midi_status()&OUTPUT_READY))
+#define sb16midi_cmd(cmd) OUTB(cmd, COMDPORT)
+#define sb16midi_read() INB(DATAPORT)
+#define sb16midi_write(byte) OUTB(byte, DATAPORT)
+
+#define OUTPUT_READY 0x40 /* Mask for Data Read Redy Bit */
+#define INPUT_AVAIL 0x80 /* Mask for Data Send Ready Bit */
+#define MPU_ACK 0xFE /* MPU-401 Acknowledge Response */
+#define MPU_RESET 0xFF /* MPU-401 Total Reset Command */
+#define UART_MODE_ON 0x3F /* MPU-401 "Dumb UART Mode" */
+
+static int sb16midi_opened = 0;
+static int sb16midi_base = 0x330;
+static int sb16midi_detected = 0;
+static int my_dev;
+
+static int reset_sb16midi (void);
+static void (*midi_input_intr) (int dev, unsigned char data);
+
+static void
+sb16midi_input_loop (void)
+{
+ int count;
+
+ count = 10;
+
+ while (count) /* Not timed out */
+ if (input_avail ())
+ {
+ unsigned char c = sb16midi_read ();
+
+ count = 100;
+
+ if (sb16midi_opened & OPEN_READ)
+ midi_input_intr (my_dev, c);
+ }
+ else
+ while (!input_avail () && count)
+ count--;
+}
+
+void
+sb16midiintr (int unit)
+{
+ if (input_avail ())
+ sb16midi_input_loop ();
+}
+
+/*
+ * It looks like there is no input interrupts in the UART mode. Let's try
+ * polling.
+ */
+
+static void
+poll_sb16midi (unsigned long dummy)
+{
+ unsigned long flags;
+
+ DEFINE_TIMER(sb16midi_timer, poll_sb16midi);
+
+ if (!(sb16midi_opened & OPEN_READ))
+ return; /* No longer required */
+
+ DISABLE_INTR (flags);
+
+ if (input_avail ())
+ sb16midi_input_loop ();
+
+ ACTIVATE_TIMER(sb16midi_timer, poll_sb16midi, 1); /* Come back later */
+
+ RESTORE_INTR (flags);
+}
+
+static int
+sb16midi_open (int dev, int mode,
+ void (*input) (int dev, unsigned char data),
+ void (*output) (int dev)
+)
+{
+ if (sb16midi_opened)
+ {
+ return RET_ERROR (EBUSY);
+ }
+
+ sb16midi_input_loop ();
+
+ midi_input_intr = input;
+ sb16midi_opened = mode;
+ poll_sb16midi (0); /* Enable input polling */
+
+ return 0;
+}
+
+static void
+sb16midi_close (int dev)
+{
+ sb16midi_opened = 0;
+}
+
+static int
+sb16midi_out (int dev, unsigned char midi_byte)
+{
+ int timeout;
+ unsigned long flags;
+
+ /*
+ * Test for input since pending input seems to block the output.
+ */
+
+ DISABLE_INTR (flags);
+
+ if (input_avail ())
+ sb16midi_input_loop ();
+
+ RESTORE_INTR (flags);
+
+ /*
+ * Sometimes it takes about 13000 loops before the output becomes ready
+ * (After reset). Normally it takes just about 10 loops.
+ */
+
+ for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /* Wait */
+
+ if (!output_ready ())
+ {
+ printk ("MPU-401: Timeout\n");
+ return 0;
+ }
+
+ sb16midi_write (midi_byte);
+ return 1;
+}
+
+static int
+sb16midi_command (int dev, unsigned char midi_byte)
+{
+ return 1;
+}
+
+static int
+sb16midi_start_read (int dev)
+{
+ return 0;
+}
+
+static int
+sb16midi_end_read (int dev)
+{
+ return 0;
+}
+
+static int
+sb16midi_ioctl (int dev, unsigned cmd, unsigned arg)
+{
+ return RET_ERROR (EINVAL);
+}
+
+static void
+sb16midi_kick (int dev)
+{
+}
+
+static int
+sb16midi_buffer_status (int dev)
+{
+ return 0; /* No data in buffers */
+}
+
+static struct midi_operations sb16midi_operations =
+{
+ {"SoundBlaster MPU-401", 0, 0, SNDCARD_SB16MIDI},
+ sb16midi_open,
+ sb16midi_close,
+ sb16midi_ioctl,
+ sb16midi_out,
+ sb16midi_start_read,
+ sb16midi_end_read,
+ sb16midi_kick,
+ sb16midi_command,
+ sb16midi_buffer_status
+};
+
+
+long
+attach_sb16midi (long mem_start, struct address_info *hw_config)
+{
+ int ok, timeout;
+ unsigned long flags;
+
+ sb16midi_base = hw_config->io_base;
+
+ if (!sb16midi_detected)
+ return RET_ERROR (EIO);
+
+ DISABLE_INTR (flags);
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */
+ sb16midi_cmd (UART_MODE_ON);
+
+ ok = 0;
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (input_avail ())
+ if (sb16midi_read () == MPU_ACK)
+ ok = 1;
+
+ RESTORE_INTR (flags);
+
+ printk (" <SoundBlaster MPU-401>");
+
+ my_dev = num_midis;
+ midi_devs[num_midis++] = &sb16midi_operations;
+ return mem_start;
+}
+
+static int
+reset_sb16midi (void)
+{
+ unsigned long flags;
+ int ok, timeout, n;
+
+ /*
+ * Send the RESET command. Try again if no success at the first time.
+ */
+
+ ok = 0;
+
+ DISABLE_INTR (flags);
+
+ for (n = 0; n < 2 && !ok; n++)
+ {
+ for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /* Wait */
+ sb16midi_cmd (MPU_RESET); /* Send MPU-401 RESET Command */
+
+ /*
+ * Wait at least 25 msec. This method is not accurate so let's make the
+ * loop bit longer. Cannot sleep since this is called during boot.
+ */
+
+ for (timeout = 50000; timeout > 0 && !ok; timeout--)
+ if (input_avail ())
+ if (sb16midi_read () == MPU_ACK)
+ ok = 1;
+
+ }
+
+ sb16midi_opened = 0;
+ if (ok)
+ sb16midi_input_loop (); /* Flush input before enabling interrupts */
+
+ RESTORE_INTR (flags);
+
+ return ok;
+}
+
+
+int
+probe_sb16midi (struct address_info *hw_config)
+{
+ int ok = 0;
+
+ sb16midi_base = hw_config->io_base;
+
+ if (sb_get_irq () < 0)
+ return 0;
+
+ ok = reset_sb16midi ();
+
+ sb16midi_detected = ok;
+ return ok;
+}
+
+#endif
+
+#endif
diff --git a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c
index e2527c5..ef90b4b 100644
--- a/drivers/sound/sb_card.c
+++ b/drivers/sound/sb_card.c
@@ -1,6 +1,5 @@
-
/*
- * linux/kernel/chr_drv/sound/sb_card.c
+ * sound/sb_card.c
*
* Detection routine for the SoundBlaster cards.
*
diff --git a/drivers/sound/sb_dsp.c b/drivers/sound/sb_dsp.c
index 2bcee0e..e66fe32 100644
--- a/drivers/sound/sb_dsp.c
+++ b/drivers/sound/sb_dsp.c
@@ -1,7 +1,7 @@
/*
* sound/sb_dsp.c
*
- * The low level driver for the SoundBlaster DS chips.
+ * The low level driver for the SoundBlaster DSP chip.
*
* Copyright by Hannu Savolainen 1993
*
@@ -101,7 +101,7 @@ sb_dsp_command (unsigned char val)
}
}
- printk ("SoundBlaster: DSP Command(%02x) Timeout.\n", val);
+ printk ("SoundBlaster: DSP Command(%x) Timeout.\n", val);
printk ("IRQ conflict???\n");
return 0;
}
@@ -114,11 +114,15 @@ sbintr (int unit)
#ifndef EXCLUDE_SBPRO
if (sb16)
{
- unsigned char src = sb_getmixer (IRQ_STAT); /* Interrupt status register */
-
+ unsigned char src = sb_getmixer (IRQ_STAT); /* Interrupt source register */
#ifndef EXCLUDE_SB16
- if (src & 2) sb16_dsp_interrupt(unit);
+ if (src & 3) sb16_dsp_interrupt(unit);
+
+#ifndef EXCLUDE_MIDI
+ if (src & 4) sb16midiintr (unit); /* MPU401 interrupt */
+#endif
+
#endif
if (!(src & 1))
@@ -150,7 +154,7 @@ sbintr (int unit)
case IMODE_MIDI:
printk ("+");
data = INB (DSP_READ);
- printk ("%02x", data);
+ printk ("%x", data);
break;
@@ -332,8 +336,8 @@ sb_dsp_output_block (int dev, unsigned long buf, int count,
DISABLE_INTR (flags);
if (sb_dsp_command (0x48)) /* High speed size */
{
- sb_dsp_command (count & 0xff);
- sb_dsp_command ((count >> 8) & 0xff);
+ sb_dsp_command ((unsigned char)(count & 0xff));
+ sb_dsp_command ((unsigned char)((count >> 8) & 0xff));
sb_dsp_command (0x91); /* High speed 8 bit DAC */
}
else
@@ -345,8 +349,8 @@ sb_dsp_output_block (int dev, unsigned long buf, int count,
DISABLE_INTR (flags);
if (sb_dsp_command (0x14)) /* 8-bit DAC (DMA) */
{
- sb_dsp_command (count & 0xff);
- sb_dsp_command ((count >> 8) & 0xff);
+ sb_dsp_command ((unsigned char)(count & 0xff));
+ sb_dsp_command ((unsigned char)((count >> 8) & 0xff));
}
else
printk ("SB Error: Unable to start DAC\n");
@@ -378,8 +382,8 @@ sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag,
DISABLE_INTR (flags);
if (sb_dsp_command (0x48)) /* High speed size */
{
- sb_dsp_command (count & 0xff);
- sb_dsp_command ((count >> 8) & 0xff);
+ sb_dsp_command ((unsigned char)(count & 0xff));
+ sb_dsp_command ((unsigned char)((count >> 8) & 0xff));
sb_dsp_command (0x99); /* High speed 8 bit ADC */
}
else
@@ -391,8 +395,8 @@ sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag,
DISABLE_INTR (flags);
if (sb_dsp_command (0x24)) /* 8-bit ADC (DMA) */
{
- sb_dsp_command (count & 0xff);
- sb_dsp_command ((count >> 8) & 0xff);
+ sb_dsp_command ((unsigned char)(count & 0xff));
+ sb_dsp_command ((unsigned char)((count >> 8) & 0xff));
}
else
printk ("SB Error: Unable to start ADC\n");
@@ -557,6 +561,8 @@ sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
break;
case SOUND_PCM_WRITE_CHANNELS:
+ if (local)
+ return dsp_set_stereo (arg - 1) + 1;
return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1);
break;
@@ -699,6 +705,9 @@ sb_dsp_init (long mem_start, struct address_info *hw_config)
printk (" <%s>", sb_dsp_operations.name);
#ifndef EXCLUDE_AUDIO
+# if !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO)
+ if (!sb16) /* There is a better driver for SB16 */
+# endif
if (num_dspdevs < MAX_DSP_DEV)
{
dsp_devs[my_dev = num_dspdevs++] = &sb_dsp_operations;
@@ -712,8 +721,8 @@ sb_dsp_init (long mem_start, struct address_info *hw_config)
#endif
#ifndef EXCLUDE_MIDI
- if (!midi_disabled) /* Midi don't work in the SB emulation mode
- * of PAS */
+ if (!midi_disabled && !sb16) /* Midi don't work in the SB emulation mode
+ * of PAS, SB16 has better midi interface */
sb_midi_init(major);
#endif
diff --git a/drivers/sound/sb_mixer.c b/drivers/sound/sb_mixer.c
index 940f03e..afd49c5 100644
--- a/drivers/sound/sb_mixer.c
+++ b/drivers/sound/sb_mixer.c
@@ -49,26 +49,26 @@ static int mixer_caps;
static mixer_tab *iomap;
void
-sb_setmixer (unsigned char port, unsigned char value)
+sb_setmixer (unsigned int port, unsigned int value)
{
unsigned long flags;
DISABLE_INTR(flags);
- OUTB (port, MIXER_ADDR); /* Select register */
+ OUTB ((unsigned char)(port & 0xff), MIXER_ADDR); /* Select register */
tenmicrosec ();
- OUTB (value, MIXER_DATA);
+ OUTB ((unsigned char)(value & 0xff), MIXER_DATA);
tenmicrosec ();
RESTORE_INTR(flags);
}
int
-sb_getmixer (unsigned char port)
+sb_getmixer (unsigned int port)
{
int val;
unsigned long flags;
DISABLE_INTR(flags);
- OUTB (port, MIXER_ADDR); /* Select register */
+ OUTB ((unsigned char)(port & 0xff), MIXER_ADDR); /* Select register */
tenmicrosec ();
val = INB (MIXER_DATA);
tenmicrosec ();
@@ -184,7 +184,7 @@ static int
set_recmask(int mask)
{
int devmask, i;
- unsigned char regimage;
+ unsigned char regimageL, regimageR;
devmask = mask & supported_rec_devices;
@@ -236,12 +236,15 @@ set_recmask(int mask)
case 4:
if (!devmask) devmask = SOUND_MASK_MIC;
- regimage = 0;
+ regimageL = regimageR = 0;
for (i=0;i<SOUND_MIXER_NRDEVICES;i++)
if ((1<<i) & devmask)
- regimage |= sb16_recmasks[i];
- sb_setmixer(SB16_IMASK_L, regimage);
- sb_setmixer(SB16_IMASK_R, regimage);
+ {
+ regimageL |= sb16_recmasks_L[i];
+ regimageR |= sb16_recmasks_R[i];
+ }
+ sb_setmixer(SB16_IMASK_L, regimageL);
+ sb_setmixer(SB16_IMASK_R, regimageR);
break;
}
@@ -277,7 +280,8 @@ sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
break;
case SOUND_MIXER_STEREODEVS:
- return IOCTL_OUT (arg, supported_devices & ~SOUND_MASK_MIC);
+ return IOCTL_OUT (arg, supported_devices &
+ ~(SOUND_MASK_MIC|SOUND_MASK_SPEAKER));
break;
case SOUND_MIXER_RECMASK:
diff --git a/drivers/sound/sb_mixer.h b/drivers/sound/sb_mixer.h
index 5729455..304af64 100644
--- a/drivers/sound/sb_mixer.h
+++ b/drivers/sound/sb_mixer.h
@@ -31,7 +31,7 @@
#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD | SOUND_MASK_VOLUME)
-#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
+#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
SOUND_MASK_CD)
#define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
@@ -80,7 +80,6 @@
#define RIGHT_CHN 1
struct mixer_def {
- char name[20];
unsigned int regno: 8;
unsigned int bitoffs:4;
unsigned int nbits:4;
@@ -91,7 +90,7 @@ typedef struct mixer_def mixer_tab[32][2];
typedef struct mixer_def mixer_ent;
#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \
- {{#name,reg_l, bit_l, len_l}, {" ", reg_r, bit_r, len_r}}
+ {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}
#ifdef __SB_MIXER_C__
mixer_tab sbpro_mix = {
@@ -126,30 +125,46 @@ MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2)
static unsigned short levels[SOUND_MIXER_NRDEVICES] =
{
- 0x4b4b, /* Master Volume */
+ 0x5a5a, /* Master Volume */
0x3232, /* Bass */
0x3232, /* Treble */
0x4b4b, /* FM */
0x4b4b, /* PCM */
0x4b4b, /* PC Speaker */
0x4b4b, /* Ext Line */
- 0x3232, /* Mic */
+ 0x0000, /* Mic */
0x4b4b, /* CD */
0x4b4b, /* Recording monitor */
0x4b4b, /* SB PCM */
0x4b4b}; /* Recording level */
-static unsigned char sb16_recmasks[SOUND_MIXER_NRDEVICES] =
+static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
{
0x00, /* SOUND_MIXER_VOLUME */
0x00, /* SOUND_MIXER_BASS */
0x00, /* SOUND_MIXER_TREBLE */
- 0x60, /* SOUND_MIXER_SYNTH */
+ 0x40, /* SOUND_MIXER_SYNTH */
0x00, /* SOUND_MIXER_PCM */
0x00, /* SOUND_MIXER_SPEAKER */
- 0x18, /* SOUND_MIXER_LINE */
+ 0x10, /* SOUND_MIXER_LINE */
0x01, /* SOUND_MIXER_MIC */
- 0x06, /* SOUND_MIXER_CD */
+ 0x04, /* SOUND_MIXER_CD */
+ 0x00, /* SOUND_MIXER_IMIX */
+ 0x00, /* SOUND_MIXER_ALTPCM */
+ 0x00 /* SOUND_MIXER_RECLEV */
+};
+
+static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
+{
+ 0x00, /* SOUND_MIXER_VOLUME */
+ 0x00, /* SOUND_MIXER_BASS */
+ 0x00, /* SOUND_MIXER_TREBLE */
+ 0x20, /* SOUND_MIXER_SYNTH */
+ 0x00, /* SOUND_MIXER_PCM */
+ 0x00, /* SOUND_MIXER_SPEAKER */
+ 0x08, /* SOUND_MIXER_LINE */
+ 0x01, /* SOUND_MIXER_MIC */
+ 0x02, /* SOUND_MIXER_CD */
0x00, /* SOUND_MIXER_IMIX */
0x00, /* SOUND_MIXER_ALTPCM */
0x00 /* SOUND_MIXER_RECLEV */
diff --git a/drivers/sound/sequencer.c b/drivers/sound/sequencer.c
index 4901ea9..9024922 100644
--- a/drivers/sound/sequencer.c
+++ b/drivers/sound/sequencer.c
@@ -1,5 +1,5 @@
/*
- * linux/kernel/chr_drv/sound/sequencer.c
+ * sound/sequencer.c
*
* The sequencer personality manager.
*
@@ -51,8 +51,10 @@ long seq_time = 0; /* Reference point for the timer */
#include "tuning.h"
#define EV_SZ 8
-static unsigned char queue[SEQ_MAX_QUEUE][EV_SZ];
-static unsigned char iqueue[SEQ_MAX_QUEUE][4];
+#define IEV_SZ 4
+static unsigned char *queue = NULL; /* SEQ_MAX_QUEUE * EV_SZ bytes */
+static unsigned char *iqueue = NULL; /* SEQ_MAX_QUEUE * IEV_SZ bytes */
+
static volatile int qhead = 0, qtail = 0, qlen = 0;
static volatile int iqhead = 0, iqtail = 0, iqlen = 0;
static volatile int seq_playing = 0;
@@ -91,7 +93,7 @@ sequencer_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
return count - c;
}
- COPY_TO_USER (buf, p, &iqueue[iqhead][0], 4);
+ COPY_TO_USER (buf, p, &iqueue[iqhead * IEV_SZ], IEV_SZ);
p += 4;
c -= 4;
@@ -116,12 +118,12 @@ copy_to_input (unsigned char *event)
if (iqlen >= (SEQ_MAX_QUEUE - 1))
return; /* Overflow */
- memcpy (iqueue[iqtail], event, 4);
+ memcpy (&iqueue[iqtail * IEV_SZ], event, IEV_SZ);
iqlen++;
iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
DISABLE_INTR (flags);
- if (SOMEONE_WAITING (midi_sleep_flag))
+ if (SOMEONE_WAITING (midi_sleeper, midi_sleep_flag))
{
WAKE_UP (midi_sleeper, midi_sleep_flag);
}
@@ -268,7 +270,7 @@ seq_queue (unsigned char *note)
if (!seq_playing)
seq_startplay (); /* Give chance to drain the queue */
- if (qlen >= SEQ_MAX_QUEUE && !SOMEONE_WAITING (seq_sleep_flag))
+ if (qlen >= SEQ_MAX_QUEUE && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
{
/* Sleep until there is enough space on the queue */
DO_SLEEP (seq_sleeper, seq_sleep_flag, 0);
@@ -277,7 +279,7 @@ seq_queue (unsigned char *note)
if (qlen >= SEQ_MAX_QUEUE)
return 0; /* To be sure */
- memcpy (&queue[qtail][0], note, EV_SZ);
+ memcpy (&queue[qtail * EV_SZ], note, EV_SZ);
qtail = (qtail + 1) % SEQ_MAX_QUEUE;
qlen++;
@@ -344,7 +346,7 @@ seq_startplay (void)
qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
qlen--;
- q = &queue[this_one][0];
+ q = &queue[this_one*EV_SZ];
switch (q[0])
{
@@ -380,7 +382,7 @@ seq_startplay (void)
unsigned long flags;
DISABLE_INTR (flags);
- if (SOMEONE_WAITING (seq_sleep_flag))
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
{
WAKE_UP (seq_sleeper, seq_sleep_flag);
}
@@ -450,7 +452,7 @@ seq_startplay (void)
unsigned long flags;
DISABLE_INTR (flags);
- if (SOMEONE_WAITING (seq_sleep_flag))
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
{
WAKE_UP (seq_sleeper, seq_sleep_flag);
}
@@ -630,7 +632,7 @@ seq_sync (void)
if (qlen && !seq_playing && !PROCESS_ABORTING (seq_sleeper, seq_sleep_flag))
seq_startplay ();
- if (qlen && !SOMEONE_WAITING (seq_sleep_flag)) /* Queue not empty */
+ if (qlen && !SOMEONE_WAITING (seq_sleeper, seq_sleep_flag)) /* Queue not empty */
{
DO_SLEEP (seq_sleeper, seq_sleep_flag, 0);
}
@@ -684,7 +686,8 @@ seq_reset (void)
{
for (chn = 0; chn < 16; chn++)
{
- midi_outc (i, 0xb0 + chn); /* Channel message */
+ midi_outc (i,
+ (unsigned char)(0xb0 + (chn & 0xff))); /* Channel msg */
midi_outc (i, 0x7b);/* All notes off */
midi_outc (i, 0); /* Dummy parameter */
}
@@ -697,7 +700,7 @@ seq_reset (void)
seq_playing = 0;
- if (SOMEONE_WAITING (seq_sleep_flag))
+ if (SOMEONE_WAITING (seq_sleeper, seq_sleep_flag))
printk ("Sequencer Warning: Unexpected sleeping process\n");
}
@@ -1090,6 +1093,8 @@ sequencer_init (long mem_start)
{
sequencer_ok = 1;
+ PERMANENT_MALLOC(unsigned char*, queue, SEQ_MAX_QUEUE*EV_SZ, mem_start);
+ PERMANENT_MALLOC(unsigned char*, iqueue, SEQ_MAX_QUEUE*IEV_SZ, mem_start);
return mem_start;
}
diff --git a/drivers/sound/sound_calls.h b/drivers/sound/sound_calls.h
index d621d50..97fb769 100644
--- a/drivers/sound/sound_calls.h
+++ b/drivers/sound/sound_calls.h
@@ -97,6 +97,16 @@ void sound_stop_timer(void);
int snd_ioctl_return(int *addr, int value);
int snd_set_irq_handler (int interrupt_level, void(*hndlr)(int));
void snd_release_irq(int vect);
+void sound_dma_malloc(int dev);
+void sound_dma_free(int dev);
+
+/* From sound_switch.c */
+int sound_read_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count);
+int sound_open_sw (int dev, struct fileinfo *file);
+void sound_release_sw (int dev, struct fileinfo *file);
+int sound_ioctl_sw (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned long arg);
/* From sb_dsp.c */
int sb_dsp_detect (struct address_info *hw_config);
@@ -112,12 +122,17 @@ void sb16_dsp_interrupt (int unused);
long sb16_dsp_init(long mem_start, struct address_info *hw_config);
int sb16_dsp_detect(struct address_info *hw_config);
+/* From sb16_midi.c */
+void sb16midiintr (int unit);
+long attach_sb16midi(long mem_start, struct address_info * hw_config);
+int probe_sb16midi(struct address_info *hw_config);
+
/* From sb_midi.c */
void sb_midi_init(int model);
/* From sb_mixer.c */
-void sb_setmixer (unsigned char port, unsigned char value);
-int sb_getmixer (unsigned char port);
+void sb_setmixer (unsigned int port, unsigned int value);
+int sb_getmixer (unsigned int port);
void sb_mixer_set_stereo(int mode);
void sb_mixer_init(int major_model);
@@ -163,7 +178,7 @@ int gus_wave_detect(int baseaddr);
long gus_wave_init(long mem_start, int irq, int dma);
void gus_voice_irq(void);
unsigned char gus_read8 (int reg);
-void gus_write8(int reg, unsigned char data);
+void gus_write8(int reg, unsigned int data);
void guswave_dma_irq(void);
void gus_delay(void);
diff --git a/drivers/sound/sound_config.h b/drivers/sound/sound_config.h
index ef86edc..a088ff0 100644
--- a/drivers/sound/sound_config.h
+++ b/drivers/sound/sound_config.h
@@ -85,6 +85,10 @@ If your card has nonstandard I/O address or IRQ number, change defines
#define SB16_DMA 6
#endif
+#ifndef SB16MIDI_BASE
+#define SB16MIDI_BASE 0x300
+#endif
+
#ifndef PAS_BASE
#define PAS_BASE 0x388
#endif
diff --git a/drivers/sound/sound_switch.c b/drivers/sound/sound_switch.c
new file mode 100644
index 0000000..cf36807
--- /dev/null
+++ b/drivers/sound/sound_switch.c
@@ -0,0 +1,417 @@
+/*
+ * sound/sound_switch.c
+ *
+ * The system call switch
+ *
+ * Copyright by Hannu Savolainen 1993
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sound_config.h"
+
+#ifdef CONFIGURE_SOUNDCARD
+
+struct sbc_device
+{
+ int usecount;
+};
+
+static struct sbc_device sbc_devices[SND_NDEVS] = {{0}};
+
+static int in_use = 0; /* Total # of open device files (excluding
+ * minor 0) */
+
+/*
+ * /dev/sndstatus -device
+ */
+static char *status_buf = NULL;
+static int status_len, status_ptr;
+static int status_busy = 0;
+
+static int
+put_status (char *s)
+{
+ int l;
+
+ for (l=0;l<256,s[l];l++); /* l=strlen(s); */
+
+ if (status_len + l >= 4000)
+ return 0;
+
+ memcpy (&status_buf[status_len], s, l);
+ status_len += l;
+
+ return 1;
+}
+
+static int
+put_status_int (unsigned int val, int radix)
+{
+ int l, v;
+
+ static char hx[] = "0123456789abcdef";
+ char buf[11];
+
+ if (!val) return put_status("0");
+
+ l = 0;
+ buf[10]=0;
+
+ while (val)
+ {
+ v = val % radix;
+ val = val / radix;
+
+ buf[9-l] = hx[v];
+ l++;
+ }
+
+ if (status_len + l >= 4000)
+ return 0;
+
+ memcpy (&status_buf[status_len], &buf[10-l], l);
+ status_len += l;
+
+ return 1;
+}
+
+static void
+init_status (void)
+{
+ /*
+ * Write the status information to the status_buf and update status_len.
+ * There is a limit of 4000 bytes for the data.
+ */
+
+ int i;
+
+ status_ptr = 0;
+
+ put_status ("Sound Driver:" SOUND_VERSION_STRING
+ " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@"
+ SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")"
+ "\n");
+
+ if (!put_status ("Config options: "))
+ return;
+ if (!put_status_int(SELECTED_SOUND_OPTIONS, 16))
+ return;
+
+ if (!put_status ("\n\nHW config: \n"))
+ return;
+
+ for (i = 0; i < (num_sound_drivers - 1); i++)
+ {
+ if (!supported_drivers[i].enabled)
+ if (!put_status ("("))
+ return;
+
+ if (!put_status ("Type ")) return;
+ if (!put_status_int(supported_drivers[i].card_type, 10)) return;
+ if (!put_status (": ")) return;
+ if (!put_status (supported_drivers[i].name)) return;
+ if (!put_status (" at 0x")) return;
+ if (!put_status_int(supported_drivers[i].config.io_base, 16)) return;
+ if (!put_status (" irq ")) return;
+ if (!put_status_int(supported_drivers[i].config.irq, 10)) return;
+ if (!put_status (" drq ")) return;
+ if (!put_status_int(supported_drivers[i].config.dma, 10)) return;
+
+ if (!supported_drivers[i].enabled)
+ if (!put_status (")"))
+ return;
+
+ if (!put_status ("\n"))
+ return;
+ }
+
+ if (!put_status ("\nPCM devices:\n"))
+ return;
+
+ for (i = 0; i < num_dspdevs; i++)
+ {
+ if (!put_status_int(i, 10)) return;
+ if (!put_status(": "))return;
+ if (!put_status(dsp_devs[i]->name))return;
+ if (!put_status("\n"))return;
+ }
+
+ if (!put_status ("\nSynth devices:\n"))
+ return;
+
+ for (i = 0; i < num_synths; i++)
+ {
+ if (!put_status_int(i, 10)) return;
+ if (!put_status(": "))return;
+ if (!put_status(synth_devs[i]->info->name))return;
+ if (!put_status("\n"))return;
+ }
+
+ if (!put_status ("\nMidi devices:\n"))
+ return;
+
+ for (i = 0; i < num_midis; i++)
+ {
+ if (!put_status_int(i, 10)) return;
+ if (!put_status(": "))return;
+ if (!put_status(midi_devs[i]->info.name))return;
+ if (!put_status("\n"))return;
+ }
+
+ if (num_mixers)
+ {
+ if (!put_status ("\nMixer(s) installed\n"))
+ return;
+ }
+ else
+ {
+ if (!put_status ("\nNo mixers installed\n"))
+ return;
+ }
+}
+
+static int
+read_status (snd_rw_buf *buf, int count)
+{
+ /*
+ * Return at most 'count' bytes from the status_buf.
+ */
+ int l, c;
+
+ l = count;
+ c = status_len - status_ptr;
+
+ if (l > c)
+ l = c;
+ if (l <= 0)
+ return 0;
+
+ COPY_TO_USER(buf, 0, &status_buf[status_ptr], l);
+ status_ptr += l;
+
+ return l;
+}
+
+int
+sound_read_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count)
+{
+ DEB (printk ("sound_read_sw(dev=%d, count=%d)\n", dev, count));
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ return read_status (buf, count);
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_read (dev, file, buf, count);
+ break;
+
+ case SND_DEV_SEQ:
+ return sequencer_read (dev, file, buf, count);
+ break;
+
+#ifndef EXCLUDE_MPU401
+ case SND_DEV_MIDIN:
+ return MIDIbuf_read (dev, file, buf, count);
+#endif
+
+ default:
+ printk ("Sound: Undefined minor device %d\n", dev);
+ }
+
+ return RET_ERROR (EPERM);
+}
+
+int
+sound_write_sw (int dev, struct fileinfo *file, snd_rw_buf *buf, int count)
+{
+
+ DEB (printk ("sound_write_sw(dev=%d, count=%d)\n", dev, count));
+
+ switch (dev & 0x0f)
+ {
+
+ case SND_DEV_SEQ:
+ return sequencer_write (dev, file, buf, count);
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_write (dev, file, buf, count);
+ break;
+
+ default:
+ return RET_ERROR (EPERM);
+ }
+
+ return count;
+}
+
+int
+sound_open_sw (int dev, struct fileinfo *file)
+{
+ int retval;
+ DEB (printk ("sound_open_sw(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount));
+
+ if ((dev >= SND_NDEVS) || (dev < 0))
+ {
+ printk ("Invalid minor device %d\n", dev);
+ return RET_ERROR (ENODEV);
+ }
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ if (status_busy)
+ return RET_ERROR (EBUSY);
+ status_busy = 1;
+ if ((status_buf = (char *) KERNEL_MALLOC (4000)) == NULL)
+ return RET_ERROR (EIO);
+ status_len = status_ptr = 0;
+ init_status ();
+ break;
+
+ case SND_DEV_CTL:
+ return 0;
+ break;
+
+ case SND_DEV_SEQ:
+ if ((retval = sequencer_open (dev, file)) < 0)
+ return retval;
+ break;
+
+#ifndef EXCLUDE_MPU401
+ case SND_DEV_MIDIN:
+ if ((retval = MIDIbuf_open (dev, file)) < 0)
+ return retval;
+ break;
+#endif
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ if ((retval = audio_open (dev, file)) < 0)
+ return retval;
+ break;
+
+ default:
+ printk ("Invalid minor device %d\n", dev);
+ return RET_ERROR (ENODEV);
+ }
+
+ sbc_devices[dev].usecount++;
+ in_use++;
+
+ return 0;
+}
+
+void
+sound_release_sw (int dev, struct fileinfo *file)
+{
+
+ DEB (printk ("sound_release_sw(dev=%d)\n", dev));
+
+ switch (dev & 0x0f)
+ {
+ case SND_DEV_STATUS:
+ if (status_buf)
+ KERNEL_FREE (status_buf);
+ status_buf = NULL;
+ status_busy = 0;
+ break;
+
+ case SND_DEV_CTL:
+ break;
+
+ case SND_DEV_SEQ:
+ sequencer_release (dev, file);
+ break;
+
+#ifndef EXCLUDE_MPU401
+ case SND_DEV_MIDIN:
+ MIDIbuf_release (dev, file);
+ break;
+#endif
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ audio_release (dev, file);
+ break;
+
+ default:
+ printk ("Sound error: Releasing unknown device 0x%02x\n", dev);
+ }
+
+ sbc_devices[dev].usecount--;
+ in_use--;
+}
+
+int
+sound_ioctl_sw (int dev, struct fileinfo *file,
+ unsigned int cmd, unsigned long arg)
+{
+ DEB (printk ("sound_ioctl_sw(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
+
+ switch (dev & 0x0f)
+ {
+
+ case SND_DEV_CTL:
+
+ if (!num_mixers)
+ return RET_ERROR (ENODEV);
+
+ if (dev >= num_mixers)
+ return RET_ERROR (ENODEV);
+
+ return mixer_devs[dev]->ioctl (dev, cmd, arg);
+ break;
+
+ case SND_DEV_SEQ:
+ return sequencer_ioctl (dev, file, cmd, arg);
+ break;
+
+ case SND_DEV_DSP:
+ case SND_DEV_DSP16:
+ case SND_DEV_AUDIO:
+ return audio_ioctl (dev, file, cmd, arg);
+ break;
+
+#ifndef EXCLUDE_MPU401
+ case SND_DEV_MIDIN:
+ return MIDIbuf_ioctl (dev, file, cmd, arg);
+ break;
+#endif
+
+ default:
+ return RET_ERROR (EPERM);
+ break;
+ }
+
+ return RET_ERROR (EPERM);
+}
+#endif
diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c
index 05c09da..a41bd9d 100644
--- a/drivers/sound/soundcard.c
+++ b/drivers/sound/soundcard.c
@@ -34,16 +34,8 @@
#include <linux/major.h>
-struct sbc_device
-{
- int usecount;
-};
-
-static struct sbc_device sbc_devices[SND_NDEVS];
extern long seq_time;
-static int in_use = 0; /* Total # of open device files (excluding
- * minor 0) */
static int soundcards_installed = 0; /* Number of installed
* soundcards */
static int soundcard_configured = 0;
@@ -54,147 +46,6 @@ extern char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT];
extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT];
extern int snd_raw_count[MAX_DSP_DEV];
-/*
- * /dev/sndstatus -device
- */
-static char *status_buf = NULL;
-static int status_len, status_ptr;
-static int status_busy = 0;
-
-static int
-put_status (char *s)
-{
- int l = strlen (s);
-
- if (status_len + l >= 4000)
- return 0;
-
- memcpy (&status_buf[status_len], s, l);
- status_len += l;
-
- return 1;
-}
-
-static void
-init_status (void)
-{
- /*
- * Write the status information to the status_buf and update status_len.
- * There is a limit of 4000 bytes for the data.
- */
-
- char tmp_buf[256]; /* Line buffer */
- int i;
-
- status_ptr = 0;
-
- put_status ("Sound Driver:" SOUND_VERSION_STRING
- " (" SOUND_CONFIG_DATE " " SOUND_CONFIG_BY "@"
- SOUND_CONFIG_HOST "." SOUND_CONFIG_DOMAIN ")"
- "\n");
-
- sprintf (tmp_buf, "Config options: 0x%08x\n\n", SELECTED_SOUND_OPTIONS);
- if (!put_status (tmp_buf))
- return;
-
- sprintf (tmp_buf, "Major number: %d\n", SOUND_MAJOR);
- if (!put_status (tmp_buf))
- return;
-
- if (!put_status ("HW config: \n"))
- return;
-
- for (i = 0; i < (num_sound_drivers - 1); i++)
- {
- if (!supported_drivers[i].enabled)
- if (!put_status ("("))
- return;
-
- sprintf (tmp_buf, "Type %02x: %s",
- supported_drivers[i].card_type,
- supported_drivers[i].name);
- if (!put_status (tmp_buf))
- return;
-
- sprintf (tmp_buf, " at 0x%03x irq %d drq %d",
- supported_drivers[i].config.io_base,
- supported_drivers[i].config.irq,
- supported_drivers[i].config.dma);
- if (!put_status (tmp_buf))
- return;
-
- if (!supported_drivers[i].enabled)
- if (!put_status (")"))
- return;
-
- if (!put_status ("\n"))
- return;
- }
-
- if (!put_status ("\nPCM devices:\n"))
- return;
-
- for (i = 0; i < num_dspdevs; i++)
- {
- sprintf (tmp_buf, "%02d: %s\n", i, dsp_devs[i]->name);
- if (!put_status (tmp_buf))
- return;
- }
-
- if (!put_status ("\nSynth devices:\n"))
- return;
-
- for (i = 0; i < num_synths; i++)
- {
- sprintf (tmp_buf, "%02d: %s\n", i, synth_devs[i]->info->name);
- if (!put_status (tmp_buf))
- return;
- }
-
- if (!put_status ("\nMidi devices:\n"))
- return;
-
- for (i = 0; i < num_midis; i++)
- {
- sprintf (tmp_buf, "%02d: %s\n", i, midi_devs[i]->info.name);
- if (!put_status (tmp_buf))
- return;
- }
-
- if (num_mixers)
- {
- if (!put_status ("\nMixer(s) installed\n"))
- return;
- }
- else
- {
- if (!put_status ("\nNo mixers installed\n"))
- return;
- }
-}
-
-static int
-read_status (char *buf, int count)
-{
- /*
- * Return at most 'count' bytes from the status_buf.
- */
- int l, c;
-
- l = count;
- c = status_len - status_ptr;
-
- if (l > c)
- l = c;
- if (l <= 0)
- return 0;
-
- memcpy_tofs (buf, &status_buf[status_ptr], l);
- status_ptr += l;
-
- return l;
-}
-
int
snd_ioctl_return (int *addr, int value)
{
@@ -213,34 +64,7 @@ sound_read (struct inode *inode, struct file *file, char *buf, int count)
dev = inode->i_rdev;
dev = MINOR (dev);
- DEB (printk ("sound_read(dev=%d, count=%d)\n", dev, count));
-
- switch (dev & 0x0f)
- {
- case SND_DEV_STATUS:
- return read_status (buf, count);
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- return audio_read (dev, &files[dev], buf, count);
- break;
-
- case SND_DEV_SEQ:
- return sequencer_read (dev, &files[dev], buf, count);
- break;
-
-#ifndef EXCLUDE_MPU401
- case SND_DEV_MIDIN:
- return MIDIbuf_read (dev, &files[dev], buf, count);
-#endif
-
- default:
- printk ("Sound: Undefined minor device %d\n", dev);
- }
-
- return RET_ERROR (EPERM);
+ return sound_read_sw (dev, &files[dev], buf, count);
}
static int
@@ -251,26 +75,7 @@ sound_write (struct inode *inode, struct file *file, char *buf, int count)
dev = inode->i_rdev;
dev = MINOR (dev);
- DEB (printk ("sound_write(dev=%d, count=%d)\n", dev, count));
-
- switch (dev & 0x0f)
- {
-
- case SND_DEV_SEQ:
- return sequencer_write (dev, &files[dev], buf, count);
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- return audio_write (dev, &files[dev], buf, count);
- break;
-
- default:
- return RET_ERROR (EPERM);
- }
-
- return count;
+ return sound_write_sw (dev, &files[dev], buf, count);
}
static int
@@ -282,19 +87,11 @@ sound_lseek (struct inode *inode, struct file *file, off_t offset, int orig)
static int
sound_open (struct inode *inode, struct file *file)
{
- int dev, retval;
+ int dev;
dev = inode->i_rdev;
dev = MINOR (dev);
- DEB (printk ("sound_open(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount));
-
- if ((dev >= SND_NDEVS) || (dev < 0))
- {
- printk ("Invalid minor device %d\n", dev);
- return RET_ERROR (ENODEV);
- }
-
if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS)
{
printk ("SoundCard Error: The soundcard system has not been configured\n");
@@ -310,55 +107,7 @@ sound_open (struct inode *inode, struct file *file)
if ((file->f_flags & O_ACCMODE) == O_WRONLY)
files[dev].mode = OPEN_WRITE;
- switch (dev & 0x0f)
- {
- case SND_DEV_STATUS:
- if (status_busy)
- return RET_ERROR (EBUSY);
- status_busy = 1;
- if ((status_buf = (char *) KERNEL_MALLOC (4000)) == NULL)
- return RET_ERROR (EIO);
- status_len = status_ptr = 0;
- init_status ();
- break;
-
- case SND_DEV_CTL:
- if (!soundcards_installed)
- if (soundcard_configured)
- {
- printk ("Soundcard not installed\n");
- return RET_ERROR (ENODEV);
- }
- break;
-
- case SND_DEV_SEQ:
- if ((retval = sequencer_open (dev, &files[dev])) < 0)
- return retval;
- break;
-
-#ifndef EXCLUDE_MPU401
- case SND_DEV_MIDIN:
- if ((retval = MIDIbuf_open (dev, &files[dev])) < 0)
- return retval;
- break;
-#endif
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- if ((retval = audio_open (dev, &files[dev])) < 0)
- return retval;
- break;
-
- default:
- printk ("Invalid minor device %d\n", dev);
- return RET_ERROR (ENODEV);
- }
-
- sbc_devices[dev].usecount++;
- in_use++;
-
- return 0;
+ return sound_open_sw (dev, &files[dev]);
}
static void
@@ -369,42 +118,7 @@ sound_release (struct inode *inode, struct file *file)
dev = inode->i_rdev;
dev = MINOR (dev);
- DEB (printk ("sound_release(dev=%d)\n", dev));
-
- switch (dev & 0x0f)
- {
- case SND_DEV_STATUS:
- if (status_buf)
- KERNEL_FREE (status_buf);
- status_buf = NULL;
- status_busy = 0;
- break;
-
- case SND_DEV_CTL:
- break;
-
- case SND_DEV_SEQ:
- sequencer_release (dev, &files[dev]);
- break;
-
-#ifndef EXCLUDE_MPU401
- case SND_DEV_MIDIN:
- MIDIbuf_release (dev, &files[dev]);
- break;
-#endif
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- audio_release (dev, &files[dev]);
- break;
-
- default:
- printk ("Sound error: Releasing unknown device 0x%02x\n", dev);
- }
-
- sbc_devices[dev].usecount--;
- in_use--;
+ sound_release_sw (dev, &files[dev]);
}
static int
@@ -416,44 +130,7 @@ sound_ioctl (struct inode *inode, struct file *file,
dev = inode->i_rdev;
dev = MINOR (dev);
- DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
-
- switch (dev & 0x0f)
- {
-
- case SND_DEV_CTL:
-
- if (!num_mixers)
- return RET_ERROR (ENODEV);
-
- if (dev >= num_mixers)
- return RET_ERROR (ENODEV);
-
- return mixer_devs[dev]->ioctl (dev, cmd, arg);
- break;
-
- case SND_DEV_SEQ:
- return sequencer_ioctl (dev, &files[dev], cmd, arg);
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- return audio_ioctl (dev, &files[dev], cmd, arg);
- break;
-
-#ifndef EXCLUDE_MPU401
- case SND_DEV_MIDIN:
- return MIDIbuf_ioctl (dev, &files[dev], cmd, arg);
- break;
-#endif
-
- default:
- return RET_ERROR (EPERM);
- break;
- }
-
- return RET_ERROR (EPERM);
+ return sound_ioctl_sw (dev, &files[dev], cmd, arg);
}
static int
@@ -495,8 +172,6 @@ static struct file_operations sound_fops =
long
soundcard_init (long mem_start)
{
- int i;
-
register_chrdev (SOUND_MAJOR, "sound", &sound_fops);
soundcard_configured = 1;
@@ -521,11 +196,6 @@ soundcard_init (long mem_start)
if (num_midis + num_synths)
mem_start = sequencer_init (mem_start);
- for (i = 0; i < SND_NDEVS; i++)
- {
- sbc_devices[i].usecount = 0;
- }
-
return mem_start;
}
diff --git a/fs/Makefile b/fs/Makefile
index 146d7bf..d008403 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -7,7 +7,7 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-SUBDIRS = minix ext ext2 msdos proc isofs nfs xiafs hpfs
+SUBDIRS = minix ext ext2 msdos proc isofs nfs xiafs hpfs sysv
ifdef CONFIG_MINIX_FS
FS_SUBDIRS := $(FS_SUBDIRS) minix
@@ -33,6 +33,9 @@ endif
ifdef CONFIG_XIA_FS
FS_SUBDIRS := $(FS_SUBDIRS) xiafs
endif
+ifdef CONFIG_SYSV_FS
+FS_SUBDIRS := $(FS_SUBDIRS) sysv
+endif
ifdef CONFIG_HPFS_FS
FS_SUBDIRS := $(FS_SUBDIRS) hpfs
endif
@@ -40,6 +43,9 @@ endif
ifdef CONFIG_BINFMT_ELF
BINFMTS := $(BINFMTS) binfmt_elf.o
endif
+ifdef CONFIG_BINFMT_COFF
+BINFMTS := $(BINFMTS) binfmt_coff.o
+endif
.c.s:
$(CC) $(CFLAGS) -S $<
diff --git a/fs/binfmt_coff.c b/fs/binfmt_coff.c
new file mode 100644
index 0000000..53b5eda
--- /dev/null
+++ b/fs/binfmt_coff.c
@@ -0,0 +1,767 @@
+/*
+ * These are the functions used to load COFF IBSC style executables.
+ * Information on COFF format may be obtained in either the Intel Binary
+ * Compatibility Specification 2 or O'Rilley's book on COFF. The shared
+ * libraries are defined only the in the Intel book.
+ *
+ * This file is based upon code written by Eric Youndale for the ELF object
+ * file format.
+ *
+ * Revision information:
+ * 28 August 1993
+ * Al Longyear (longyear@sii.com)
+ * initial release to alpha level testing. This version does not load
+ * shared libraries, but will identify them and log the file names.
+ *
+ * 4 September 1993
+ * Al Longyear (longyear@sii.com)
+ * Added support for shared libraries.
+ *
+ * 9 September 1993
+ * Al Longyear (longyear@sii.com)
+ * Load the FS register with the proper value prior to the call to
+ * sys_uselib().
+ *
+ * Built the parameter and envionment strings before destroying the
+ * current executable.
+ *
+ * 10 September 1993
+ * Al Longyear (longyear@sii.com)
+ * Added new parameter to the create_tables() function to allow the
+ * proper creation of the IBCS environment stack when the process is
+ * started.
+ *
+ * Added code to create_tables() which I mistakenly deleted in the
+ * last patch.
+ *
+ * 13 September 1993
+ * Al Longyear (longyear@sii.com)
+ * Removed erroneous code which mistakenly folded .data with .bss for
+ * a shared library.
+ */
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/a.out.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/binfmts.h>
+#include <asm/segment.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/coff.h>
+#include <linux/malloc.h>
+
+asmlinkage int sys_exit (int exit_code);
+asmlinkage int sys_close (unsigned fd);
+asmlinkage int sys_open (const char *, int, int);
+asmlinkage int sys_uselib(const char * library);
+
+static int preload_library (struct linux_binprm *exe_bprm,
+ COFF_SCNHDR * sect,
+ struct file *fp);
+
+static int load_object (struct linux_binprm *bprm,
+ struct pt_regs *regs,
+ int lib_ok);
+
+/*
+ * Small procedure to test for the proper file alignment.
+ */
+
+static inline int
+is_properly_aligned (COFF_SCNHDR *sect)
+{
+ long scnptr = COFF_LONG (sect->s_scnptr);
+ long vaddr = COFF_LONG (sect->s_vaddr);
+/*
+ * Print the section information if needed
+ */
+
+#ifdef COFF_DEBUG
+ printk ("%s, scnptr = %d, vaddr = %d\n",
+ sect->s_name,
+ scnptr, vaddr);
+#endif
+
+/*
+ * Return the error code if the section is not properly aligned.
+ */
+
+#ifdef COFF_DEBUG
+ if (((vaddr - scnptr) & ~PAGE_MASK) != 0)
+ printk ("bad alignment in %s\n", sect->s_name);
+#endif
+ return ((((vaddr - scnptr) & ~PAGE_MASK) != 0) ? -ENOEXEC : 0);
+}
+
+/*
+ * Helper function to process the load operation.
+ */
+
+static int
+load_object (struct linux_binprm * bprm, struct pt_regs *regs, int lib_ok)
+{
+ COFF_FILHDR *coff_hdr = (COFF_FILHDR *) bprm->buf; /* COFF Header */
+ COFF_SCNHDR *sect_bufr; /* Pointer to section table */
+ COFF_SCNHDR *text_sect; /* Pointer to the text section */
+ COFF_SCNHDR *data_sect; /* Pointer to the data section */
+ COFF_SCNHDR *bss_sect; /* Pointer to the bss section */
+ int text_count; /* Number of text sections */
+ int data_count; /* Number of data sections */
+ int bss_count; /* Number of bss sections */
+ int lib_count; /* Number of lib sections */
+ unsigned int start_addr = 0;/* Starting location for program */
+ int status = 0; /* Result status register */
+ int fd = -1; /* Open file descriptor */
+ struct file *fp = NULL; /* Pointer to the file at "fd" */
+ short int sections = 0; /* Number of sections in the file */
+ short int aout_size = 0; /* Size of the a.out header area */
+ short int flags; /* Flag bits from the COFF header */
+
+#ifdef COFF_DEBUG
+ printk ("binfmt_coff entry: %s\n", bprm->filename);
+#endif
+
+/*
+ * Validate the magic value for the object file.
+ */
+ do {
+ if (COFF_I386BADMAG (*coff_hdr)) {
+#ifdef COFF_DEBUG
+ printk ("bad filehdr magic\n");
+#endif
+ status = -ENOEXEC;
+ break;
+ }
+/*
+ * The object file should have 32 BIT little endian format. Do not allow
+ * it to have the 16 bit object file flag set as Linux is not able to run
+ * on the 80286/80186/8086.
+ */
+ flags = COFF_SHORT (coff_hdr->f_flags);
+ if ((flags & (COFF_F_AR32WR | COFF_F_AR16WR)) != COFF_F_AR32WR) {
+#ifdef COFF_DEBUG
+ printk ("invalid f_flags bits\n");
+#endif
+ status = -ENOEXEC;
+ break;
+ }
+/*
+ * Extract the header information which we need.
+ */
+ sections = COFF_SHORT (coff_hdr->f_nscns); /* Number of sections */
+ aout_size = COFF_SHORT (coff_hdr->f_opthdr); /* Size of opt. headr */
+/*
+ * If the file is not executable then reject the exectution. This means
+ * that there must not be external references.
+ */
+ if ((flags & COFF_F_EXEC) == 0) {
+#ifdef COFF_DEBUG
+ printk ("not executable bit\n");
+#endif
+ status = -ENOEXEC;
+ break;
+ }
+/*
+ * There must be atleast one section.
+ */
+ if (sections == 0) {
+#ifdef COFF_DEBUG
+ printk ("no sections\n");
+#endif
+ status = -ENOEXEC;
+ break;
+ }
+/*
+ * Do some additional consistency checks.
+ * The system requires mapping for this loader. If you try
+ * to use a file system with no mapping, the format is not valid.
+ */
+ if (!bprm->inode->i_op ||
+ !bprm->inode->i_op->default_file_ops->mmap) {
+#ifdef COFF_DEBUG
+ printk ("no mmap in fs\n");
+#endif
+ status = -ENOEXEC;
+ }
+ }
+ while (0);
+/*
+ * Allocate a buffer to hold the entire coff section list.
+ */
+ if (status >= 0) {
+ int nbytes = sections * COFF_SCNHSZ;
+
+ sect_bufr = (COFF_SCNHDR *) kmalloc (nbytes, GFP_KERNEL);
+ if (0 == sect_bufr) {
+#ifdef COFF_DEBUG
+ printk ("kmalloc failed\n");
+#endif
+ status = -ENOEXEC;
+ }
+/*
+ * Read the section list from the disk file.
+ */
+ else {
+ int old_fs = get_fs ();
+ set_fs (get_ds ()); /* Make it point to the proper location */
+ status = read_exec (bprm->inode, /* INODE for file */
+ aout_size + COFF_FILHSZ, /* Offset in the file */
+ (char *) sect_bufr, /* Buffer for read */
+ nbytes); /* Byte count reqd. */
+ set_fs (old_fs); /* Restore the selector */
+#ifdef COFF_DEBUG
+ if (status < 0)
+ printk ("read aout hdr, status = %d\n", status);
+#endif
+ }
+ }
+ else
+ sect_bufr = NULL; /* Errors do not have a section buffer */
+/*
+ * Count the number of sections for the required types and store the location
+ * of the last section for the three primary types.
+ */
+ text_count = 0;
+ data_count = 0;
+ bss_count = 0;
+ lib_count = 0;
+
+ text_sect = NULL;
+ data_sect = NULL;
+ bss_sect = NULL;
+/*
+ * Loop through the sections and find the various types
+ */
+ if (status >= 0) {
+ int nIndex;
+ COFF_SCNHDR *sect_ptr = sect_bufr;
+
+ for (nIndex = 0; nIndex < sections; ++nIndex) {
+ long int sect_flags = COFF_LONG (sect_ptr->s_flags);
+
+ switch (sect_flags) {
+ case COFF_STYP_TEXT:
+ text_sect = sect_ptr;
+ ++text_count;
+ status = is_properly_aligned (sect_ptr);
+ break;
+
+ case COFF_STYP_DATA:
+ data_sect = sect_ptr;
+ ++data_count;
+ status = is_properly_aligned (sect_ptr);
+ break;
+
+ case COFF_STYP_BSS:
+ bss_sect = sect_ptr;
+ ++bss_count;
+ break;
+
+ case COFF_STYP_LIB:
+#ifdef COFF_DEBUG
+ printk (".lib section found\n");
+#endif
+ ++lib_count;
+ break;
+
+ default:
+ break;
+ }
+ sect_ptr = (COFF_SCNHDR *) & ((char *) sect_ptr)[COFF_SCNHSZ];
+ }
+/*
+ * Ensure that there are the required sections. There must be one text
+ * sections and one each of the data and bss sections for an executable.
+ * A library may or may not have a data / bss section.
+ */
+ if (text_count != 1) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("no text sections\n");
+#endif
+ }
+ else {
+ if (lib_ok) {
+ if (data_count != 1 || bss_count != 1) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("no .data nor .bss sections\n");
+#endif
+ }
+ }
+ }
+ }
+/*
+ * If there is no additional header then assume the file starts at
+ * the first byte of the text section. This may not be the proper place,
+ * so the best solution is to include the optional header. A shared library
+ * __MUST__ have an optional header to indicate that it is a shared library.
+ */
+ if (status >= 0) {
+ if (aout_size == 0) {
+ if (!lib_ok) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("no header in library\n");
+#endif
+ }
+ start_addr = COFF_LONG (text_sect->s_vaddr);
+ }
+/*
+ * There is some header. Ensure that it is sufficient.
+ */
+ else {
+ if (aout_size < COFF_AOUTSZ) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("header too small\n");
+#endif
+ }
+ else {
+ COFF_AOUTHDR *aout_hdr = /* Pointer to a.out header */
+ (COFF_AOUTHDR *) & ((char *) coff_hdr)[COFF_FILHSZ];
+ short int aout_magic = COFF_SHORT (aout_hdr->magic); /* id */
+/*
+ * Validate the magic number in the a.out header. If it is valid then
+ * update the starting symbol location. Do not accept these file formats
+ * when loading a shared library.
+ */
+ switch (aout_magic) {
+ case COFF_OMAGIC:
+ case COFF_ZMAGIC:
+ case COFF_STMAGIC:
+ if (!lib_ok) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("wrong a.out header magic\n");
+#endif
+ }
+ start_addr = (unsigned int) COFF_LONG (aout_hdr->entry);
+ break;
+/*
+ * Magic value for a shared library. This is valid only when loading a
+ * shared library. (There is no need for a start_addr. It won't be used.)
+ */
+ case COFF_SHMAGIC:
+ if (lib_ok) {
+#ifdef COFF_DEBUG
+ printk ("wrong a.out header magic\n");
+#endif
+ status = -ENOEXEC;
+ }
+ break;
+
+ default:
+#ifdef COFF_DEBUG
+ printk ("wrong a.out header magic\n");
+#endif
+ status = -ENOEXEC;
+ break;
+ }
+ }
+ }
+ }
+/*
+ * Fetch a file pointer to the executable.
+ */
+ if (status >= 0) {
+ fd = open_inode (bprm->inode, O_RDONLY);
+ if (fd < 0) {
+#ifdef COFF_DEBUG
+ printk ("can not open inode, result = %d\n", fd);
+#endif
+ status = fd;
+ }
+ else
+ fp = current->filp[fd];
+ }
+ else
+ fd = -1; /* Invalidate the open file descriptor */
+/*
+ * Generate the proper values for the text fields
+ *
+ * THIS IS THE POINT OF NO RETURN. THE NEW PROCESS WILL TRAP OUT SHOULD
+ * SOMETHING FAIL IN THE LOAD SEQUENCE FROM THIS POINT ONWARD.
+ */
+ if (status >= 0) {
+ long text_scnptr = COFF_LONG (text_sect->s_scnptr);
+ long text_size = COFF_LONG (text_sect->s_size);
+ long text_vaddr = COFF_LONG (text_sect->s_vaddr);
+
+ long data_scnptr;
+ long data_size;
+ long data_vaddr;
+
+ long bss_size;
+ long bss_vaddr;
+/*
+ * Generate the proper values for the data fields
+ */
+ if (data_sect != NULL) {
+ data_scnptr = COFF_LONG (data_sect->s_scnptr);
+ data_size = COFF_LONG (data_sect->s_size);
+ data_vaddr = COFF_LONG (data_sect->s_vaddr);
+ }
+ else {
+ data_scnptr = 0;
+ data_size = 0;
+ data_vaddr = 0;
+ }
+/*
+ * Generate the proper values for the bss fields
+ */
+ if (bss_sect != NULL) {
+ bss_size = COFF_LONG (bss_sect->s_size);
+ bss_vaddr = COFF_LONG (bss_sect->s_vaddr);
+ }
+ else {
+ bss_size = 0;
+ bss_vaddr = 0;
+ }
+/*
+ * Flush the executable from memory. At this point the executable is
+ * committed to being defined or a segmentation violation will occur.
+ */
+ if (lib_ok) {
+#ifdef COFF_DEBUG
+ printk ("flushing executable\n");
+#endif
+ flush_old_exec (bprm);
+/*
+ * Define the initial locations for the various items in the new process
+ */
+ current->mmap = NULL;
+ current->rss = 0;
+/*
+ * Construct the parameter and environment string table entries.
+ */
+ bprm->p += change_ldt (0, bprm->page);
+ bprm->p -= MAX_ARG_PAGES*PAGE_SIZE;
+ bprm->p = (unsigned long) create_tables ((char *) bprm->p,
+ bprm->argc,
+ bprm->envc,
+ 1);
+/*
+ * Do the end processing once the stack has been constructed
+ */
+ current->start_code = text_vaddr & PAGE_MASK;
+ current->end_code = text_vaddr + text_size;
+ current->end_data = data_vaddr + data_size;
+ current->start_brk =
+ current->brk = bss_vaddr + bss_size;
+ current->suid =
+ current->euid = bprm->e_uid;
+ current->sgid =
+ current->egid = bprm->e_gid;
+ current->executable = bprm->inode; /* Store inode for file */
+ ++bprm->inode->i_count; /* Count the open inode */
+ regs->eip = start_addr; /* Current EIP register */
+ regs->esp =
+ current->start_stack = bprm->p;
+ }
+/*
+ * Map the text pages
+ */
+
+#ifdef COFF_DEBUG
+ printk (".text: vaddr = %d, size = %d, scnptr = %d\n",
+ text_vaddr,
+ text_size,
+ text_scnptr);
+#endif
+ status = do_mmap (fp,
+ text_vaddr & PAGE_MASK,
+ text_size + (text_vaddr & ~PAGE_MASK),
+ PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_SHARED,
+ text_scnptr & PAGE_MASK);
+
+ status = (status == (text_vaddr & PAGE_MASK)) ? 0 : -ENOEXEC;
+/*
+ * Map the data pages
+ */
+ if (status >= 0 && data_size != 0) {
+#ifdef COFF_DEBUG
+ printk (".data: vaddr = %d, size = %d, scnptr = %d\n",
+ data_vaddr,
+ data_size,
+ data_scnptr);
+#endif
+ status = do_mmap (fp,
+ data_vaddr & PAGE_MASK,
+ data_size + (data_vaddr & ~PAGE_MASK),
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE,
+ data_scnptr & PAGE_MASK);
+
+ status = (status == (data_vaddr & PAGE_MASK)) ? 0 : -ENOEXEC;
+ }
+/*
+ * Construct the bss data for the process. The bss ranges from the
+ * end of the data (which may not be on a page boundry) to the end
+ * of the bss section. Allocate any necessary pages for the data.
+ */
+ if (status >= 0 && bss_size != 0) {
+#ifdef COFF_DEBUG
+ printk (".bss: vaddr = %d, size = %d\n",
+ bss_vaddr,
+ bss_size);
+#endif
+ zeromap_page_range (PAGE_ALIGN (bss_vaddr),
+ PAGE_ALIGN (bss_size),
+ PAGE_COPY);
+ }
+/*
+ * Load any shared library for the executable.
+ */
+ if (lib_ok && lib_count != 0) {
+ int nIndex;
+ COFF_SCNHDR *sect_ptr = sect_bufr;
+/*
+ * Find the library sections. (There should be atleast one. It was counted
+ * earlier.) This will evenutally recurse to our code and load the shared
+ * library with our own procedures.
+ */
+ for (nIndex = 0; nIndex < sections; ++nIndex) {
+ long int sect_flags = COFF_LONG (sect_ptr->s_flags);
+ if (sect_flags == COFF_STYP_LIB) {
+ status = preload_library (bprm, sect_ptr, fp);
+ if (status != 0)
+ break;
+ }
+ sect_ptr = (COFF_SCNHDR *) &((char *) sect_ptr) [COFF_SCNHSZ];
+ }
+ }
+/*
+ * Generate any needed trap for this process. If an error occured then
+ * generate a segmentation violation. If the process is being debugged
+ * then generate the load trap. (Note: If this is a library load then
+ * do not generate the trap here. Pass the error to the caller who
+ * will do it for the process in the outer lay of this procedure call.)
+ */
+ if (lib_ok) {
+ if (status < 0)
+ send_sig (SIGSEGV, current, 0); /* Generate the error trap */
+ else {
+ if (current->flags & PF_PTRACED)
+ send_sig (SIGTRAP, current, 0);
+ }
+ status = 0; /* We are committed. It can't fail */
+ }
+ }
+/*
+ * Do any cleanup processing
+ */
+ if (fd >= 0)
+ sys_close (fd); /* Close unused code file */
+
+ if (sect_bufr != NULL)
+ kfree (sect_bufr); /* Release section list buffer */
+/*
+ * Return the completion status.
+ */
+#ifdef COFF_DEBUG
+ printk ("binfmt_coff: result = %d\n", status);
+#endif
+ return (status);
+}
+
+/*
+ * This procedure will load the library listed in the file name given
+ * as the paramter. The result will be non-zero should something fail
+ * to load.
+ */
+
+static int
+preload_this_library (struct linux_binprm *exe_bprm, char *lib_name)
+{
+ int status;
+ int old_fs = get_fs();
+/*
+ * If debugging then print "we have arrived"
+ */
+#ifdef COFF_DEBUG
+ printk ("%s loading shared library %s\n",
+ exe_bprm->filename,
+ lib_name);
+#endif
+/*
+ * Change the FS register to the proper kernel address space and attempt
+ * to load the library. The library name is allocated from the kernel
+ * pool.
+ */
+ set_fs (get_ds ());
+ status = sys_uselib (lib_name);
+ set_fs (old_fs);
+/*
+ * Return the success/failure to the caller.
+ */
+ return (status);
+}
+
+/*
+ * This procedure is called to load a library section. The various
+ * libraries are loaded from the list given in the section data.
+ */
+
+static int
+preload_library (struct linux_binprm *exe_bprm,
+ COFF_SCNHDR * sect, struct file *fp)
+{
+ int status = 0; /* Completion status */
+ long nbytes; /* Count of bytes in the header area */
+/*
+ * Fetch the size of the section. There must be enough room for atleast
+ * one entry.
+ */
+ nbytes = COFF_LONG (sect->s_size);
+ if (nbytes < COFF_SLIBSZ) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("library section too small\n");
+#endif
+ }
+/*
+ * Allocate a buffer to hold the section data
+ */
+ else {
+ COFF_SLIBHD *phdr;
+ char *buffer = (char *) kmalloc (nbytes, GFP_KERNEL);
+
+ if (0 == buffer) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("kmalloc failed\n");
+#endif
+ }
+ else {
+ int old_fs = get_fs ();
+/*
+ * Read the section data from the disk file.
+ */
+ set_fs (get_ds ()); /* Make it point to the proper location */
+ status = read_exec (exe_bprm->inode, /* INODE for file */
+ COFF_LONG (sect->s_scnptr), /* Disk location */
+ buffer, /* Buffer for read */
+ nbytes); /* Byte count reqd. */
+ set_fs (old_fs); /* Restore the selector */
+ }
+/*
+ * At this point, go through the list of libraries in the data area.
+ */
+ phdr = (COFF_SLIBHD *) buffer;
+ while (status == 0 && nbytes > COFF_SLIBSZ) {
+ int entry_size = COFF_LONG (phdr->sl_entsz) * sizeof (long);
+ int header_size = COFF_LONG (phdr->sl_pathndx) * sizeof (long);
+/*
+ * Validate the sizes of the various items. I don't trust the linker!!
+ */
+ if ((unsigned) header_size >= (unsigned) nbytes ||
+ entry_size <= 0 ||
+ (unsigned) entry_size <= (unsigned) header_size) {
+ status = -ENOEXEC;
+#ifdef COFF_DEBUG
+ printk ("header count is invalid\n");
+#endif
+ }
+/*
+ * Load the library. Stop the load process on the first error.
+ */
+ else {
+ status = preload_this_library (exe_bprm,
+ &((char *) phdr)[header_size]);
+#ifdef COFF_DEBUG
+ printk ("preload_this_library result = %d\n", status);
+#endif
+ }
+/*
+ * Point to the next library in the section data.
+ */
+ nbytes -= entry_size;
+ phdr = (COFF_SLIBHD *) &((char *) phdr)[entry_size];
+ }
+/*
+ * Release the space for the library list.
+ */
+ if (buffer != NULL)
+ kfree (buffer);
+ }
+/*
+ * Return the resulting status to the caller.
+ */
+ return (status);
+}
+
+/*
+ * This procedure is called by the main load sequence. It will load
+ * the executable and prepare it for execution. It provides the additional
+ * parameters used by the recursive coff loader and tells the loader that
+ * this is the main executable. How simple it is . . . .
+ */
+
+int
+load_coff_binary (struct linux_binprm *bprm, struct pt_regs *regs)
+{
+ return (load_object (bprm, regs, 1));
+}
+
+/*
+ * Load the image for any shared library.
+ *
+ * This is called when we need to load a library based upon a file name.
+ */
+
+int
+load_coff_library (int fd)
+{
+ struct linux_binprm *bprm; /* Parameters for the load operation */
+ int status; /* Status of the request */
+/*
+ * Read the first portion of the file.
+ */
+ bprm = (struct linux_binprm *) kmalloc (sizeof (struct linux_binprm),
+ GFP_KERNEL);
+ if (0 == bprm) {
+#ifdef COFF_DEBUG
+ printk ("kmalloc failed\n");
+#endif
+ status = -ENOEXEC;
+ }
+ else {
+ struct file *file; /* Pointer to the file table */
+ struct pt_regs regs; /* Register work area */
+ int old_fs = get_fs (); /* Previous FS register value */
+
+ memset (bprm, '\0', sizeof (struct linux_binprm));
+
+ file = current->filp[fd];
+ bprm->inode = file->f_inode; /* The only item _really_ needed */
+ bprm->filename = ""; /* Make it a legal string */
+/*
+ * Read the section list from the disk file.
+ */
+ set_fs (get_ds ()); /* Make it point to the proper location */
+ status = read_exec (bprm->inode, /* INODE for file */
+ 0L, /* Offset in the file */
+ bprm->buf, /* Buffer for read */
+ sizeof (bprm->buf)); /* Size of the buffer */
+ set_fs (old_fs); /* Restore the selector */
+/*
+ * Try to load the library.
+ */
+ status = load_object (bprm, &regs, 0);
+/*
+ * Release the work buffer and return the result.
+ */
+ kfree (bprm); /* Release the buffer area */
+ }
+/*
+ * Return the result of the load operation
+ */
+ return (status);
+}
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 4a7a1c7..23d9abe 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -307,7 +307,7 @@ int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
current->start_stack = p = elf_stack - 4;
#endif
bprm->p -= MAX_ARG_PAGES*PAGE_SIZE;
- bprm->p = (unsigned long) create_tables((char *)bprm->p,bprm->argc,bprm->envc);
+ bprm->p = (unsigned long) create_tables((char *)bprm->p,bprm->argc,bprm->envc,0);
if(elf_interpreter) current->arg_start += strlen(passed_fileno) + 1;
current->start_brk = current->brk = elf_brk;
current->end_code = end_code;
diff --git a/fs/exec.c b/fs/exec.c
index 741da6b..4f744f1 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -261,7 +261,7 @@ asmlinkage int sys_uselib(const char * library)
* memory and creates the pointer tables from them, and puts their
* addresses on the "stack", returning the new stack pointer value.
*/
-unsigned long * create_tables(char * p,int argc,int envc)
+unsigned long * create_tables(char * p,int argc,int envc,int ibcs)
{
unsigned long *argv,*envp;
unsigned long * sp;
@@ -285,8 +285,10 @@ unsigned long * create_tables(char * p,int argc,int envc)
envp = sp;
sp -= argc+1;
argv = sp;
- put_fs_long((unsigned long)envp,--sp);
- put_fs_long((unsigned long)argv,--sp);
+ if (!ibcs) {
+ put_fs_long((unsigned long)envp,--sp);
+ put_fs_long((unsigned long)argv,--sp);
+ }
put_fs_long((unsigned long)argc,--sp);
current->arg_start = (unsigned long) p;
while (argc-->0) {
@@ -723,9 +725,17 @@ extern int load_aout_binary(struct linux_binprm *,
struct pt_regs * regs);
extern int load_aout_library(int fd);
+#ifdef CONFIG_BINFMT_ELF
extern int load_elf_binary(struct linux_binprm *,
struct pt_regs * regs);
extern int load_elf_library(int fd);
+#endif
+
+#ifdef CONFIG_BINFMT_COFF
+extern int load_coff_binary(struct linux_binprm *,
+ struct pt_regs * regs);
+extern int load_coff_library(int fd);
+#endif
/* Here are the actual binaries that will be accepted */
struct linux_binfmt formats[] = {
@@ -733,6 +743,9 @@ struct linux_binfmt formats[] = {
#ifdef CONFIG_BINFMT_ELF
{load_elf_binary, load_elf_library},
#endif
+#ifdef CONFIG_BINFMT_COFF
+ {load_coff_binary, load_coff_library},
+#endif
{NULL, NULL}
};
@@ -828,7 +841,7 @@ beyond_if:
p += change_ldt(ex.a_text,bprm->page);
p -= MAX_ARG_PAGES*PAGE_SIZE;
- p = (unsigned long) create_tables((char *)p,bprm->argc,bprm->envc);
+ p = (unsigned long) create_tables((char *)p,bprm->argc,bprm->envc,0);
current->start_stack = p;
regs->eip = ex.a_entry; /* eip, magic happens :-) */
regs->esp = p; /* stack pointer */
diff --git a/fs/filesystems.c b/fs/filesystems.c
index 671d76e..4d59fb7 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -35,6 +35,9 @@
#ifdef CONFIG_HPFS_FS
#include <linux/hpfs_fs.h>
#endif
+#ifdef CONFIG_SYSV_FS
+#include <linux/sysv_fs.h>
+#endif
struct file_system_type file_systems[] = {
#ifdef CONFIG_MINIX_FS
@@ -61,6 +64,11 @@ struct file_system_type file_systems[] = {
#ifdef CONFIG_ISO9660_FS
{isofs_read_super, "iso9660", 1},
#endif
+#ifdef CONFIG_SYSV_FS
+ {sysv_read_super, "xenix", 1},
+ {sysv_read_super, "sysv", 1},
+ {sysv_read_super, "coherent", 1},
+#endif
#ifdef CONFIG_HPFS_FS
{hpfs_read_super, "hpfs", 1},
#endif
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 01eb341..550d792 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -331,8 +331,10 @@ void isofs_read_inode(struct inode * inode)
int i;
block = inode->i_ino >> ISOFS_BUFFER_BITS(inode);
- if (!(bh=bread(inode->i_dev,block, bufsize)))
- panic("unable to read i-node block");
+ if (!(bh=bread(inode->i_dev,block, bufsize))) {
+ printk("unable to read i-node block");
+ goto fail;
+ };
pnt = ((unsigned char *) bh->b_data
+ (inode->i_ino & (bufsize - 1)));
@@ -343,8 +345,11 @@ void isofs_read_inode(struct inode * inode)
cpnt = kmalloc(1 << ISOFS_BLOCK_BITS, GFP_KERNEL);
memcpy(cpnt, bh->b_data, bufsize);
brelse(bh);
- if (!(bh = bread(inode->i_dev,++block, bufsize)))
- panic("unable to read i-node block");
+ if (!(bh = bread(inode->i_dev,++block, bufsize))) {
+ kfree_s (cpnt, 1 << ISOFS_BLOCK_BITS);
+ printk("unable to read i-node block");
+ goto fail;
+ };
memcpy((char *)cpnt + bufsize, bh->b_data, bufsize);
pnt = ((unsigned char *) cpnt
+ (inode->i_ino & (bufsize - 1)));
@@ -391,10 +396,6 @@ void isofs_read_inode(struct inode * inode)
inode->i_size &= 0x00ffffff;
};
- if (isonum_723 (raw_inode->volume_sequence_number) != 1) {
- printk("Multi volume CD somehow got mounted.\n");
- };
-
if (raw_inode->interleave[0]) {
printk("Interleaved files not (yet) supported.\n");
inode->i_size = 0;
@@ -467,18 +468,35 @@ void isofs_read_inode(struct inode * inode)
};
inode->i_op = NULL;
- if (S_ISREG(inode->i_mode))
- inode->i_op = &isofs_file_inode_operations;
- else if (S_ISDIR(inode->i_mode))
- inode->i_op = &isofs_dir_inode_operations;
- else if (S_ISLNK(inode->i_mode))
- inode->i_op = &isofs_symlink_inode_operations;
- else if (S_ISCHR(inode->i_mode))
- inode->i_op = &chrdev_inode_operations;
- else if (S_ISBLK(inode->i_mode))
- inode->i_op = &blkdev_inode_operations;
- else if (S_ISFIFO(inode->i_mode))
- init_fifo(inode);
+ if (inode->i_sb->u.isofs_sb.s_cruft != 'y' &&
+ isonum_723 (raw_inode->volume_sequence_number) != 1) {
+ printk("Multi volume CD somehow got mounted.\n");
+ } else {
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &isofs_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &isofs_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &isofs_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &blkdev_inode_operations;
+ else if (S_ISFIFO(inode->i_mode))
+ init_fifo(inode);
+ }
+ return;
+ fail:
+ /* With a data error we return this information */
+ inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
+ inode->u.isofs_i.i_first_extent = 0;
+ inode->u.isofs_i.i_backlink = 0xffffffff;
+ inode->i_size = 0;
+ inode->i_nlink = 1;
+ inode->i_uid = inode->i_gid = 0;
+ inode->i_mode = S_IFREG; /*Regular file, noone gets to read*/
+ inode->i_op = NULL;
+ return;
}
/* There are times when we need to know the inode number of a parent of
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
index c2aeb26..25b4214 100644
--- a/fs/isofs/rock.c
+++ b/fs/isofs/rock.c
@@ -388,8 +388,10 @@ char * get_rock_ridge_symlink(struct inode * inode)
rpnt = 0;
block = inode->i_ino >> bufbits;
- if (!(bh=bread(inode->i_dev,block, bufsize)))
- panic("unable to read i-node block");
+ if (!(bh=bread(inode->i_dev,block, bufsize))) {
+ printk("unable to read i-node block");
+ return NULL;
+ };
pnt = ((unsigned char *) bh->b_data) + (inode->i_ino & (bufsize - 1));
@@ -399,8 +401,11 @@ char * get_rock_ridge_symlink(struct inode * inode)
cpnt = kmalloc(1 << ISOFS_BLOCK_BITS, GFP_KERNEL);
memcpy(cpnt, bh->b_data, bufsize);
brelse(bh);
- if (!(bh = bread(inode->i_dev,++block, bufsize)))
- panic("unable to read i-node block");
+ if (!(bh = bread(inode->i_dev,++block, bufsize))) {
+ kfree_s(cpnt, 1 << ISOFS_BLOCK_BITS);
+ printk("unable to read i-node block");
+ return NULL;
+ };
memcpy((char *)cpnt+bufsize, bh->b_data, bufsize);
pnt = ((unsigned char *) cpnt) + (inode->i_ino & (bufsize - 1));
raw_inode = ((struct iso_directory_record *) pnt);
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 2f73cf9..f051d0e 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -113,7 +113,7 @@ static inline int *xdr_decode_data(int *p, char *data, int *lenp, int maxlen)
static int *xdr_decode_fattr(int *p, struct nfs_fattr *fattr)
{
- fattr->type = (enum nfs_stat) ntohl(*p++);
+ fattr->type = (enum nfs_ftype) ntohl(*p++);
fattr->mode = ntohl(*p++);
fattr->nlink = ntohl(*p++);
fattr->uid = ntohl(*p++);
diff --git a/fs/proc/net.c b/fs/proc/net.c
index 9972dde..c7c300e 100644
--- a/fs/proc/net.c
+++ b/fs/proc/net.c
@@ -36,8 +36,8 @@ static int proc_lookupnet(struct inode *,const char *,int,struct inode **);
/* the get_*_info() functions are in the net code, and are configured
in via the standard mechanism... */
-#ifdef CONFIG_INET
extern int unix_get_info(char *);
+#ifdef CONFIG_INET
extern int tcp_get_info(char *);
extern int udp_get_info(char *);
extern int raw_get_info(char *);
@@ -83,10 +83,10 @@ struct inode_operations proc_net_inode_operations = {
static struct proc_dir_entry net_dir[] = {
{ 1,2,".." },
- { 8,1,"." }
+ { 8,1,"." },
+ { 128,4,"unix" }
#ifdef CONFIG_INET
- ,{ 128,4,"unix" },
- { 129,3,"arp" },
+ ,{ 129,3,"arp" },
{ 130,5,"route" },
{ 131,3,"dev" },
{ 132,3,"raw" },
diff --git a/fs/sysv/INTRO b/fs/sysv/INTRO
new file mode 100644
index 0000000..fbf59b4
--- /dev/null
+++ b/fs/sysv/INTRO
@@ -0,0 +1,189 @@
+This is the implementation of the SystemV/Coherent filesystem for Linux.
+It grew out of separate filesystem implementations
+
+ Xenix FS Doug Evans <dje@cygnus.com> June 1992
+ SystemV FS Paul B. Monday <pmonday@eecs.wsu.edu> March-June 1993
+ Coherent FS B. Haible <haible@ma2s2.mathematik.uni-karlsruhe.de> June 1993
+
+and was merged together in July 1993.
+
+These filesystems are rather similar. Here is a comparison with Minix FS:
+
+* Linux fdisk reports on partitions
+ - Minix FS 0x81 Linux/Minix
+ - Xenix FS ??
+ - SystemV FS ??
+ - Coherent FS 0x08 AIX bootable
+
+* Size of a block or zone (data allocation unit on disk)
+ - Minix FS 1024
+ - Xenix FS 1024 (also 512 ??)
+ - SystemV FS 1024 (also 512)
+ - Coherent FS 512
+
+* General layout: all have one boot block, one super block and
+ separate areas for inodes and for directories/data.
+ On SystemV Release 2 FS (e.g. Microport) the first track is reserved and
+ all the block numbers (including the super block) are offset by one track.
+
+* Byte ordering of "short" (16 bit entities) on disk:
+ - Minix FS little endian 0 1
+ - Xenix FS little endian 0 1
+ - SystemV FS little endian 0 1
+ - Coherent FS little endian 0 1
+ Of course, this affects only the file system, not the data of files on it!
+
+* Byte ordering of "long" (32 bit entities) on disk:
+ - Minix FS little endian 0 1 2 3
+ - Xenix FS little endian 0 1 2 3
+ - SystemV FS little endian 0 1 2 3
+ - Coherent FS PDP-11 2 3 0 1
+ Of course, this affects only the file system, not the data of files on it!
+
+* Inode on disk: "short", 0 means non-existent, the root dir ino is:
+ - Minix FS 1
+ - Xenix FS, SystemV FS, Coherent FS 2
+
+* Maximum number of hard links to a file:
+ - Minix FS 250
+ - Xenix FS ??
+ - SystemV FS ??
+ - Coherent FS >=10000
+
+* Free inode management:
+ - Minix FS a bitmap
+ - Xenix FS, SystemV FS, Coherent FS
+ There is a cache of a certain number of free inodes in the super-block.
+ When it is exhausted, new free inodes are found using a linear search.
+
+* Free block management:
+ - Minix FS a bitmap
+ - Xenix FS, SystemV FS, Coherent FS
+ Free blocks are organized in a "free list". Maybe a misleading term,
+ since it is not true that every free block contains a pointer to
+ the next free block. Rather, the free blocks are organized in chunks
+ of limited size, and every now and then a free block contains pointers
+ to the free blocks pertaining to the next chunk; the first of these
+ contains pointers and so on. The list terminates with a "block number"
+ 0 on Xenix FS and SystemV FS, with a block zeroed out on Coherent FS.
+
+* Super-block location:
+ - Minix FS block 1 = bytes 1024..2047
+ - Xenix FS block 1 = bytes 1024..2047
+ - SystemV FS bytes 512..1023
+ - Coherent FS block 1 = bytes 512..1023
+
+* Super-block layout:
+ - Minix FS
+ unsigned short s_ninodes;
+ unsigned short s_nzones;
+ unsigned short s_imap_blocks;
+ unsigned short s_zmap_blocks;
+ unsigned short s_firstdatazone;
+ unsigned short s_log_zone_size;
+ unsigned long s_max_size;
+ unsigned short s_magic;
+ - Xenix FS, SystemV FS, Coherent FS
+ unsigned short s_firstdatazone;
+ unsigned long s_nzones;
+ unsigned short s_fzone_count;
+ unsigned long s_fzones[NICFREE];
+ unsigned short s_finode_count;
+ unsigned short s_finodes[NICINOD];
+ char s_flock;
+ char s_ilock;
+ char s_modified;
+ char s_rdonly;
+ unsigned long s_time;
+ short s_dinfo[4]; -- SystemV FS only
+ unsigned long s_free_zones;
+ unsigned short s_free_inodes;
+ short s_dinfo[4]; -- Xenix FS only
+ unsigned short s_interleave_m,s_interleave_n; -- Coherent FS only
+ char s_fname[6];
+ char s_fpack[6];
+ then they differ considerably:
+ Xenix FS
+ char s_clean;
+ char s_fill[371];
+ long s_magic;
+ long s_type;
+ SystemV FS
+ long s_fill[12 or 14];
+ long s_state;
+ long s_magic;
+ long s_type;
+ Coherent FS
+ unsigned long s_unique;
+ Note that Coherent FS has no magic.
+
+* Inode layout:
+ - Minix FS
+ unsigned short i_mode;
+ unsigned short i_uid;
+ unsigned long i_size;
+ unsigned long i_time;
+ unsigned char i_gid;
+ unsigned char i_nlinks;
+ unsigned short i_zone[7+1+1];
+ - Xenix FS, SystemV FS, Coherent FS
+ unsigned short i_mode;
+ unsigned short i_nlink;
+ unsigned short i_uid;
+ unsigned short i_gid;
+ unsigned long i_size;
+ unsigned char i_zone[3*(10+1+1+1)];
+ unsigned long i_atime;
+ unsigned long i_mtime;
+ unsigned long i_ctime;
+
+* Regular file data blocks are organized as
+ - Minix FS
+ 7 direct blocks
+ 1 indirect block (pointers to blocks)
+ 1 double-indirect block (pointer to pointers to blocks)
+ - Xenix FS, SystemV FS, Coherent FS
+ 10 direct blocks
+ 1 indirect block (pointers to blocks)
+ 1 double-indirect block (pointer to pointers to blocks)
+ 1 triple-indirect block (pointer to pointers to pointers to blocks)
+
+* Inode size, inodes per block
+ - Minix FS 32 32
+ - Xenix FS 64 16
+ - SystemV FS 64 16
+ - Coherent FS 64 8
+
+* Directory entry on disk
+ - Minix FS
+ unsigned short inode;
+ char name[14/30];
+ - Xenix FS, SystemV FS, Coherent FS
+ unsigned short inode;
+ char name[14];
+
+* Dir entry size, dir entries per block
+ - Minix FS 16/32 64/32
+ - Xenix FS 16 64
+ - SystemV FS 16 64
+ - Coherent FS 16 32
+
+* How to implement symbolic links such that the host fsck doesn't scream:
+ - Minix FS normal
+ - Xenix FS kludge: as regular files with chmod 1000
+ - SystemV FS ??
+ - Coherent FS kludge: as regular files with chmod 1000
+
+
+Notation: We often speak of a "block" but mean a zone (the allocation unit)
+and not the disk driver's notion of "block".
+
+Because the block size may be smaller than 1024 (which is the unit used by
+the disk drivers and the buffer code), many functions must return a pointer
+to the buffer data additionally to the buffer head pointer. One must not
+assume that the entire buffer is occupied by this single block. This makes
+the implementation of truncate() difficult.
+
+
+Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de>
+
diff --git a/fs/sysv/Makefile b/fs/sysv/Makefile
new file mode 100644
index 0000000..94f8d04
--- /dev/null
+++ b/fs/sysv/Makefile
@@ -0,0 +1,31 @@
+#
+# Makefile for the Linux SystemV/Coherent-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.s.o:
+ $(AS) -o $*.o $<
+
+OBJS= ialloc.o balloc.o inode.o file.o dir.o symlink.o namei.o \
+ fsync.o truncate.o # mmap.o
+
+sysv.o: $(OBJS)
+ $(LD) -r -o sysv.o $(OBJS)
+
+dep:
+ $(CPP) -M *.c > .depend
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/fs/sysv/README b/fs/sysv/README
new file mode 100644
index 0000000..f42e57d
--- /dev/null
+++ b/fs/sysv/README
@@ -0,0 +1,42 @@
+This is the implementation of the SystemV/Coherent filesystem for Linux.
+It implements all of
+ - Xenix FS,
+ - SystemV/386 FS,
+ - Coherent FS.
+
+This is version alpha 5.
+
+To install:
+* You need Linux 0.99.14.
+* Go to /usr/src/linux, unpack the tar file there, and patch the Linux source:
+ patch -p1 < sysvfs.cdif
+ To build the Linux kernel with the patches:
+ make config
+ make depend
+ make
+* To mount a disk or a partition, use
+ mount [-r] -t sysv device mountpoint
+ The file system type names
+ -t sysv
+ -t xenix
+ -t coherent
+ may be used interchangeably, but the last two will eventually disappear.
+
+Bugs in the present implementation:
+- Coherent FS:
+ - The "free list interleave" n:m is currently ignored.
+ - Only file systems with no filesystem name and no pack name are recognized.
+ (See Coherent "man mkfs" for a description of these features.)
+- SystemV Release 2 FS:
+ The superblock is only searched in the blocks 9, 15, 18, which corresponds to the
+ beginning of track 1 on floppy disks. No support for this FS on hard disk yet.
+
+
+Please report any bugs and suggestions to
+ Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhde.de> or
+ Pascal Haible <haible@izfm.uni-stuttgart.de> .
+
+
+Bruno Haible
+<haible@ma2s2.mathematik.uni-karlsruhe.de>
+
diff --git a/fs/sysv/balloc.c b/fs/sysv/balloc.c
new file mode 100644
index 0000000..6990b41
--- /dev/null
+++ b/fs/sysv/balloc.c
@@ -0,0 +1,349 @@
+/*
+ * linux/fs/sysv/balloc.c
+ *
+ * minix/bitmap.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext/freelists.c
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ *
+ * xenix/alloc.c
+ * Copyright (C) 1992 Doug Evans
+ *
+ * coh/alloc.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/balloc.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * This file contains code for allocating/freeing blocks.
+ */
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+/* We don't trust the value of
+ sb->sv_sbd->s_tfree = *sb->sv_sb_total_free_blocks
+ but we nevertheless keep it up to date. */
+
+extern inline void memzero (void * s, size_t count)
+{
+__asm__("cld\n\t"
+ "rep\n\t"
+ "stosl"
+ :
+ :"a" (0),"D" (s),"c" (count/4)
+ :"cx","di","memory");
+}
+
+void sysv_free_block(struct super_block * sb, unsigned int block)
+{
+ struct buffer_head * bh;
+ char * bh_data;
+
+ if (!sb) {
+ printk("sysv_free_block: trying to free block on nonexistent device\n");
+ return;
+ }
+ if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
+ printk("sysv_free_block: trying to free block not in datazone\n");
+ return;
+ }
+ lock_super(sb);
+ if (*sb->sv_sb_flc_count > sb->sv_flc_size) {
+ printk("sysv_free_block: flc_count > flc_size\n");
+ unlock_super(sb);
+ return;
+ }
+ /* If the free list head in super-block is full, it is copied
+ * into this block being freed:
+ */
+ if (*sb->sv_sb_flc_count == sb->sv_flc_size) {
+ unsigned short * flc_count;
+ unsigned long * flc_blocks;
+
+ if (sb->sv_block_size_ratio_bits > 0) /* block_size < BLOCK_SIZE ? */
+ bh = bread(sb->s_dev, (block >> sb->sv_block_size_ratio_bits) + sb->sv_block_base, BLOCK_SIZE);
+ else
+ bh = getblk(sb->s_dev, (block >> sb->sv_block_size_ratio_bits) + sb->sv_block_base, BLOCK_SIZE);
+ if (!bh) {
+ printk("sysv_free_block: getblk() or bread() failed\n");
+ unlock_super(sb);
+ return;
+ }
+ bh_data = bh->b_data + ((block & sb->sv_block_size_ratio_1) << sb->sv_block_size_bits);
+ switch (sb->sv_type) {
+ case FSTYPE_XENIX:
+ flc_count = &((struct xenix_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct xenix_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_SYSV4:
+ flc_count = &((struct sysv4_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct sysv4_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_SYSV2:
+ flc_count = &((struct sysv2_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct sysv2_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_COH:
+ flc_count = &((struct coh_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct coh_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ default: panic("sysv_free_block: invalid fs type\n");
+ }
+ *flc_count = *sb->sv_sb_flc_count; /* = sb->sv_flc_size */
+ memcpy(flc_blocks, sb->sv_sb_flc_blocks, *flc_count * sizeof(sysv_zone_t));
+ bh->b_dirt = 1;
+ bh->b_uptodate = 1;
+ brelse(bh);
+ *sb->sv_sb_flc_count = 0;
+ } else
+ /* If the free list head in super-block is empty, create a new head
+ * in this block being freed:
+ */
+ if (*sb->sv_sb_flc_count == 0) { /* Applies only to Coherent FS */
+ if (sb->sv_block_size_ratio_bits > 0) /* block_size < BLOCK_SIZE ? */
+ bh = bread(sb->s_dev, (block >> sb->sv_block_size_ratio_bits) + sb->sv_block_base, BLOCK_SIZE);
+ else
+ bh = getblk(sb->s_dev, (block >> sb->sv_block_size_ratio_bits) + sb->sv_block_base, BLOCK_SIZE);
+ if (!bh) {
+ printk("sysv_free_block: getblk() or bread() failed\n");
+ unlock_super(sb);
+ return;
+ }
+ bh_data = bh->b_data + ((block & sb->sv_block_size_ratio_1) << sb->sv_block_size_bits);
+ memzero(bh_data, sb->sv_block_size);
+ /* this implies ((struct ..._freelist_chunk *) bh_data)->flc_count = 0; */
+ bh->b_dirt = 1;
+ bh->b_uptodate = 1;
+ brelse(bh);
+ /* still *sb->sv_sb_flc_count = 0 */
+ } else {
+ if (sb->sv_block_size_ratio_bits == 0) { /* block_size == BLOCK_SIZE ? */
+ /* Throw away block's contents */
+ bh = get_hash_table(sb->s_dev, (block >> sb->sv_block_size_ratio_bits) + sb->sv_block_base, BLOCK_SIZE);
+ if (bh)
+ bh->b_dirt = 0;
+ brelse(bh);
+ }
+ }
+ if (sb->sv_convert)
+ block = to_coh_ulong(block);
+ sb->sv_sb_flc_blocks[(*sb->sv_sb_flc_count)++] = block;
+ if (sb->sv_convert)
+ *sb->sv_sb_total_free_blocks =
+ to_coh_ulong(from_coh_ulong(*sb->sv_sb_total_free_blocks) + 1);
+ else
+ *sb->sv_sb_total_free_blocks = *sb->sv_sb_total_free_blocks + 1;
+ sb->sv_bh->b_dirt = 1; /* super-block has been modified */
+ sb->s_dirt = 1; /* and needs time stamp */
+ unlock_super(sb);
+}
+
+int sysv_new_block(struct super_block * sb)
+{
+ unsigned int block;
+ struct buffer_head * bh;
+ char * bh_data;
+
+ if (!sb) {
+ printk("sysv_new_block: trying to get new block from nonexistent device\n");
+ return 0;
+ }
+ lock_super(sb);
+ if (*sb->sv_sb_flc_count == 0) { /* Applies only to Coherent FS */
+ unlock_super(sb);
+ return 0; /* no blocks available */
+ }
+ block = sb->sv_sb_flc_blocks[(*sb->sv_sb_flc_count)-1];
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (block == 0) { /* Applies only to Xenix FS, SystemV FS */
+ unlock_super(sb);
+ return 0; /* no blocks available */
+ }
+ (*sb->sv_sb_flc_count)--;
+ if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
+ printk("sysv_new_block: new block %d is not in data zone\n",block);
+ unlock_super(sb);
+ return 0;
+ }
+ if (*sb->sv_sb_flc_count == 0) { /* the last block continues the free list */
+ unsigned short * flc_count;
+ unsigned long * flc_blocks;
+
+ if (!(bh = sysv_bread(sb, sb->s_dev, block, &bh_data))) {
+ printk("sysv_new_block: cannot read free-list block\n");
+ /* retry this same block next time */
+ (*sb->sv_sb_flc_count)++;
+ unlock_super(sb);
+ return 0;
+ }
+ switch (sb->sv_type) {
+ case FSTYPE_XENIX:
+ flc_count = &((struct xenix_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct xenix_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_SYSV4:
+ flc_count = &((struct sysv4_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct sysv4_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_SYSV2:
+ flc_count = &((struct sysv2_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct sysv2_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_COH:
+ flc_count = &((struct coh_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct coh_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ default: panic("sysv_new_block: invalid fs type\n");
+ }
+ if (*flc_count > sb->sv_flc_size) {
+ printk("sysv_new_block: free-list block with >flc_size entries\n");
+ brelse(bh);
+ unlock_super(sb);
+ return 0;
+ }
+ *sb->sv_sb_flc_count = *flc_count;
+ memcpy(sb->sv_sb_flc_blocks, flc_blocks, *flc_count * sizeof(sysv_zone_t));
+ brelse(bh);
+ }
+ /* Now the free list head in the superblock is valid again. */
+ if (sb->sv_block_size_ratio_bits > 0) /* block_size < BLOCK_SIZE ? */
+ bh = bread(sb->s_dev, (block >> sb->sv_block_size_ratio_bits) + sb->sv_block_base, BLOCK_SIZE);
+ else
+ bh = getblk(sb->s_dev, (block >> sb->sv_block_size_ratio_bits) + sb->sv_block_base, BLOCK_SIZE);
+ if (!bh) {
+ printk("sysv_new_block: getblk() or bread() failed\n");
+ unlock_super(sb);
+ return 0;
+ }
+ bh_data = bh->b_data + ((block & sb->sv_block_size_ratio_1) << sb->sv_block_size_bits);
+ if (sb->sv_block_size_ratio_bits == 0) /* block_size == BLOCK_SIZE ? */
+ if (bh->b_count != 1) {
+ printk("sysv_new_block: block already in use\n");
+ unlock_super(sb);
+ return 0;
+ }
+ memzero(bh_data,sb->sv_block_size);
+ bh->b_dirt = 1;
+ bh->b_uptodate = 1;
+ brelse(bh);
+ if (sb->sv_convert)
+ *sb->sv_sb_total_free_blocks =
+ to_coh_ulong(from_coh_ulong(*sb->sv_sb_total_free_blocks) - 1);
+ else
+ *sb->sv_sb_total_free_blocks = *sb->sv_sb_total_free_blocks - 1;
+ sb->sv_bh->b_dirt = 1; /* super-block has been modified */
+ sb->s_dirt = 1; /* and needs time stamp */
+ unlock_super(sb);
+ return block;
+}
+
+unsigned long sysv_count_free_blocks(struct super_block * sb)
+{
+#if 1 /* test */
+ int count, old_count;
+ unsigned int block;
+ struct buffer_head * bh;
+ char * bh_data;
+ int i;
+
+ /* this causes a lot of disk traffic ... */
+ count = 0;
+ lock_super(sb);
+ if (*sb->sv_sb_flc_count > 0) {
+ for (i = *sb->sv_sb_flc_count ; /* i > 0 */ ; ) {
+ block = sb->sv_sb_flc_blocks[--i];
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (block == 0) /* block 0 terminates list */
+ goto done;
+ count++;
+ if (i == 0)
+ break;
+ }
+ /* block = sb->sv_sb_flc_blocks[0], the last block continues the free list */
+ while (1) {
+ unsigned short * flc_count;
+ unsigned long * flc_blocks;
+
+ if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
+ printk("sysv_count_free_blocks: new block %d is not in data zone\n",block);
+ break;
+ }
+ if (!(bh = sysv_bread(sb, sb->s_dev, block, &bh_data))) {
+ printk("sysv_count_free_blocks: cannot read free-list block\n");
+ break;
+ }
+ switch (sb->sv_type) {
+ case FSTYPE_XENIX:
+ flc_count = &((struct xenix_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct xenix_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_SYSV4:
+ flc_count = &((struct sysv4_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct sysv4_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_SYSV2:
+ flc_count = &((struct sysv2_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct sysv2_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ case FSTYPE_COH:
+ flc_count = &((struct coh_freelist_chunk *) bh_data)->fl_nfree;
+ flc_blocks = &((struct coh_freelist_chunk *) bh_data)->fl_free[0];
+ break;
+ default: panic("sysv_count_free_blocks: invalid fs type\n");
+ }
+ if (*flc_count > sb->sv_flc_size) {
+ printk("sysv_count_free_blocks: free-list block with >flc_size entries\n");
+ brelse(bh);
+ break;
+ }
+ if (*flc_count == 0) { /* Applies only to Coherent FS */
+ brelse(bh);
+ break;
+ }
+ for (i = *flc_count ; /* i > 0 */ ; ) {
+ block = flc_blocks[--i];
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (block == 0) /* block 0 terminates list */
+ break;
+ count++;
+ if (i == 0)
+ break;
+ }
+ /* block = flc_blocks[0], the last block continues the free list */
+ brelse(bh);
+ if (block == 0) /* Applies only to Xenix FS and SystemV FS */
+ break;
+ }
+ done: ;
+ }
+ old_count = *sb->sv_sb_total_free_blocks;
+ if (sb->sv_convert)
+ old_count = from_coh_ulong(old_count);
+ if (count != old_count) {
+ printk("sysv_count_free_blocks: free block count was %d, correcting to %d\n",old_count,count);
+ if (!(sb->s_flags & MS_RDONLY)) {
+ *sb->sv_sb_total_free_blocks = (sb->sv_convert ? to_coh_ulong(count) : count);
+ sb->sv_bh->b_dirt = 1; /* super-block has been modified */
+ sb->s_dirt = 1; /* and needs time stamp */
+ }
+ }
+ unlock_super(sb);
+ return count;
+#else
+ int count;
+
+ count = *sb->sv_sb_total_free_blocks;
+ if (sb->sv_convert)
+ count = from_coh_ulong(count);
+ return count;
+#endif
+}
+
diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c
new file mode 100644
index 0000000..af6647e
--- /dev/null
+++ b/fs/sysv/dir.c
@@ -0,0 +1,110 @@
+/*
+ * linux/fs/sysv/dir.c
+ *
+ * minix/dir.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * coh/dir.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/dir.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * SystemV/Coherent directory handling functions
+ */
+
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/stat.h>
+
+static int sysv_dir_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ return -EISDIR;
+}
+
+static int sysv_readdir(struct inode *, struct file *, struct dirent *, int);
+
+static struct file_operations sysv_dir_operations = {
+ NULL, /* lseek - default */
+ sysv_dir_read, /* read */
+ NULL, /* write - bad */
+ sysv_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ file_fsync /* default fsync */
+};
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations sysv_dir_inode_operations = {
+ &sysv_dir_operations, /* default directory file-ops */
+ sysv_create, /* create */
+ sysv_lookup, /* lookup */
+ sysv_link, /* link */
+ sysv_unlink, /* unlink */
+ sysv_symlink, /* symlink */
+ sysv_mkdir, /* mkdir */
+ sysv_rmdir, /* rmdir */
+ sysv_mknod, /* mknod */
+ sysv_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ sysv_truncate, /* truncate */
+ NULL /* permission */
+};
+
+static int sysv_readdir(struct inode * inode, struct file * filp,
+ struct dirent * dirent, int count)
+{
+ struct super_block * sb;
+ unsigned int offset,i;
+ char c;
+ struct buffer_head * bh;
+ char* bh_data;
+ struct sysv_dir_entry * de;
+
+ if (!inode || !(sb = inode->i_sb) || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+ if ((unsigned long)(filp->f_pos) % SYSV_DIRSIZE)
+ return -EBADF;
+ while (filp->f_pos < inode->i_size) {
+ offset = filp->f_pos & sb->sv_block_size_1;
+ bh = sysv_file_bread(inode, filp->f_pos >> sb->sv_block_size_bits, 0, &bh_data);
+ if (!bh) {
+ filp->f_pos += sb->sv_block_size - offset;
+ continue;
+ }
+ while (offset < sb->sv_block_size && filp->f_pos < inode->i_size) {
+ de = (struct sysv_dir_entry *) (offset + bh_data);
+ offset += SYSV_DIRSIZE;
+ filp->f_pos += SYSV_DIRSIZE;
+ if (de->inode) {
+ for (i = 0; i < SYSV_NAMELEN; i++)
+ if ((c = de->name[i]) != 0)
+ put_fs_byte(c,i+dirent->d_name);
+ else
+ break;
+ if (i) {
+ if (de->inode > inode->i_sb->sv_ninodes)
+ printk("sysv_readdir: Bad inode number on dev 0x%04x, ino %ld, offset 0x%04lx: %d is out of range\n",
+ inode->i_dev, inode->i_ino, filp->f_pos - SYSV_DIRSIZE, de->inode);
+ put_fs_long(de->inode,&dirent->d_ino);
+ put_fs_byte(0,i+dirent->d_name);
+ put_fs_word(i,&dirent->d_reclen);
+ brelse(bh);
+ return i;
+ }
+ }
+ }
+ brelse(bh);
+ }
+ return 0;
+}
diff --git a/fs/sysv/file.c b/fs/sysv/file.c
new file mode 100644
index 0000000..4c012fc
--- /dev/null
+++ b/fs/sysv/file.c
@@ -0,0 +1,313 @@
+/*
+ * linux/fs/sysv/file.c
+ *
+ * minix/file.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * coh/file.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/file.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * SystemV/Coherent regular file handling primitives
+ */
+
+#include <asm/segment.h>
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+#define NBUF 32
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+
+static int sysv_file_read(struct inode *, struct file *, char *, int);
+static int sysv_file_write(struct inode *, struct file *, char *, int);
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the coh filesystem.
+ */
+static struct file_operations sysv_file_operations = {
+ NULL, /* lseek - default */
+ sysv_file_read, /* read */
+ sysv_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ sysv_sync_file /* fsync */
+};
+
+static struct file_operations sysv_file_operations_with_bmap = {
+ NULL, /* lseek - default */
+ sysv_file_read, /* read */
+ sysv_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ generic_mmap, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ sysv_sync_file /* fsync */
+};
+
+struct inode_operations sysv_file_inode_operations = {
+ &sysv_file_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ sysv_truncate, /* truncate */
+ NULL /* permission */
+};
+
+struct inode_operations sysv_file_inode_operations_with_bmap = {
+ &sysv_file_operations_with_bmap, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ sysv_bmap, /* bmap */
+ sysv_truncate, /* truncate */
+ NULL /* permission */
+};
+
+struct sysv_buffer {
+ struct buffer_head * bh;
+ char * bh_data;
+};
+
+static int sysv_file_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ struct super_block * sb = inode->i_sb;
+ int read,left,chars;
+ unsigned int block;
+ int blocks, offset;
+ int bhrequest, bhreqi, uptodate;
+ struct sysv_buffer * bhb, * bhe;
+ struct buffer_head * bhreq[NBUF];
+ struct sysv_buffer buflist[NBUF];
+ unsigned int size;
+
+ if (!inode) {
+ printk("sysv_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ printk("sysv_file_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+ offset = filp->f_pos;
+ size = inode->i_size;
+ if (offset > size)
+ left = 0;
+ else
+ left = size - offset;
+ if (left > count)
+ left = count;
+ if (left <= 0)
+ return 0;
+ read = 0;
+ block = offset >> sb->sv_block_size_bits;
+ offset &= sb->sv_block_size_1;
+ size = (size + sb->sv_block_size_1) >> sb->sv_block_size_bits;
+ blocks = (left + offset + sb->sv_block_size_1) >> sb->sv_block_size_bits;
+ bhb = bhe = buflist;
+ if (filp->f_reada) {
+ blocks += read_ahead[MAJOR(inode->i_dev)] >> (sb->sv_block_size_bits - 9);
+ if (block + blocks > size)
+ blocks = size - block;
+ }
+
+ /* We do this in a two stage process. We first try and request
+ as many blocks as we can, then we wait for the first one to
+ complete, and then we try and wrap up as many as are actually
+ done. This routine is rather generic, in that it can be used
+ in a filesystem by substituting the appropriate function in
+ for getblk.
+
+ This routine is optimized to make maximum use of the various
+ buffers and caches.
+
+ We must remove duplicates from the bhreq array as ll_rw_block
+ doesn't like duplicate requests (it hangs in wait_on_buffer...).
+ */
+
+ do {
+ bhrequest = 0;
+ uptodate = 1;
+ while (blocks) {
+ --blocks;
+ bhb->bh = sysv_getblk(inode, block++, 0, &bhb->bh_data);
+ if (bhb->bh && !bhb->bh->b_uptodate) {
+ uptodate = 0;
+ if (sb->sv_block_size_ratio_bits > 0) /* block_size < BLOCK_SIZE ? */
+ for (bhreqi = 0; bhreqi < bhrequest; bhreqi++)
+ if (bhreq[bhreqi] == bhb->bh)
+ goto notreq;
+ bhreq[bhrequest++] = bhb->bh;
+ notreq: ;
+ }
+
+ if (++bhb == &buflist[NBUF])
+ bhb = buflist;
+
+ /* If the block we have on hand is uptodate, go ahead
+ and complete processing. */
+ if (uptodate)
+ break;
+ if (bhb == bhe)
+ break;
+ }
+
+ /* Now request them all */
+ if (bhrequest)
+ ll_rw_block(READ, bhrequest, bhreq);
+
+ do { /* Finish off all I/O that has actually completed */
+ if (bhe->bh) {
+ wait_on_buffer(bhe->bh);
+ if (!bhe->bh->b_uptodate) { /* read error? */
+ brelse(bhe->bh);
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ left = 0;
+ break;
+ }
+ }
+ if (left < sb->sv_block_size - offset)
+ chars = left;
+ else
+ chars = sb->sv_block_size - offset;
+ filp->f_pos += chars;
+ left -= chars;
+ read += chars;
+ if (bhe->bh) {
+ memcpy_tofs(buf,offset+bhe->bh_data,chars);
+ brelse(bhe->bh);
+ buf += chars;
+ } else {
+ while (chars-- > 0)
+ put_fs_byte(0,buf++);
+ }
+ offset = 0;
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ } while (left > 0 && bhe != bhb && (!bhe->bh || !bhe->bh->b_lock));
+ } while (left > 0);
+
+/* Release the read-ahead blocks */
+ while (bhe != bhb) {
+ brelse(bhe->bh);
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ };
+ if (!read)
+ return -EIO;
+ filp->f_reada = 1;
+ if (!IS_RDONLY(inode))
+ inode->i_atime = CURRENT_TIME;
+ return read;
+}
+
+static int sysv_file_write(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ struct super_block * sb = inode->i_sb;
+ off_t pos;
+ int written,c;
+ struct buffer_head * bh;
+ char * bh_data;
+ char * p;
+
+ if (!inode) {
+ printk("sysv_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ printk("sysv_file_write: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ * But we need to protect against simultaneous truncate as we may end up
+ * writing our data into blocks that have meanwhile been incorporated into
+ * the freelist, thereby trashing the freelist.
+ */
+ if (sb->sv_block_size_ratio_bits > 0) /* block_size < BLOCK_SIZE ? */
+ coh_lock_inode(inode);
+ if (filp->f_flags & O_APPEND)
+ pos = inode->i_size;
+ else
+ pos = filp->f_pos;
+ written = 0;
+ while (written<count) {
+ bh = sysv_getblk (inode, pos >> sb->sv_block_size_bits, 1, &bh_data);
+ if (!bh) {
+ if (!written)
+ written = -ENOSPC;
+ break;
+ }
+ c = sb->sv_block_size - (pos & sb->sv_block_size_1);
+ if (c > count-written)
+ c = count-written;
+ if (c != BLOCK_SIZE && !bh->b_uptodate) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!bh->b_uptodate) {
+ brelse(bh);
+ if (!written)
+ written = -EIO;
+ break;
+ }
+ }
+ /* now either c==BLOCK_SIZE or bh->b_uptodate */
+ p = (pos & sb->sv_block_size_1) + bh_data;
+ pos += c;
+ if (pos > inode->i_size) {
+ inode->i_size = pos;
+ inode->i_dirt = 1;
+ }
+ written += c;
+ memcpy_fromfs(p,buf,c);
+ buf += c;
+ bh->b_uptodate = 1;
+ bh->b_dirt = 1;
+ brelse(bh);
+ }
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ filp->f_pos = pos;
+ inode->i_dirt = 1;
+ if (sb->sv_block_size_ratio_bits > 0) /* block_size < BLOCK_SIZE ? */
+ coh_unlock_inode(inode);
+ return written;
+}
diff --git a/fs/sysv/fsync.c b/fs/sysv/fsync.c
new file mode 100644
index 0000000..bd8fd4e
--- /dev/null
+++ b/fs/sysv/fsync.c
@@ -0,0 +1,200 @@
+/*
+ * linux/fs/sysv/fsync.c
+ *
+ * minix/fsync.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk)
+ *
+ * coh/fsync.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/fsync.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * SystemV/Coherent fsync primitive
+ */
+
+#include <linux/errno.h>
+#include <linux/stat.h>
+
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+
+
+/* return values: 0 means OK/done, 1 means redo, -1 means I/O error. */
+
+/* Sync one block. The block number is
+ * from_coh_ulong(*blockp) if convert=1, *blockp if convert=0.
+ */
+static int sync_block (struct inode * inode, unsigned long * blockp, int convert, int wait)
+{
+ struct buffer_head * bh;
+ unsigned long tmp, block;
+ struct super_block * sb;
+
+ block = tmp = *blockp;
+ if (convert)
+ block = from_coh_ulong(block);
+ if (!block)
+ return 0;
+ sb = inode->i_sb;
+ bh = get_hash_table(inode->i_dev, (block >> sb->sv_block_size_ratio_bits) + sb->sv_block_base, BLOCK_SIZE);
+ if (!bh)
+ return 0;
+ if (*blockp != tmp) {
+ brelse (bh);
+ return 1;
+ }
+ if (wait && bh->b_req && !bh->b_uptodate) {
+ brelse(bh);
+ return -1;
+ }
+ if (wait || !bh->b_uptodate || !bh->b_dirt) {
+ brelse(bh);
+ return 0;
+ }
+ ll_rw_block(WRITE, 1, &bh);
+ bh->b_count--;
+ return 0;
+}
+
+/* Sync one block full of indirect pointers and read it because we'll need it. */
+static int sync_iblock (struct inode * inode, unsigned long * iblockp, int convert,
+ struct buffer_head * *bh, char * *bh_data, int wait)
+{
+ int rc;
+ unsigned long tmp, block;
+
+ *bh = NULL;
+ block = tmp = *iblockp;
+ if (convert)
+ block = from_coh_ulong(block);
+ if (!block)
+ return 0;
+ rc = sync_block (inode, iblockp, convert, wait);
+ if (rc)
+ return rc;
+ *bh = sysv_bread(inode->i_sb, inode->i_dev, block, bh_data);
+ if (tmp != *iblockp) {
+ brelse(*bh);
+ *bh = NULL;
+ return 1;
+ }
+ if (!*bh)
+ return -1;
+ return 0;
+}
+
+
+static int sync_direct(struct inode *inode, int wait)
+{
+ int i;
+ int rc, err = 0;
+
+ for (i = 0; i < 10; i++) {
+ rc = sync_block (inode, inode->u.sysv_i.i_data + i, 0, wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ return err;
+}
+
+static int sync_indirect(struct inode *inode, unsigned long *iblockp, int convert, int wait)
+{
+ int i;
+ struct buffer_head * ind_bh;
+ char * ind_bh_data;
+ int rc, err = 0;
+ struct super_block * sb;
+
+ rc = sync_iblock (inode, iblockp, convert, &ind_bh, &ind_bh_data, wait);
+ if (rc || !ind_bh)
+ return rc;
+
+ sb = inode->i_sb;
+ for (i = 0; i < sb->sv_ind_per_block; i++) {
+ rc = sync_block (inode,
+ ((unsigned long *) ind_bh_data) + i, sb->sv_convert,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(ind_bh);
+ return err;
+}
+
+static int sync_dindirect(struct inode *inode, unsigned long *diblockp, int convert,
+ int wait)
+{
+ int i;
+ struct buffer_head * dind_bh;
+ char * dind_bh_data;
+ int rc, err = 0;
+ struct super_block * sb;
+
+ rc = sync_iblock (inode, diblockp, convert, &dind_bh, &dind_bh_data, wait);
+ if (rc || !dind_bh)
+ return rc;
+
+ sb = inode->i_sb;
+ for (i = 0; i < sb->sv_ind_per_block; i++) {
+ rc = sync_indirect (inode,
+ ((unsigned long *) dind_bh_data) + i, sb->sv_convert,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(dind_bh);
+ return err;
+}
+
+static int sync_tindirect(struct inode *inode, unsigned long *tiblockp, int convert,
+ int wait)
+{
+ int i;
+ struct buffer_head * tind_bh;
+ char * tind_bh_data;
+ int rc, err = 0;
+ struct super_block * sb;
+
+ rc = sync_iblock (inode, tiblockp, convert, &tind_bh, &tind_bh_data, wait);
+ if (rc || !tind_bh)
+ return rc;
+
+ sb = inode->i_sb;
+ for (i = 0; i < sb->sv_ind_per_block; i++) {
+ rc = sync_dindirect (inode,
+ ((unsigned long *) tind_bh_data) + i, sb->sv_convert,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(tind_bh);
+ return err;
+}
+
+int sysv_sync_file(struct inode * inode, struct file * file)
+{
+ int wait, err = 0;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return -EINVAL;
+
+ for (wait=0; wait<=1; wait++) {
+ err |= sync_direct(inode, wait);
+ err |= sync_indirect(inode, inode->u.sysv_i.i_data+10, 0, wait);
+ err |= sync_dindirect(inode, inode->u.sysv_i.i_data+11, 0, wait);
+ err |= sync_tindirect(inode, inode->u.sysv_i.i_data+12, 0, wait);
+ }
+ err |= sysv_sync_inode (inode);
+ return (err < 0) ? -EIO : 0;
+}
diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c
new file mode 100644
index 0000000..c8cc98b
--- /dev/null
+++ b/fs/sysv/ialloc.c
@@ -0,0 +1,203 @@
+/*
+ * linux/fs/sysv/ialloc.c
+ *
+ * minix/bitmap.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext/freelists.c
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ *
+ * xenix/alloc.c
+ * Copyright (C) 1992 Doug Evans
+ *
+ * coh/alloc.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/ialloc.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * This file contains code for allocating/freeing inodes.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+/* We don't trust the value of
+ sb->sv_sbd->s_tinode = *sb->sv_sb_total_free_inodes
+ but we nevertheless keep it up to date. */
+
+/* An inode on disk is considered free if both i_mode == 0 and i_nlink == 0. */
+
+void sysv_free_inode(struct inode * inode)
+{
+ struct super_block * sb;
+ unsigned int ino;
+ struct buffer_head * bh;
+ char * bh_data;
+ struct sysv_inode * raw_inode;
+
+ if (!inode)
+ return;
+ if (!inode->i_dev) {
+ printk("sysv_free_inode: inode has no device\n");
+ return;
+ }
+ if (inode->i_count != 1) {
+ printk("sysv_free_inode: inode has count=%d\n", inode->i_count);
+ return;
+ }
+ if (inode->i_nlink) {
+ printk("sysv_free_inode: inode has nlink=%d\n", inode->i_nlink);
+ return;
+ }
+ if (!(sb = inode->i_sb)) {
+ printk("sysv_free_inode: inode on nonexistent device\n");
+ return;
+ }
+ ino = inode->i_ino;
+ if (ino <= SYSV_ROOT_INO || ino > sb->sv_ninodes) {
+ printk("sysv_free_inode: inode 0,1,2 or nonexistent inode\n");
+ return;
+ }
+ if (!(bh = sysv_bread(sb, inode->i_dev, sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits), &bh_data))) {
+ panic("sysv_free_inode: unable to read inode block"); /* FIXME: too severe? */
+ return;
+ }
+ raw_inode = (struct sysv_inode *) bh_data + ((ino-1) & sb->sv_inodes_per_block_1);
+ lock_super(sb);
+ if (*sb->sv_sb_fic_count < sb->sv_fic_size)
+ sb->sv_sb_fic_inodes[(*sb->sv_sb_fic_count)++] = ino;
+ (*sb->sv_sb_total_free_inodes)++;
+ sb->sv_bh->b_dirt = 1; /* super-block has been modified */
+ sb->s_dirt = 1; /* and needs time stamp */
+ memset(raw_inode, 0, sizeof(struct sysv_inode));
+ bh->b_dirt = 1;
+ unlock_super(sb);
+ brelse(bh);
+ clear_inode(inode);
+}
+
+struct inode * sysv_new_inode(const struct inode * dir)
+{
+ struct inode * inode;
+ struct super_block * sb;
+ struct buffer_head * bh;
+ char * bh_data;
+ struct sysv_inode * raw_inode;
+ int i,j,ino,block;
+
+ if (!dir || !(inode = get_empty_inode()))
+ return NULL;
+ sb = dir->i_sb;
+ inode->i_sb = sb;
+ inode->i_flags = inode->i_sb->s_flags;
+ lock_super(sb); /* protect against task switches */
+ if ((*sb->sv_sb_fic_count == 0)
+ || (sb->sv_sb_fic_inodes[(*sb->sv_sb_fic_count)-1] == 0) /* Applies only to SystemV2 FS */
+ ) {
+ /* Rebuild cache of free inodes: */
+ /* i : index into cache slot being filled */
+ /* ino : inode we are trying */
+ /* block : firstinodezone + (ino-1)/inodes_per_block */
+ /* j : (ino-1)%inodes_per_block */
+ /* bh : buffer for block */
+ /* raw_inode : pointer to inode ino in the block */
+ for (i = 0, ino = SYSV_ROOT_INO+1, block = sb->sv_firstinodezone, j = SYSV_ROOT_INO ; i < sb->sv_fic_size && block < sb->sv_firstdatazone ; block++, j = 0) {
+ if (!(bh = sysv_bread(sb, sb->s_dev, block, &bh_data))) {
+ printk("sysv_new_inode: unable to read inode table\n");
+ break; /* go with what we've got */
+ /* FIXME: Perhaps try the next block? */
+ }
+ raw_inode = (struct sysv_inode *) bh_data + j;
+ for (; j < sb->sv_inodes_per_block && i < sb->sv_fic_size; ino++, j++, raw_inode++) {
+ if (raw_inode->i_mode == 0 && raw_inode->i_nlink == 0)
+ sb->sv_sb_fic_inodes[i++] = ino;
+ }
+ brelse(bh);
+ }
+ if (i == 0) {
+ iput(inode);
+ unlock_super(sb);
+ return NULL; /* no inodes available */
+ }
+ *sb->sv_sb_fic_count = i;
+ }
+ /* Now *sb->sv_sb_fic_count > 0. */
+ ino = sb->sv_sb_fic_inodes[--(*sb->sv_sb_fic_count)];
+ sb->sv_bh->b_dirt = 1; /* super-block has been modified */
+ sb->s_dirt = 1; /* and needs time stamp */
+ inode->i_count = 1;
+ inode->i_nlink = 1;
+ inode->i_dev = sb->s_dev;
+ inode->i_uid = current->euid;
+ inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->egid;
+ inode->i_dirt = 1;
+ inode->i_ino = ino;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_op = NULL;
+ inode->i_blocks = inode->i_blksize = 0;
+ inode->u.sysv_i.i_lock = 0; inode->u.sysv_i.i_wait = NULL;
+ insert_inode_hash(inode);
+ /* Change directory entry: */
+ inode->i_mode = 0; /* for sysv_write_inode() */
+ inode->i_size = 0; /* ditto */
+ sysv_write_inode(inode); /* ensure inode not allocated again */
+ /* FIXME: caller may call this too. */
+ inode->i_dirt = 1; /* cleared by sysv_write_inode() */
+ /* That's it. */
+ (*sb->sv_sb_total_free_inodes)--;
+ sb->sv_bh->b_dirt = 1; /* super-block has been modified again */
+ sb->s_dirt = 1; /* and needs time stamp again */
+ unlock_super(sb);
+ return inode;
+}
+
+unsigned long sysv_count_free_inodes(struct super_block * sb)
+{
+#if 1 /* test */
+ struct buffer_head * bh;
+ char * bh_data;
+ struct sysv_inode * raw_inode;
+ int j,block,count;
+
+ /* this causes a lot of disk traffic ... */
+ count = 0;
+ lock_super(sb);
+ /* i : index into cache slot being filled */
+ /* ino : inode we are trying */
+ /* block : firstinodezone + (ino-1)/inodes_per_block */
+ /* j : (ino-1)%inodes_per_block */
+ /* bh : buffer for block */
+ /* raw_inode : pointer to inode ino in the block */
+ for (block = sb->sv_firstinodezone, j = SYSV_ROOT_INO ; block < sb->sv_firstdatazone ; block++, j = 0) {
+ if (!(bh = sysv_bread(sb, sb->s_dev, block, &bh_data))) {
+ printk("sysv_count_free_inodes: unable to read inode table\n");
+ break; /* go with what we've got */
+ /* FIXME: Perhaps try the next block? */
+ }
+ raw_inode = (struct sysv_inode *) bh_data + j;
+ for (; j < sb->sv_inodes_per_block ; j++, raw_inode++)
+ if (raw_inode->i_mode == 0 && raw_inode->i_nlink == 0)
+ count++;
+ brelse(bh);
+ }
+ if (count != *sb->sv_sb_total_free_inodes) {
+ printk("sysv_count_free_inodes: free inode count was %d, correcting to %d\n",(short)(*sb->sv_sb_total_free_inodes),count);
+ if (!(sb->s_flags & MS_RDONLY)) {
+ *sb->sv_sb_total_free_inodes = count;
+ sb->sv_bh->b_dirt = 1; /* super-block has been modified */
+ sb->s_dirt = 1; /* and needs time stamp */
+ }
+ }
+ unlock_super(sb);
+ return count;
+#else
+ return *sb->sv_sb_total_free_inodes;
+#endif
+}
+
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
new file mode 100644
index 0000000..4e2af91
--- /dev/null
+++ b/fs/sysv/inode.c
@@ -0,0 +1,808 @@
+/*
+ * linux/fs/sysv/inode.c
+ *
+ * minix/inode.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * xenix/inode.c
+ * Copyright (C) 1992 Doug Evans
+ *
+ * coh/inode.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/inode.c
+ * Copyright (C) 1993 Paul B. Monday
+ *
+ * sysv/inode.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * This file contains code for allocating/freeing inodes and for read/writing
+ * the superblock.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+#include <asm/segment.h>
+
+void _coh_wait_on_inode (struct inode * inode)
+{
+ struct wait_queue wait = { current, NULL };
+
+ add_wait_queue(&inode->u.sysv_i.i_wait, &wait);
+repeat:
+ current->state = TASK_UNINTERRUPTIBLE;
+ if (inode->u.sysv_i.i_lock) {
+ schedule();
+ goto repeat;
+ }
+ remove_wait_queue(&inode->u.sysv_i.i_wait, &wait);
+ current->state = TASK_RUNNING;
+}
+
+void sysv_put_inode(struct inode *inode)
+{
+ if (inode->i_nlink)
+ return;
+ inode->i_size = 0;
+ sysv_truncate(inode);
+ sysv_free_inode(inode);
+}
+
+
+static struct super_operations sysv_sops = {
+ sysv_read_inode,
+ sysv_notify_change,
+ sysv_write_inode,
+ sysv_put_inode,
+ sysv_put_super,
+ sysv_write_super,
+ sysv_statfs,
+ NULL
+};
+
+/* The following functions try to recognize specific filesystems.
+ * We recognize:
+ * - Xenix FS by its magic number.
+ * - SystemV FS by its magic number.
+ * - Coherent FS by its funny fname/fpack field.
+ * We discriminate among SystemV4 and SystemV2 FS by the assumption that
+ * the time stamp is not < 01-01-1980.
+ */
+
+static void detected_bs512 (struct super_block *sb)
+{
+ sb->sv_block_size = 512;
+ sb->sv_block_size_1 = 512-1;
+ sb->sv_block_size_bits = 9;
+ sb->sv_block_size_ratio = 2;
+ sb->sv_block_size_ratio_1 = 2-1;
+ sb->sv_block_size_ratio_bits = 1;
+ sb->sv_inodes_per_block = 512/64;
+ sb->sv_inodes_per_block_1 = 512/64-1;
+ sb->sv_inodes_per_block_bits = 9-6;
+ sb->sv_toobig_block = 10 +
+ (sb->sv_ind_per_block = 512/4) +
+ (sb->sv_ind_per_block_2 = (512/4)*(512/4)) +
+ (sb->sv_ind_per_block_3 = (512/4)*(512/4)*(512/4));
+ sb->sv_ind_per_block_1 = 512/4-1;
+ sb->sv_ind_per_block_2_1 = (512/4)*(512/4)-1;
+ sb->sv_ind_per_block_2_bits = 2 *
+ (sb->sv_ind_per_block_bits = 9-2);
+}
+
+static void detected_bs1024 (struct super_block *sb)
+{
+ sb->sv_block_size = 1024;
+ sb->sv_block_size_1 = 1024-1;
+ sb->sv_block_size_bits = 10;
+ sb->sv_block_size_ratio = 1;
+ sb->sv_block_size_ratio_1 = 1-1;
+ sb->sv_block_size_ratio_bits = 0;
+ sb->sv_inodes_per_block = 1024/64;
+ sb->sv_inodes_per_block_1 = 1024/64-1;
+ sb->sv_inodes_per_block_bits = 10-6;
+ sb->sv_toobig_block = 10 +
+ (sb->sv_ind_per_block = 1024/4) +
+ (sb->sv_ind_per_block_2 = (1024/4)*(1024/4)) +
+ (sb->sv_ind_per_block_3 = (1024/4)*(1024/4)*(1024/4));
+ sb->sv_ind_per_block_1 = 1024/4-1;
+ sb->sv_ind_per_block_2_1 = (1024/4)*(1024/4)-1;
+ sb->sv_ind_per_block_2_bits = 2 *
+ (sb->sv_ind_per_block_bits = 10-2);
+}
+
+static const char* detect_xenix (struct super_block *sb, struct buffer_head *bh)
+{
+ struct xenix_super_block * sbd;
+
+ sbd = (struct xenix_super_block *) bh->b_data;
+ if (sbd->s_magic != 0x2b5544)
+ return NULL;
+ sb->sv_type = FSTYPE_XENIX;
+ sb->sv_convert = 0;
+ sb->sv_kludge_symlinks = 1;
+ sb->sv_truncate = 1;
+ sb->sv_link_max = XENIX_LINK_MAX;
+ sb->sv_fic_size = XENIX_NICINOD;
+ sb->sv_flc_size = XENIX_NICFREE;
+ sb->sv_bh = bh;
+ sb->sv_sbd = (char *) sbd;
+ sb->sv_sb_fic_count = &sbd->s_ninode;
+ sb->sv_sb_fic_inodes = &sbd->s_inode[0];
+ sb->sv_sb_total_free_inodes = &sbd->s_tinode;
+ sb->sv_sb_flc_count = &sbd->s_nfree;
+ sb->sv_sb_flc_blocks = &sbd->s_free[0];
+ sb->sv_sb_total_free_blocks = &sbd->s_tfree;
+ sb->sv_sb_time = &sbd->s_time;
+ sb->sv_block_base = 0;
+ sb->sv_firstinodezone = 2;
+ sb->sv_firstdatazone = sbd->s_isize;
+ sb->sv_nzones = sbd->s_fsize;
+ sb->sv_ndatazones = sb->sv_nzones - sb->sv_firstdatazone;
+ switch (sbd->s_type) {
+ case 1: detected_bs512(sb); break;
+ case 2: detected_bs1024(sb); break;
+ default: return NULL;
+ }
+ return "Xenix";
+}
+
+static const char* detect_sysv4 (struct super_block *sb, struct buffer_head *bh)
+{
+ struct sysv4_super_block * sbd;
+
+ sbd = (struct sysv4_super_block *) (bh->b_data + BLOCK_SIZE/2);
+ if (sbd->s_magic != 0xfd187e20)
+ return NULL;
+ if (sbd->s_time < 315532800) /* this is likely to happen on SystemV2 FS */
+ return NULL;
+ sb->sv_type = FSTYPE_SYSV4;
+ sb->sv_convert = 0;
+ sb->sv_kludge_symlinks = 0; /* ?? */
+ sb->sv_truncate = 1;
+ sb->sv_link_max = SYSV_LINK_MAX;
+ sb->sv_fic_size = SYSV_NICINOD;
+ sb->sv_flc_size = SYSV_NICFREE;
+ sb->sv_bh = bh;
+ sb->sv_sbd = (char *) sbd;
+ sb->sv_sb_fic_count = &sbd->s_ninode;
+ sb->sv_sb_fic_inodes = &sbd->s_inode[0];
+ sb->sv_sb_total_free_inodes = &sbd->s_tinode;
+ sb->sv_sb_flc_count = &sbd->s_nfree;
+ sb->sv_sb_flc_blocks = &sbd->s_free[0];
+ sb->sv_sb_total_free_blocks = &sbd->s_tfree;
+ sb->sv_sb_time = &sbd->s_time;
+ sb->sv_block_base = 0;
+ sb->sv_firstinodezone = 2;
+ sb->sv_firstdatazone = sbd->s_isize;
+ sb->sv_nzones = sbd->s_fsize;
+ sb->sv_ndatazones = sb->sv_nzones - sb->sv_firstdatazone;
+ switch (sbd->s_type) {
+ case 1: detected_bs512(sb); break;
+ case 2: detected_bs1024(sb); break;
+ default: return NULL;
+ }
+ return "SystemV";
+}
+
+static const char* detect_sysv2 (struct super_block *sb, struct buffer_head *bh)
+{
+ struct sysv2_super_block * sbd;
+
+ sbd = (struct sysv2_super_block *) (bh->b_data + BLOCK_SIZE/2);
+ if (sbd->s_magic != 0xfd187e20)
+ return NULL;
+ if (sbd->s_time < 315532800) /* this is likely to happen on SystemV4 FS */
+ return NULL;
+ sb->sv_type = FSTYPE_SYSV2;
+ sb->sv_convert = 0;
+ sb->sv_kludge_symlinks = 0; /* ?? */
+ sb->sv_truncate = 1;
+ sb->sv_link_max = SYSV_LINK_MAX;
+ sb->sv_fic_size = SYSV_NICINOD;
+ sb->sv_flc_size = SYSV_NICFREE;
+ sb->sv_bh = bh;
+ sb->sv_sbd = (char *) sbd;
+ sb->sv_sb_fic_count = &sbd->s_ninode;
+ sb->sv_sb_fic_inodes = &sbd->s_inode[0];
+ sb->sv_sb_total_free_inodes = &sbd->s_tinode;
+ sb->sv_sb_flc_count = &sbd->s_nfree;
+ sb->sv_sb_flc_blocks = &sbd->s_free[0];
+ sb->sv_sb_total_free_blocks = &sbd->s_tfree;
+ sb->sv_sb_time = &sbd->s_time;
+ sb->sv_block_base = 0;
+ sb->sv_firstinodezone = 2;
+ sb->sv_firstdatazone = sbd->s_isize;
+ sb->sv_nzones = sbd->s_fsize;
+ sb->sv_ndatazones = sb->sv_nzones - sb->sv_firstdatazone;
+ switch (sbd->s_type) {
+ case 1: detected_bs512(sb); break;
+ case 2: detected_bs1024(sb); break;
+ default: return NULL;
+ }
+ return "SystemV Release 2";
+}
+
+static const char* detect_coherent (struct super_block *sb, struct buffer_head *bh)
+{
+ struct coh_super_block * sbd;
+
+ sbd = (struct coh_super_block *) (bh->b_data + BLOCK_SIZE/2);
+ if ((memcmp(sbd->s_fname,"noname",6) && memcmp(sbd->s_fname,"xxxxx ",6))
+ || (memcmp(sbd->s_fpack,"nopack",6) && memcmp(sbd->s_fpack,"xxxxx\n",6)))
+ return NULL;
+ sb->sv_type = FSTYPE_COH;
+ sb->sv_convert = 1;
+ sb->sv_kludge_symlinks = 1;
+ sb->sv_truncate = 1;
+ sb->sv_link_max = COH_LINK_MAX;
+ sb->sv_fic_size = COH_NICINOD;
+ sb->sv_flc_size = COH_NICFREE;
+ sb->sv_bh = bh;
+ sb->sv_sbd = (char *) sbd;
+ sb->sv_sb_fic_count = &sbd->s_ninode;
+ sb->sv_sb_fic_inodes = &sbd->s_inode[0];
+ sb->sv_sb_total_free_inodes = &sbd->s_tinode;
+ sb->sv_sb_flc_count = &sbd->s_nfree;
+ sb->sv_sb_flc_blocks = &sbd->s_free[0];
+ sb->sv_sb_total_free_blocks = &sbd->s_tfree;
+ sb->sv_sb_time = &sbd->s_time;
+ sb->sv_block_base = 0;
+ sb->sv_firstinodezone = 2;
+ sb->sv_firstdatazone = sbd->s_isize;
+ sb->sv_nzones = from_coh_ulong(sbd->s_fsize);
+ sb->sv_ndatazones = sb->sv_nzones - sb->sv_firstdatazone;
+ detected_bs512(sb);
+ return "Coherent";
+}
+
+struct super_block *sysv_read_super(struct super_block *sb,void *data,
+ int silent)
+{
+ struct buffer_head *bh;
+ const char *found;
+ int dev = sb->s_dev;
+
+ if (1024 != sizeof (struct xenix_super_block))
+ panic("Xenix FS: bad super-block size");
+ if ((512 != sizeof (struct sysv4_super_block))
+ || (512 != sizeof (struct sysv2_super_block)))
+ panic("SystemV FS: bad super-block size");
+ if (500 != sizeof (struct coh_super_block))
+ panic("Coherent FS: bad super-block size");
+ if (64 != sizeof (struct sysv_inode))
+ panic("sysv fs: bad i-node size");
+ lock_super(sb);
+ sb->s_blocksize = BLOCK_SIZE; /* anything else not supported by the block device drivers */
+ sb->s_blocksize_bits = BLOCK_SIZE_BITS;
+
+ /* Try to read Xenix superblock */
+ if ((bh = bread(dev, 1, BLOCK_SIZE)) != NULL) {
+ if ((found = detect_xenix(sb,bh)) != NULL)
+ goto ok;
+ brelse(bh);
+ }
+ if ((bh = bread(dev, 0, BLOCK_SIZE)) != NULL) {
+ /* Try to recognize SystemV superblock */
+ if ((found = detect_sysv4(sb,bh)) != NULL)
+ goto ok;
+ if ((found = detect_sysv2(sb,bh)) != NULL)
+ goto ok;
+ /* Try to recognize Coherent superblock */
+ if ((found = detect_coherent(sb,bh)) != NULL)
+ goto ok;
+ brelse(bh);
+ }
+ /* Try to recognize SystemV2 superblock */
+ /* Offset by 1 track, i.e. most probably 9, 15, or 18 kilobytes. */
+ { static int offsets[] = { 9, 15, 18, };
+ int i;
+ for (i = 0; i < sizeof(offsets)/sizeof(offsets[0]); i++)
+ if ((bh = bread(dev, offsets[i], BLOCK_SIZE)) != NULL) {
+ /* Try to recognize SystemV superblock */
+ if ((found = detect_sysv2(sb,bh)) != NULL) {
+ sb->sv_block_base = offsets[i];
+ goto ok;
+ }
+ brelse(bh);
+ }
+ }
+ sb->s_dev=0;
+ unlock_super(sb);
+ if (!silent)
+ printk("VFS: unable to read Xenix/SystemV/Coherent superblock\n");
+ return NULL;
+
+ ok:
+ sb->sv_ninodes = (sb->sv_firstdatazone - sb->sv_firstinodezone) << sb->sv_inodes_per_block_bits;
+ if (!silent)
+ printk("VFS: Found a %s FS (block size = %d) on device %d/%d\n",found,sb->sv_block_size,MAJOR(dev),MINOR(dev));
+ sb->s_magic = SYSV_MAGIC_BASE + sb->sv_type;
+ /* set up enough so that it can read an inode */
+ sb->s_dev = dev;
+ sb->s_op = &sysv_sops;
+ sb->s_mounted = iget(sb,SYSV_ROOT_INO);
+ unlock_super(sb);
+ if (!sb->s_mounted) {
+ brelse(bh);
+ sb->s_dev = 0;
+ printk("SysV FS: get root inode failed\n");
+ return NULL;
+ }
+ sb->s_dirt = 1; /* brelse(bh); occurs when the disk is unmounted. */
+ return sb;
+}
+
+/* This is only called on sync() and umount(), when s_dirt=1. */
+void sysv_write_super (struct super_block *sb)
+{
+ lock_super(sb);
+ if (sb->sv_bh->b_dirt) {
+ /* If we are going to write out the super block,
+ then attach current time stamp. */
+ unsigned long time = CURRENT_TIME;
+ if (sb->sv_convert)
+ time = to_coh_ulong(time);
+ *sb->sv_sb_time = time;
+ }
+ sb->s_dirt = 0;
+ unlock_super(sb);
+}
+
+void sysv_put_super(struct super_block *sb)
+{
+ /* we can assume sysv_write_super() has already been called */
+ lock_super(sb);
+ brelse(sb->sv_bh);
+ sb->s_dev = 0;
+ unlock_super(sb);
+}
+
+void sysv_statfs(struct super_block *sb, struct statfs *buf)
+{
+ long tmp;
+
+ put_fs_long(sb->s_magic, &buf->f_type); /* type of filesystem */
+ put_fs_long(sb->sv_block_size, &buf->f_bsize); /* block size */
+ put_fs_long(sb->sv_ndatazones, &buf->f_blocks); /* total data blocks in file system */
+ tmp = sysv_count_free_blocks(sb);
+ put_fs_long(tmp, &buf->f_bfree); /* free blocks in fs */
+ put_fs_long(tmp, &buf->f_bavail); /* free blocks available to non-superuser */
+ put_fs_long(sb->sv_ninodes, &buf->f_files); /* total file nodes in file system */
+ put_fs_long(sysv_count_free_inodes(sb), &buf->f_ffree); /* free file nodes in fs */
+ put_fs_long(SYSV_NAMELEN, &buf->f_namelen);
+ /* Don't know what value to put in buf->f_fsid */ /* file system id */
+}
+
+
+/* bmap support for running executables and shared libraries.
+ Used only if block_size = BLOCK_SIZE. */
+
+#define IND_PER_BLOCK (BLOCK_SIZE / sizeof(sysv_zone_t))
+
+static inline int inode_bmap(struct super_block * sb, struct inode * inode, int nr)
+{
+ int tmp = inode->u.sysv_i.i_data[nr];
+ if (!tmp)
+ return 0;
+ return tmp + sb->sv_block_base;
+}
+
+static int block_bmap(struct super_block * sb, struct buffer_head * bh, int nr, int convert)
+{
+ int tmp;
+
+ if (!bh)
+ return 0;
+ tmp = ((sysv_zone_t *) bh->b_data) [nr];
+ if (convert)
+ tmp = from_coh_ulong(tmp);
+ brelse(bh);
+ if (!tmp)
+ return 0;
+ return tmp + sb->sv_block_base;
+}
+
+int sysv_bmap(struct inode * inode,int block_nr)
+{
+ unsigned int block = block_nr;
+ struct super_block * sb = inode->i_sb;
+ int convert;
+ int i;
+ struct buffer_head * bh;
+
+ if (block < 10)
+ return inode_bmap(sb,inode,block);
+ block -= 10;
+ convert = sb->sv_convert;
+ if (block < IND_PER_BLOCK) {
+ i = inode_bmap(sb,inode,10);
+ if (!i)
+ return 0;
+ bh = bread(inode->i_dev,i,BLOCK_SIZE);
+ return block_bmap(sb,bh,block,convert);
+ }
+ block -= IND_PER_BLOCK;
+ if (block < IND_PER_BLOCK*IND_PER_BLOCK) {
+ i = inode_bmap(sb,inode,11);
+ if (!i)
+ return 0;
+ bh = bread(inode->i_dev,i,BLOCK_SIZE);
+ i = block_bmap(sb,bh,block/IND_PER_BLOCK,convert);
+ if (!i)
+ return 0;
+ bh = bread(inode->i_dev,i,BLOCK_SIZE);
+ return block_bmap(sb,bh,block%IND_PER_BLOCK,convert);
+ }
+ block -= IND_PER_BLOCK*IND_PER_BLOCK;
+ if (block < IND_PER_BLOCK*IND_PER_BLOCK*IND_PER_BLOCK) {
+ i = inode_bmap(sb,inode,12);
+ if (!i)
+ return 0;
+ bh = bread(inode->i_dev,i,BLOCK_SIZE);
+ i = block_bmap(sb,bh,block/(IND_PER_BLOCK*IND_PER_BLOCK),convert);
+ if (!i)
+ return 0;
+ bh = bread(inode->i_dev,i,BLOCK_SIZE);
+ i = block_bmap(sb,bh,(block/IND_PER_BLOCK)%IND_PER_BLOCK,convert);
+ if (!i)
+ return 0;
+ bh = bread(inode->i_dev,i,BLOCK_SIZE);
+ return block_bmap(sb,bh,block%IND_PER_BLOCK,convert);
+ }
+ if ((int)block<0) {
+ printk("sysv_bmap: block<0");
+ return 0;
+ }
+ printk("sysv_bmap: block>big");
+ return 0;
+}
+
+/* End of bmap support. */
+
+
+/* Access selected blocks of regular files (or directories) */
+
+static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create, char * *start)
+{
+ struct super_block *sb;
+ unsigned long tmp;
+ unsigned long *p;
+ struct buffer_head * result;
+
+ sb = inode->i_sb;
+ p = inode->u.sysv_i.i_data + nr;
+repeat:
+ tmp = *p;
+ if (tmp) {
+ result = getblk(inode->i_dev, (tmp >> sb->sv_block_size_ratio_bits) + sb->sv_block_base, BLOCK_SIZE);
+ if (tmp == *p) {
+ *start = result->b_data + ((tmp & sb->sv_block_size_ratio_1) << sb->sv_block_size_bits);
+ return result;
+ }
+ brelse(result);
+ goto repeat;
+ }
+ if (!create)
+ return NULL;
+ tmp = sysv_new_block(sb);
+ if (!tmp)
+ return NULL;
+ result = getblk(inode->i_dev, (tmp >> sb->sv_block_size_ratio_bits) + sb->sv_block_base, BLOCK_SIZE);
+ if (*p) {
+ sysv_free_block(sb,tmp);
+ brelse(result);
+ goto repeat;
+ }
+ *p = tmp;
+ inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ *start = result->b_data + ((tmp & sb->sv_block_size_ratio_1) << sb->sv_block_size_bits);
+ return result;
+}
+
+static struct buffer_head * block_getblk(struct inode * inode,
+ struct buffer_head * bh, int nr, int create, char * *start)
+{
+ struct super_block *sb;
+ unsigned long tmp, block;
+ sysv_zone_t *p;
+ struct buffer_head * result;
+
+ if (!bh)
+ return NULL;
+ if (!bh->b_uptodate) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!bh->b_uptodate) {
+ brelse(bh);
+ return NULL;
+ }
+ }
+ sb = inode->i_sb;
+ p = nr + (sysv_zone_t *) *start;
+repeat:
+ block = tmp = *p;
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (tmp) {
+ result = getblk(bh->b_dev, (block >> sb->sv_block_size_ratio_bits) + sb->sv_block_base, BLOCK_SIZE);
+ if (tmp == *p) {
+ brelse(bh);
+ *start = result->b_data + ((block & sb->sv_block_size_ratio_1) << sb->sv_block_size_bits);
+ return result;
+ }
+ brelse(result);
+ goto repeat;
+ }
+ if (!create) {
+ brelse(bh);
+ return NULL;
+ }
+ block = sysv_new_block(sb);
+ if (!block) {
+ brelse(bh);
+ return NULL;
+ }
+ result = getblk(bh->b_dev, (block >> sb->sv_block_size_ratio_bits) + sb->sv_block_base, BLOCK_SIZE);
+ if (*p) {
+ sysv_free_block(sb,block);
+ brelse(result);
+ goto repeat;
+ }
+ *p = (sb->sv_convert ? to_coh_ulong(block) : block);
+ bh->b_dirt = 1;
+ brelse(bh);
+ *start = result->b_data + ((block & sb->sv_block_size_ratio_1) << sb->sv_block_size_bits);
+ return result;
+}
+
+struct buffer_head * sysv_getblk(struct inode * inode, unsigned int block,
+ int create, char * *start)
+{
+ struct super_block * sb = inode->i_sb;
+ struct buffer_head * bh;
+
+ if (block < 10)
+ return inode_getblk(inode,block,create,start);
+ block -= 10;
+ if (block < sb->sv_ind_per_block) {
+ bh = inode_getblk(inode,10,create,start);
+ return block_getblk(inode, bh, block, create, start);
+ }
+ block -= sb->sv_ind_per_block;
+ if (block < sb->sv_ind_per_block_2) {
+ bh = inode_getblk(inode,11,create,start);
+ bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_bits, create, start);
+ return block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create, start);
+ }
+ block -= sb->sv_ind_per_block_2;
+ if (block < sb->sv_ind_per_block_3) {
+ bh = inode_getblk(inode,12,create,start);
+ bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_2_bits, create, start);
+ bh = block_getblk(inode, bh, (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1, create, start);
+ return block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create, start);
+ }
+ if ((int)block<0) {
+ printk("sysv_getblk: block<0");
+ return NULL;
+ }
+ printk("sysv_getblk: block>big");
+ return NULL;
+}
+
+struct buffer_head * sysv_file_bread(struct inode * inode, int block, int create, char * *start)
+{
+ struct buffer_head * bh;
+
+ bh = sysv_getblk(inode,block,create,start);
+ if (!bh || bh->b_uptodate)
+ return bh;
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (bh->b_uptodate)
+ return bh;
+ brelse(bh);
+ return NULL;
+}
+
+
+static inline unsigned long read3byte (char * p)
+{
+ return (unsigned long)(*(unsigned short *)p)
+ | (unsigned long)(*(unsigned char *)(p+2)) << 16;
+}
+
+static inline void write3byte (char * p, unsigned long val)
+{
+ *(unsigned short *)p = (unsigned short) val;
+ *(unsigned char *)(p+2) = val >> 16;
+}
+
+static inline unsigned long coh_read3byte (char * p)
+{
+ return (unsigned long)(*(unsigned char *)p) << 16
+ | (unsigned long)(*(unsigned short *)(p+1));
+}
+
+static inline void coh_write3byte (char * p, unsigned long val)
+{
+ *(unsigned char *)p = val >> 16;
+ *(unsigned short *)(p+1) = (unsigned short) val;
+}
+
+void sysv_read_inode(struct inode * inode)
+{
+ struct super_block * sb = inode->i_sb;
+ struct buffer_head * bh;
+ char * bh_data;
+ struct sysv_inode * raw_inode;
+ unsigned int block, ino;
+ umode_t mode;
+
+ ino = inode->i_ino;
+ inode->i_op = NULL;
+ inode->i_mode = 0;
+ if (!ino || ino > sb->sv_ninodes) {
+ printk("Bad inode number on dev 0x%04x: %d is out of range\n",
+ inode->i_dev, ino);
+ return;
+ }
+ block = sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits);
+ if (!(bh=sysv_bread(sb,inode->i_dev,block,&bh_data))) {
+ printk("Major problem: unable to read inode from dev 0x%04x\n",
+ inode->i_dev);
+ return;
+ }
+ raw_inode = (struct sysv_inode *) bh_data + ((ino-1) & sb->sv_inodes_per_block_1);
+ mode = raw_inode->i_mode;
+ if (sb->sv_kludge_symlinks)
+ mode = from_coh_imode(mode);
+ /* SystemV FS: kludge permissions if ino==SYSV_ROOT_INO ?? */
+ inode->i_mode = mode;
+ inode->i_uid = raw_inode->i_uid;
+ inode->i_gid = raw_inode->i_gid;
+ inode->i_nlink = raw_inode->i_nlink;
+ if (sb->sv_convert) {
+ inode->i_size = from_coh_ulong(raw_inode->i_size);
+ inode->i_atime = from_coh_ulong(raw_inode->i_atime);
+ inode->i_mtime = from_coh_ulong(raw_inode->i_mtime);
+ inode->i_ctime = from_coh_ulong(raw_inode->i_ctime);
+ } else {
+ inode->i_size = raw_inode->i_size;
+ inode->i_atime = raw_inode->i_atime;
+ inode->i_mtime = raw_inode->i_mtime;
+ inode->i_ctime = raw_inode->i_ctime;
+ }
+ inode->i_blocks = inode->i_blksize = 0;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ inode->i_rdev = raw_inode->i_a.i_rdev;
+ else
+ if (sb->sv_convert)
+ for (block = 0; block < 10+1+1+1; block++)
+ inode->u.sysv_i.i_data[block] =
+ coh_read3byte(&raw_inode->i_a.i_addb[3*block]);
+ else
+ for (block = 0; block < 10+1+1+1; block++)
+ inode->u.sysv_i.i_data[block] =
+ read3byte(&raw_inode->i_a.i_addb[3*block]);
+ brelse(bh);
+ if (S_ISREG(inode->i_mode))
+ if (sb->sv_block_size_ratio_bits == 0) /* block_size == BLOCK_SIZE ? */
+ inode->i_op = &sysv_file_inode_operations_with_bmap;
+ else
+ inode->i_op = &sysv_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &sysv_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &sysv_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &blkdev_inode_operations;
+ else if (S_ISFIFO(inode->i_mode))
+ init_fifo(inode);
+}
+
+/* To avoid inconsistencies between inodes in memory and inodes on disk. */
+extern int sysv_notify_change(int flags, struct inode *inode)
+{
+ if (flags & NOTIFY_MODE)
+ if (inode->i_sb->sv_kludge_symlinks)
+ if (inode->i_mode == COH_KLUDGE_SYMLINK_MODE) {
+ inode->i_mode = COH_KLUDGE_NOT_SYMLINK;
+ inode->i_dirt = 1;
+ }
+ return 0;
+}
+
+static struct buffer_head * sysv_update_inode(struct inode * inode)
+{
+ struct super_block * sb = inode->i_sb;
+ struct buffer_head * bh;
+ char * bh_data;
+ struct sysv_inode * raw_inode;
+ unsigned int ino, block;
+ umode_t mode;
+
+ ino = inode->i_ino;
+ if (!ino || ino > sb->sv_ninodes) {
+ printk("Bad inode number on dev 0x%04x: %d is out of range\n",
+ inode->i_dev, ino);
+ inode->i_dirt = 0;
+ return 0;
+ }
+ block = sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits);
+ if (!(bh = sysv_bread(sb,inode->i_dev,block,&bh_data))) {
+ printk("unable to read i-node block\n");
+ inode->i_dirt = 0;
+ return 0;
+ }
+ raw_inode = (struct sysv_inode *) bh_data + ((ino-1) & sb->sv_inodes_per_block_1);
+ mode = inode->i_mode;
+ if (sb->sv_kludge_symlinks)
+ mode = to_coh_imode(mode);
+ raw_inode->i_mode = mode;
+ raw_inode->i_uid = inode->i_uid;
+ raw_inode->i_gid = inode->i_gid;
+ raw_inode->i_nlink = inode->i_nlink;
+ if (sb->sv_convert) {
+ raw_inode->i_size = to_coh_ulong(inode->i_size);
+ raw_inode->i_atime = to_coh_ulong(inode->i_atime);
+ raw_inode->i_mtime = to_coh_ulong(inode->i_mtime);
+ raw_inode->i_ctime = to_coh_ulong(inode->i_ctime);
+ } else {
+ raw_inode->i_size = inode->i_size;
+ raw_inode->i_atime = inode->i_atime;
+ raw_inode->i_mtime = inode->i_mtime;
+ raw_inode->i_ctime = inode->i_ctime;
+ }
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ raw_inode->i_a.i_rdev = inode->i_rdev; /* write 2 or 3 bytes ?? */
+ else
+ if (sb->sv_convert)
+ for (block = 0; block < 10+1+1+1; block++)
+ coh_write3byte(&raw_inode->i_a.i_addb[3*block],inode->u.sysv_i.i_data[block]);
+ else
+ for (block = 0; block < 10+1+1+1; block++)
+ write3byte(&raw_inode->i_a.i_addb[3*block],inode->u.sysv_i.i_data[block]);
+ inode->i_dirt=0;
+ bh->b_dirt=1;
+ return bh;
+}
+
+void sysv_write_inode(struct inode * inode)
+{
+ struct buffer_head *bh;
+ bh = sysv_update_inode(inode);
+ brelse(bh);
+}
+
+int sysv_sync_inode(struct inode * inode)
+{
+ int err = 0;
+ struct buffer_head *bh;
+
+ bh = sysv_update_inode(inode);
+ if (bh && bh->b_dirt) {
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ if (bh->b_req && !bh->b_uptodate)
+ {
+ printk ("IO error syncing sysv inode [%04x:%08lx]\n",
+ inode->i_dev, inode->i_ino);
+ err = -1;
+ }
+ }
+ else if (!bh)
+ err = -1;
+ brelse (bh);
+ return err;
+}
+
diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c
new file mode 100644
index 0000000..e0e17cd
--- /dev/null
+++ b/fs/sysv/namei.c
@@ -0,0 +1,836 @@
+/*
+ * linux/fs/sysv/namei.c
+ *
+ * minix/namei.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * coh/namei.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/namei.c
+ * Copyright (C) 1993 Bruno Haible
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+
+/* compare strings: name[0..len-1] (not zero-terminated) and
+ * buffer[0..] (filled with zeroes up to buffer[0..maxlen-1])
+ */
+static inline int namecompare(int len, int maxlen,
+ const char * name, const char * buffer)
+{
+ if (len >= maxlen || !buffer[len]) {
+ unsigned char same;
+ __asm__("repe ; cmpsb ; setz %0"
+ :"=q" (same)
+ :"S" ((long) name),"D" ((long) buffer),"c" (len)
+ :"cx","di","si");
+ return same;
+ }
+ /* if (len<maxlen && buffer[len]) then buffer is longer than name */
+ return 0;
+}
+
+/*
+ * ok, we cannot use strncmp, as the name is not in our data space. [Now it is!]
+ * Thus we'll have to use sysv_match. No big problem. Match also makes
+ * some sanity tests.
+ *
+ * NOTE! unlike strncmp, sysv_match returns 1 for success, 0 for failure.
+ */
+static int sysv_match(int len, const char * name, struct sysv_dir_entry * de)
+{
+ if (!de->inode || len > SYSV_NAMELEN)
+ return 0;
+ /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
+ if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
+ return 1;
+ return namecompare(len,SYSV_NAMELEN,name,de->name);
+}
+
+/*
+ * sysv_find_entry()
+ *
+ * finds an entry in the specified directory with the wanted name. It
+ * returns the cache buffer in which the entry was found, and the entry
+ * itself (as a parameter - res_dir). It does NOT read the inode of the
+ * entry - you'll have to do that yourself if you want to.
+ */
+static struct buffer_head * sysv_find_entry(struct inode * dir,
+ const char * name, int namelen, struct sysv_dir_entry ** res_dir)
+{
+ struct super_block * sb;
+ unsigned long pos, block, offset; /* pos = block * block_size + offset */
+ struct buffer_head * bh;
+ char * bh_data;
+
+ *res_dir = NULL;
+ if (!dir)
+ return NULL;
+ sb = dir->i_sb;
+ if (namelen > SYSV_NAMELEN)
+ if (sb->sv_truncate)
+ namelen = SYSV_NAMELEN;
+ else
+ return NULL;
+ bh = NULL;
+ pos = block = offset = 0;
+ while (pos < dir->i_size) {
+ if (!bh) {
+ bh = sysv_file_bread(dir,block,0,&bh_data);
+ if (!bh) {
+ /* offset = 0; */ block++;
+ pos += sb->sv_block_size;
+ continue;
+ }
+ }
+ if (sysv_match(namelen, name,
+ *res_dir = (struct sysv_dir_entry *) (bh_data + offset) ))
+ return bh;
+ pos += SYSV_DIRSIZE;
+ offset += SYSV_DIRSIZE;
+ if (offset < sb->sv_block_size)
+ continue;
+ brelse(bh);
+ bh = NULL;
+ offset = 0; block++;
+ }
+ brelse(bh);
+ *res_dir = NULL;
+ return NULL;
+}
+
+int sysv_lookup(struct inode * dir,const char * name, int len,
+ struct inode ** result)
+{
+ int ino;
+ struct sysv_dir_entry * de;
+ struct buffer_head * bh;
+
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+ if (!S_ISDIR(dir->i_mode)) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!(bh = sysv_find_entry(dir,name,len,&de))) {
+ iput(dir);
+ return -ENOENT;
+ }
+ ino = de->inode;
+ brelse(bh);
+ if (!(*result = iget(dir->i_sb,ino))) {
+ iput(dir);
+ return -EACCES;
+ }
+ iput(dir);
+ return 0;
+}
+
+/*
+ * sysv_add_entry()
+ *
+ * adds a file entry to the specified directory, returning a possible
+ * error value if it fails.
+ *
+ * NOTE!! The inode part of 'de' is left at 0 - which means you
+ * may not sleep between calling this and putting something into
+ * the entry, as someone else might have used it while you slept.
+ */
+static int sysv_add_entry(struct inode * dir,
+ const char * name, int namelen,
+ struct buffer_head ** res_buf,
+ struct sysv_dir_entry ** res_dir)
+{
+ struct super_block * sb;
+ int i;
+ unsigned long pos, block, offset; /* pos = block * block_size + offset */
+ struct buffer_head * bh;
+ char * bh_data;
+ struct sysv_dir_entry * de;
+
+ *res_buf = NULL;
+ *res_dir = NULL;
+ if (!dir)
+ return -ENOENT;
+ sb = dir->i_sb;
+ if (namelen > SYSV_NAMELEN)
+ if (sb->sv_truncate)
+ namelen = SYSV_NAMELEN;
+ else
+ return -ENAMETOOLONG;
+ if (!namelen)
+ return -ENOENT;
+ bh = NULL;
+ pos = block = offset = 0;
+ while (1) {
+ if (!bh) {
+ bh = sysv_file_bread(dir,block,1,&bh_data);
+ if (!bh)
+ return -ENOSPC;
+ }
+ de = (struct sysv_dir_entry *) (bh_data + offset);
+ pos += SYSV_DIRSIZE;
+ offset += SYSV_DIRSIZE;
+ if (pos > dir->i_size) {
+ de->inode = 0;
+ dir->i_size = pos;
+ dir->i_dirt = 1;
+ }
+ if (de->inode) {
+ if (namecompare(namelen, SYSV_NAMELEN, name, de->name)) {
+ brelse(bh);
+ return -EEXIST;
+ }
+ } else {
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ for (i = 0; i < SYSV_NAMELEN ; i++)
+ de->name[i] = (i < namelen) ? name[i] : 0;
+ bh->b_dirt = 1;
+ *res_dir = de;
+ break;
+ }
+ if (offset < sb->sv_block_size)
+ continue;
+ brelse(bh);
+ bh = NULL;
+ offset = 0; block++;
+ }
+ *res_buf = bh;
+ return 0;
+}
+
+int sysv_create(struct inode * dir,const char * name, int len, int mode,
+ struct inode ** result)
+{
+ int error;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct sysv_dir_entry * de;
+
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+ inode = sysv_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ if (inode->i_sb->sv_block_size_ratio_bits == 0) /* block_size == BLOCK_SIZE ? */
+ inode->i_op = &sysv_file_inode_operations_with_bmap;
+ else
+ inode->i_op = &sysv_file_inode_operations;
+ inode->i_mode = mode;
+ inode->i_dirt = 1;
+ error = sysv_add_entry(dir,name,len, &bh ,&de);
+ if (error) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ iput(dir);
+ return error;
+ }
+ de->inode = inode->i_ino;
+ bh->b_dirt = 1;
+ brelse(bh);
+ iput(dir);
+ *result = inode;
+ return 0;
+}
+
+int sysv_mknod(struct inode * dir, const char * name, int len, int mode, int rdev)
+{
+ int error;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct sysv_dir_entry * de;
+
+ if (!dir)
+ return -ENOENT;
+ bh = sysv_find_entry(dir,name,len,&de);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ inode = sysv_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_uid = current->euid;
+ inode->i_mode = mode;
+ inode->i_op = NULL;
+ if (S_ISREG(inode->i_mode))
+ if (inode->i_sb->sv_block_size_ratio_bits == 0) /* block_size == BLOCK_SIZE ? */
+ inode->i_op = &sysv_file_inode_operations_with_bmap;
+ else
+ inode->i_op = &sysv_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &sysv_dir_inode_operations;
+ if (dir->i_mode & S_ISGID)
+ inode->i_mode |= S_ISGID;
+ }
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &sysv_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &blkdev_inode_operations;
+ else if (S_ISFIFO(inode->i_mode))
+ init_fifo(inode);
+ if (S_ISBLK(mode) || S_ISCHR(mode))
+ inode->i_rdev = rdev;
+ inode->i_dirt = 1;
+ error = sysv_add_entry(dir, name, len, &bh, &de);
+ if (error) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ iput(dir);
+ return error;
+ }
+ de->inode = inode->i_ino;
+ bh->b_dirt = 1;
+ brelse(bh);
+ iput(dir);
+ iput(inode);
+ return 0;
+}
+
+int sysv_mkdir(struct inode * dir, const char * name, int len, int mode)
+{
+ int error;
+ struct inode * inode;
+ struct buffer_head * bh, *dir_block;
+ char * bh_data;
+ struct sysv_dir_entry * de;
+
+ if (!dir) {
+ iput(dir);
+ return -EINVAL;
+ }
+ bh = sysv_find_entry(dir,name,len,&de);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ if (dir->i_nlink >= dir->i_sb->sv_link_max) {
+ iput(dir);
+ return -EMLINK;
+ }
+ inode = sysv_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_op = &sysv_dir_inode_operations;
+ inode->i_size = 2 * SYSV_DIRSIZE;
+ dir_block = sysv_file_bread(inode,0,1,&bh_data);
+ if (!dir_block) {
+ iput(dir);
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ return -ENOSPC;
+ }
+ de = (struct sysv_dir_entry *) (bh_data + 0*SYSV_DIRSIZE);
+ de->inode = inode->i_ino;
+ strcpy(de->name,"."); /* rest of de->name is zero, see sysv_new_block */
+ de = (struct sysv_dir_entry *) (bh_data + 1*SYSV_DIRSIZE);
+ de->inode = dir->i_ino;
+ strcpy(de->name,".."); /* rest of de->name is zero, see sysv_new_block */
+ inode->i_nlink = 2;
+ dir_block->b_dirt = 1;
+ brelse(dir_block);
+ inode->i_mode = S_IFDIR | (mode & 0777 & ~current->umask);
+ if (dir->i_mode & S_ISGID)
+ inode->i_mode |= S_ISGID;
+ inode->i_dirt = 1;
+ error = sysv_add_entry(dir, name, len, &bh, &de);
+ if (error) {
+ iput(dir);
+ inode->i_nlink=0;
+ iput(inode);
+ return error;
+ }
+ de->inode = inode->i_ino;
+ bh->b_dirt = 1;
+ dir->i_nlink++;
+ dir->i_dirt = 1;
+ iput(dir);
+ iput(inode);
+ brelse(bh);
+ return 0;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+static int empty_dir(struct inode * inode)
+{
+ struct super_block * sb;
+ unsigned long pos, block, offset; /* pos = block * block_size + offset */
+ struct buffer_head * bh;
+ char * bh_data;
+ struct sysv_dir_entry * de;
+
+ if (!inode)
+ return 1;
+ block = 0;
+ bh = NULL;
+ pos = offset = 2*SYSV_DIRSIZE;
+ if (inode->i_size % SYSV_DIRSIZE)
+ goto bad_dir;
+ if (inode->i_size < pos)
+ goto bad_dir;
+ bh = sysv_file_bread(inode,0,0,&bh_data);
+ if (!bh)
+ goto bad_dir;
+ de = (struct sysv_dir_entry *) (bh_data + 0*SYSV_DIRSIZE);
+ if (!de->inode || strcmp(de->name,"."))
+ goto bad_dir;
+ de = (struct sysv_dir_entry *) (bh_data + 1*SYSV_DIRSIZE);
+ if (!de->inode || strcmp(de->name,".."))
+ goto bad_dir;
+ sb = inode->i_sb;
+ while (pos < inode->i_size) {
+ if (!bh) {
+ bh = sysv_file_bread(inode,block,0,&bh_data);
+ if (!bh) {
+ /* offset = 0; */ block++;
+ pos += sb->sv_block_size;
+ continue;
+ }
+ }
+ de = (struct sysv_dir_entry *) (bh_data + offset);
+ pos += SYSV_DIRSIZE;
+ offset += SYSV_DIRSIZE;
+ if (de->inode) {
+ brelse(bh);
+ return 0;
+ }
+ if (offset < sb->sv_block_size)
+ continue;
+ brelse(bh);
+ bh = NULL;
+ offset = 0; block++;
+ }
+ brelse(bh);
+ return 1;
+bad_dir:
+ brelse(bh);
+ printk("Bad directory on device %04x\n",inode->i_dev);
+ return 1;
+}
+
+int sysv_rmdir(struct inode * dir, const char * name, int len)
+{
+ int retval;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct sysv_dir_entry * de;
+
+ inode = NULL;
+ bh = sysv_find_entry(dir,name,len,&de);
+ retval = -ENOENT;
+ if (!bh)
+ goto end_rmdir;
+ retval = -EPERM;
+ if (!(inode = iget(dir->i_sb, de->inode)))
+ goto end_rmdir;
+ if ((dir->i_mode & S_ISVTX) && current->euid &&
+ inode->i_uid != current->euid)
+ goto end_rmdir;
+ if (inode->i_dev != dir->i_dev)
+ goto end_rmdir;
+ if (inode == dir) /* we may not delete ".", but "../dir" is ok */
+ goto end_rmdir;
+ if (!S_ISDIR(inode->i_mode)) {
+ retval = -ENOTDIR;
+ goto end_rmdir;
+ }
+ if (!empty_dir(inode)) {
+ retval = -ENOTEMPTY;
+ goto end_rmdir;
+ }
+ if (de->inode != inode->i_ino) {
+ retval = -ENOENT;
+ goto end_rmdir;
+ }
+ if (inode->i_count > 1) {
+ retval = -EBUSY;
+ goto end_rmdir;
+ }
+ if (inode->i_nlink != 2)
+ printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink);
+ de->inode = 0;
+ bh->b_dirt = 1;
+ inode->i_nlink=0;
+ inode->i_dirt=1;
+ dir->i_nlink--;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt=1;
+ retval = 0;
+end_rmdir:
+ iput(dir);
+ iput(inode);
+ brelse(bh);
+ return retval;
+}
+
+int sysv_unlink(struct inode * dir, const char * name, int len)
+{
+ int retval;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct sysv_dir_entry * de;
+
+repeat:
+ retval = -ENOENT;
+ inode = NULL;
+ bh = sysv_find_entry(dir,name,len,&de);
+ if (!bh)
+ goto end_unlink;
+ if (!(inode = iget(dir->i_sb, de->inode)))
+ goto end_unlink;
+ retval = -EPERM;
+ if (S_ISDIR(inode->i_mode))
+ goto end_unlink;
+ if (de->inode != inode->i_ino) {
+ iput(inode);
+ brelse(bh);
+ current->counter = 0;
+ schedule();
+ goto repeat;
+ }
+ if ((dir->i_mode & S_ISVTX) && !suser() &&
+ current->euid != inode->i_uid &&
+ current->euid != dir->i_uid)
+ goto end_unlink;
+ if (de->inode != inode->i_ino) {
+ retval = -ENOENT;
+ goto end_unlink;
+ }
+ if (!inode->i_nlink) {
+ printk("Deleting nonexistent file (%04x:%lu), %d\n",
+ inode->i_dev,inode->i_ino,inode->i_nlink);
+ inode->i_nlink=1;
+ }
+ de->inode = 0;
+ bh->b_dirt = 1;
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt = 1;
+ inode->i_nlink--;
+ inode->i_ctime = dir->i_ctime;
+ inode->i_dirt = 1;
+ retval = 0;
+end_unlink:
+ brelse(bh);
+ iput(inode);
+ iput(dir);
+ return retval;
+}
+
+int sysv_symlink(struct inode * dir, const char * name, int len, const char * symname)
+{
+ struct sysv_dir_entry * de;
+ struct inode * inode;
+ struct buffer_head * name_block;
+ char * name_block_data;
+ struct super_block * sb;
+ int i;
+ char c;
+ struct buffer_head * bh;
+
+ if (!(inode = sysv_new_inode(dir))) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_mode = S_IFLNK | 0777;
+ inode->i_op = &sysv_symlink_inode_operations;
+ name_block = sysv_file_bread(inode,0,1,&name_block_data);
+ if (!name_block) {
+ iput(dir);
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ return -ENOSPC;
+ }
+ sb = inode->i_sb;
+ i = 0;
+ while (i < sb->sv_block_size_1 && (c = *(symname++)))
+ name_block_data[i++] = c;
+ name_block_data[i] = 0;
+ name_block->b_dirt = 1;
+ brelse(name_block);
+ inode->i_size = i;
+ inode->i_dirt = 1;
+ bh = sysv_find_entry(dir,name,len,&de);
+ if (bh) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ i = sysv_add_entry(dir, name, len, &bh, &de);
+ if (i) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ iput(dir);
+ return i;
+ }
+ de->inode = inode->i_ino;
+ bh->b_dirt = 1;
+ brelse(bh);
+ iput(dir);
+ iput(inode);
+ return 0;
+}
+
+int sysv_link(struct inode * oldinode, struct inode * dir, const char * name, int len)
+{
+ int error;
+ struct sysv_dir_entry * de;
+ struct buffer_head * bh;
+
+ if (S_ISDIR(oldinode->i_mode)) {
+ iput(oldinode);
+ iput(dir);
+ return -EPERM;
+ }
+ if (oldinode->i_nlink >= oldinode->i_sb->sv_link_max) {
+ iput(oldinode);
+ iput(dir);
+ return -EMLINK;
+ }
+ bh = sysv_find_entry(dir,name,len,&de);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ iput(oldinode);
+ return -EEXIST;
+ }
+ error = sysv_add_entry(dir, name, len, &bh, &de);
+ if (error) {
+ iput(dir);
+ iput(oldinode);
+ return error;
+ }
+ de->inode = oldinode->i_ino;
+ bh->b_dirt = 1;
+ brelse(bh);
+ iput(dir);
+ oldinode->i_nlink++;
+ oldinode->i_ctime = CURRENT_TIME;
+ oldinode->i_dirt = 1;
+ iput(oldinode);
+ return 0;
+}
+
+/* return 1 if `new' is a subdir of `old' on the same device */
+static int subdir(struct inode * new_inode, struct inode * old_inode)
+{
+ int ino;
+ int result;
+
+ new_inode->i_count++;
+ result = 0;
+ for (;;) {
+ if (new_inode == old_inode) {
+ result = 1;
+ break;
+ }
+ if (new_inode->i_dev != old_inode->i_dev)
+ break;
+ ino = new_inode->i_ino;
+ if (sysv_lookup(new_inode,"..",2,&new_inode))
+ break;
+ if (new_inode->i_ino == ino) /* root dir reached ? */
+ break;
+ }
+ iput(new_inode);
+ return result;
+}
+
+#define PARENT_INO(buffer) \
+(((struct sysv_dir_entry *) ((buffer) + 1*SYSV_DIRSIZE))->inode)
+
+/*
+ * rename uses retrying to avoid race-conditions: at least they should be minimal.
+ * it tries to allocate all the blocks, then sanity-checks, and if the sanity-
+ * checks fail, it tries to restart itself again. Very practical - no changes
+ * are done until we know everything works ok.. and then all the changes can be
+ * done in one fell swoop when we have claimed all the buffers needed.
+ *
+ * Anybody can rename anything with this: the permission checks are left to the
+ * higher-level routines.
+ */
+static int do_sysv_rename(struct inode * old_dir, const char * old_name, int old_len,
+ struct inode * new_dir, const char * new_name, int new_len)
+{
+ struct inode * old_inode, * new_inode;
+ struct buffer_head * old_bh, * new_bh, * dir_bh;
+ char * dir_bh_data;
+ struct sysv_dir_entry * old_de, * new_de;
+ int retval;
+
+ goto start_up;
+try_again:
+ brelse(old_bh);
+ brelse(new_bh);
+ brelse(dir_bh);
+ iput(old_inode);
+ iput(new_inode);
+ current->counter = 0;
+ schedule();
+start_up:
+ old_inode = new_inode = NULL;
+ old_bh = new_bh = dir_bh = NULL;
+ old_bh = sysv_find_entry(old_dir,old_name,old_len,&old_de);
+ retval = -ENOENT;
+ if (!old_bh)
+ goto end_rename;
+ old_inode = __iget(old_dir->i_sb, old_de->inode, 0); /* don't cross mnt-points */
+ if (!old_inode)
+ goto end_rename;
+ retval = -EPERM;
+ if ((old_dir->i_mode & S_ISVTX) &&
+ current->euid != old_inode->i_uid &&
+ current->euid != old_dir->i_uid && !suser())
+ goto end_rename;
+ new_bh = sysv_find_entry(new_dir,new_name,new_len,&new_de);
+ if (new_bh) {
+ new_inode = __iget(new_dir->i_sb, new_de->inode, 0);
+ if (!new_inode) {
+ brelse(new_bh);
+ new_bh = NULL;
+ }
+ }
+ if (new_inode == old_inode) {
+ retval = 0;
+ goto end_rename;
+ }
+ if (new_inode && S_ISDIR(new_inode->i_mode)) {
+ retval = -EISDIR;
+ if (!S_ISDIR(old_inode->i_mode))
+ goto end_rename;
+ retval = -EINVAL;
+ if (subdir(new_dir, old_inode))
+ goto end_rename;
+ retval = -ENOTEMPTY;
+ if (!empty_dir(new_inode))
+ goto end_rename;
+ retval = -EBUSY;
+ if (new_inode->i_count > 1)
+ goto end_rename;
+ }
+ retval = -EPERM;
+ if (new_inode && (new_dir->i_mode & S_ISVTX) &&
+ current->euid != new_inode->i_uid &&
+ current->euid != new_dir->i_uid && !suser())
+ goto end_rename;
+ if (S_ISDIR(old_inode->i_mode)) {
+ retval = -ENOTDIR;
+ if (new_inode && !S_ISDIR(new_inode->i_mode))
+ goto end_rename;
+ retval = -EINVAL;
+ if (subdir(new_dir, old_inode))
+ goto end_rename;
+ retval = -EIO;
+ dir_bh = sysv_file_bread(old_inode,0,0,&dir_bh_data);
+ if (!dir_bh)
+ goto end_rename;
+ if (PARENT_INO(dir_bh_data) != old_dir->i_ino)
+ goto end_rename;
+ retval = -EMLINK;
+ if (!new_inode && new_dir->i_nlink >= new_dir->i_sb->sv_link_max)
+ goto end_rename;
+ }
+ if (!new_bh) {
+ retval = sysv_add_entry(new_dir,new_name,new_len,&new_bh,&new_de);
+ if (retval)
+ goto end_rename;
+ }
+/* sanity checking before doing the rename - avoid races */
+ if (new_inode && (new_de->inode != new_inode->i_ino))
+ goto try_again;
+ if (new_de->inode && !new_inode)
+ goto try_again;
+ if (old_de->inode != old_inode->i_ino)
+ goto try_again;
+/* ok, that's it */
+ old_de->inode = 0;
+ new_de->inode = old_inode->i_ino;
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ old_dir->i_dirt = 1;
+ new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
+ new_dir->i_dirt = 1;
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_ctime = CURRENT_TIME;
+ new_inode->i_dirt = 1;
+ }
+ old_bh->b_dirt = 1;
+ new_bh->b_dirt = 1;
+ if (dir_bh) {
+ PARENT_INO(dir_bh_data) = new_dir->i_ino;
+ dir_bh->b_dirt = 1;
+ old_dir->i_nlink--;
+ old_dir->i_dirt = 1;
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_dirt = 1;
+ } else {
+ new_dir->i_nlink++;
+ new_dir->i_dirt = 1;
+ }
+ }
+ retval = 0;
+end_rename:
+ brelse(dir_bh);
+ brelse(old_bh);
+ brelse(new_bh);
+ iput(old_inode);
+ iput(new_inode);
+ iput(old_dir);
+ iput(new_dir);
+ return retval;
+}
+
+/*
+ * Ok, rename also locks out other renames, as they can change the parent of
+ * a directory, and we don't want any races. Other races are checked for by
+ * "do_rename()", which restarts if there are inconsistencies.
+ *
+ * Note that there is no race between different filesystems: it's only within
+ * the same device that races occur: many renames can happen at once, as long
+ * as they are on different partitions.
+ */
+int sysv_rename(struct inode * old_dir, const char * old_name, int old_len,
+ struct inode * new_dir, const char * new_name, int new_len)
+{
+ static struct wait_queue * wait = NULL;
+ static int lock = 0;
+ int result;
+
+ while (lock)
+ sleep_on(&wait);
+ lock = 1;
+ result = do_sysv_rename(old_dir, old_name, old_len,
+ new_dir, new_name, new_len);
+ lock = 0;
+ wake_up(&wait);
+ return result;
+}
diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c
new file mode 100644
index 0000000..0e1e9bb
--- /dev/null
+++ b/fs/sysv/symlink.c
@@ -0,0 +1,110 @@
+/*
+ * linux/fs/sysv/symlink.c
+ *
+ * minix/symlink.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * coh/symlink.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/symlink.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * SystemV/Coherent symlink handling code
+ */
+
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/sysv_fs.h>
+#include <linux/stat.h>
+
+static int sysv_readlink(struct inode *, char *, int);
+static int sysv_follow_link(struct inode *, struct inode *, int, int, struct inode **);
+
+/*
+ * symlinks can't do much...
+ */
+struct inode_operations sysv_symlink_inode_operations = {
+ NULL, /* no file-operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ sysv_readlink, /* readlink */
+ sysv_follow_link, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static int sysv_follow_link(struct inode * dir, struct inode * inode,
+ int flag, int mode, struct inode ** res_inode)
+{
+ int error;
+ struct buffer_head * bh;
+ char * bh_data;
+
+ *res_inode = NULL;
+ if (!dir) {
+ dir = current->root;
+ dir->i_count++;
+ }
+ if (!inode) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(dir);
+ *res_inode = inode;
+ return 0;
+ }
+ if (current->link_count > 5) {
+ iput(inode);
+ iput(dir);
+ return -ELOOP;
+ }
+ if (!(bh = sysv_file_bread(inode, 0, 0, &bh_data))) { /* is reading 1 block enough ?? */
+ iput(inode);
+ iput(dir);
+ return -EIO;
+ }
+ iput(inode);
+ current->link_count++;
+ error = open_namei(bh_data,flag,mode,res_inode,dir);
+ current->link_count--;
+ brelse(bh);
+ return error;
+}
+
+static int sysv_readlink(struct inode * inode, char * buffer, int buflen)
+{
+ struct buffer_head * bh;
+ char * bh_data;
+ int i;
+ char c;
+
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(inode);
+ return -EINVAL;
+ }
+ if (buflen > inode->i_sb->sv_block_size_1)
+ buflen = inode->i_sb->sv_block_size_1;
+ bh = sysv_file_bread(inode, 0, 0, &bh_data);
+ iput(inode);
+ if (!bh)
+ return 0;
+ i = 0;
+ while (i<buflen && (c = bh_data[i])) {
+ i++;
+ put_fs_byte(c,buffer++);
+ }
+ brelse(bh);
+ return i;
+}
diff --git a/fs/sysv/truncate.c b/fs/sysv/truncate.c
new file mode 100644
index 0000000..e9b6ccf
--- /dev/null
+++ b/fs/sysv/truncate.c
@@ -0,0 +1,508 @@
+/*
+ * linux/fs/sysv/truncate.c
+ *
+ * minix/truncate.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * coh/truncate.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/truncate.c
+ * Copyright (C) 1993 Bruno Haible
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/stat.h>
+
+
+/* There are two different implementations of truncate() here.
+ * One (by Bruno) needs to do locking to ensure that noone is writing
+ * to a block being truncated away and incorporated into the free list.
+ * The better one (by Linus) doesn't need locking because it can tell from
+ * looking at bh->b_count whether a given block is in use elsewhere.
+ * Alas, this doesn't work if block_size < BLOCK_SIZE.
+ */
+
+
+/* Bruno's implementation of truncate. */
+
+/* Leave at most `blocks' direct blocks. */
+static int coh_trunc_direct (struct inode * inode, unsigned long blocks)
+{
+ unsigned int i;
+ unsigned long * p;
+ unsigned long block;
+
+ for (i = blocks; i < 10 ; i++) {
+ p = &inode->u.sysv_i.i_data[i];
+ block = *p;
+ if (!block)
+ continue;
+ *p = 0;
+ inode->i_dirt = 1;
+ sysv_free_block(inode->i_sb,block);
+ }
+ return 0;
+}
+
+/* Leave at most `blocks' blocks out of an indirect block whose number is
+ * from_coh_ulong(*p) if convert=1, *p if convert=0.
+ */
+static int coh_trunc_indirect (struct inode * inode, unsigned long blocks, unsigned long * p, int convert, unsigned char * dirt)
+{
+ struct super_block * sb = inode->i_sb;
+ unsigned long tmp, block, indblock;
+ struct buffer_head * bh;
+ char * bh_data;
+ unsigned long i;
+ sysv_zone_t * ind;
+
+ if (blocks >= sb->sv_ind_per_block)
+ return 0;
+ block = tmp = *p;
+ if (convert)
+ block = from_coh_ulong(block);
+ if (!block)
+ return 0;
+ bh = sysv_bread(sb,inode->i_dev,block,&bh_data);
+ if (tmp != *p) {
+ brelse(bh);
+ return 1;
+ }
+ if (!bh) {
+ *p = 0;
+ *dirt = 1;
+ return 0;
+ }
+ for (i = blocks; i < sb->sv_ind_per_block; i++) {
+ ind = &((sysv_zone_t *) bh_data)[i];
+ indblock = *ind;
+ if (sb->sv_convert)
+ indblock = from_coh_ulong(indblock);
+ if (!indblock)
+ continue;
+ *ind = 0;
+ bh->b_dirt = 1;
+ sysv_free_block(sb,indblock);
+ }
+ for (i = 0; i < sb->sv_ind_per_block; i++)
+ if (((sysv_zone_t *) bh_data)[i])
+ goto done;
+ if (tmp != *p) {
+ brelse(bh);
+ return 1;
+ }
+ *p = 0;
+ *dirt = 1;
+ sysv_free_block(sb,block);
+done:
+ brelse(bh);
+ return 0;
+}
+
+/* Leave at most `blocks' blocks out of an double indirect block whose number is
+ * from_coh_ulong(*p) if convert=1, *p if convert=0.
+ */
+static int coh_trunc_dindirect (struct inode * inode, unsigned long blocks, unsigned long * p, int convert, unsigned char * dirt)
+{
+ struct super_block * sb = inode->i_sb;
+ unsigned long tmp, block, dindblock;
+ struct buffer_head * bh;
+ char * bh_data;
+ unsigned long i, j;
+ sysv_zone_t * dind;
+ int retry = 0;
+
+ if (blocks >= sb->sv_ind_per_block_2)
+ return 0;
+ block = tmp = *p;
+ if (convert)
+ block = from_coh_ulong(block);
+ if (!block)
+ return 0;
+ bh = sysv_bread(sb,inode->i_dev,block,&bh_data);
+ if (tmp != *p) {
+ brelse(bh);
+ return 1;
+ }
+ if (!bh) {
+ *p = 0;
+ *dirt = 1;
+ return 0;
+ }
+ for (i = blocks >> sb->sv_ind_per_block_bits, j = blocks & sb->sv_ind_per_block_1;
+ i < sb->sv_ind_per_block;
+ i++, j = 0) {
+ /* j = max(blocks-i*ind_per_block,0) */
+ dind = &((sysv_zone_t *) bh_data)[i];
+ dindblock = *dind;
+ if (sb->sv_convert)
+ dindblock = from_coh_ulong(dindblock);
+ if (!dindblock)
+ continue;
+ retry |= coh_trunc_indirect(inode,j,dind,sb->sv_convert,&bh->b_dirt);
+ }
+ for (i = 0; i < sb->sv_ind_per_block; i++)
+ if (((sysv_zone_t *) bh_data)[i])
+ goto done;
+ if (tmp != *p) {
+ brelse(bh);
+ return 1;
+ }
+ *p = 0;
+ *dirt = 1;
+ sysv_free_block(sb,block);
+done:
+ brelse(bh);
+ return retry;
+}
+
+/* Leave at most `blocks' blocks out of an triple indirect block whose number is
+ * from_coh_ulong(*p) if convert=1, *p if convert=0.
+ */
+static int coh_trunc_tindirect (struct inode * inode, unsigned long blocks, unsigned long * p)
+{
+ struct super_block * sb = inode->i_sb;
+ unsigned long block, tindblock;
+ struct buffer_head * bh;
+ char * bh_data;
+ unsigned long i, j;
+ sysv_zone_t * tind;
+ int retry = 0;
+
+ if (blocks >= sb->sv_ind_per_block_3)
+ return 0;
+ block = *p;
+ if (!block)
+ return 0;
+ bh = sysv_bread(sb,inode->i_dev,block,&bh_data);
+ if (block != *p) {
+ brelse(bh);
+ return 1;
+ }
+ if (!bh) {
+ *p = 0;
+ inode->i_dirt = 1;
+ return 0;
+ }
+ for (i = blocks >> sb->sv_ind_per_block_2_bits, j = blocks & sb->sv_ind_per_block_2_1;
+ i < sb->sv_ind_per_block;
+ i++, j = 0) {
+ /* j = max(blocks-i*ind_per_block^2,0) */
+ tind = &((sysv_zone_t *) bh_data)[i];
+ tindblock = *tind;
+ if (sb->sv_convert)
+ tindblock = from_coh_ulong(tindblock);
+ if (!tindblock)
+ continue;
+ retry |= coh_trunc_dindirect(inode,j,tind,sb->sv_convert,&bh->b_dirt);
+ }
+ for (i = 0; i < sb->sv_ind_per_block; i++)
+ if (((sysv_zone_t *) bh_data)[i])
+ goto done;
+ if (block != *p) {
+ brelse(bh);
+ return 1;
+ }
+ *p = 0;
+ inode->i_dirt = 1;
+ sysv_free_block(sb,block);
+done:
+ brelse(bh);
+ return retry;
+}
+
+static int coh_trunc_all(struct inode * inode)
+{
+ struct super_block * sb = inode->i_sb;
+ long blocks;
+ int retry;
+
+ blocks = (inode->i_size + sb->sv_block_size_1) >> sb->sv_block_size_bits;
+ retry = coh_trunc_direct(inode,blocks);
+ blocks -= 10;
+ if (blocks < 0) blocks = 0;
+ retry |= coh_trunc_indirect(inode,blocks,&inode->u.sysv_i.i_data[10],0,&inode->i_dirt);
+ blocks -= sb->sv_ind_per_block;
+ if (blocks < 0) blocks = 0;
+ retry |= coh_trunc_dindirect(inode,blocks,&inode->u.sysv_i.i_data[11],0,&inode->i_dirt);
+ blocks -= sb->sv_ind_per_block_2;
+ if (blocks < 0) blocks = 0;
+ retry |= coh_trunc_tindirect(inode,blocks,&inode->u.sysv_i.i_data[12]);
+ return retry;
+}
+
+
+/* Linus' implementation of truncate. Used only if block_size = BLOCK_SIZE. */
+
+/*
+ * Truncate has the most races in the whole filesystem: coding it is
+ * a pain in the a**. Especially as I don't do any locking...
+ *
+ * The code may look a bit weird, but that's just because I've tried to
+ * handle things like file-size changes in a somewhat graceful manner.
+ * Anyway, truncating a file at the same time somebody else writes to it
+ * is likely to result in pretty weird behaviour...
+ *
+ * The new code handles normal truncates (size = 0) as well as the more
+ * general case (size = XXX). I hope.
+ */
+
+/* We throw away any data beyond inode->i_size. */
+
+static int trunc_direct(struct inode * inode)
+{
+ struct super_block * sb;
+ unsigned int i;
+ unsigned long * p;
+ unsigned long block;
+ struct buffer_head * bh;
+ int retry = 0;
+
+ sb = inode->i_sb;
+repeat:
+ for (i = ((unsigned long) inode->i_size + BLOCK_SIZE-1) / BLOCK_SIZE; i < 10; i++) {
+ p = inode->u.sysv_i.i_data + i;
+ block = *p;
+ if (!block)
+ continue;
+ bh = get_hash_table(inode->i_dev,block+sb->sv_block_base,BLOCK_SIZE);
+ if (i*BLOCK_SIZE < inode->i_size) {
+ brelse(bh);
+ goto repeat;
+ }
+ if ((bh && bh->b_count != 1) || (block != *p)) {
+ retry = 1;
+ brelse(bh);
+ continue;
+ }
+ *p = 0;
+ inode->i_dirt = 1;
+ brelse(bh);
+ sysv_free_block(sb,block);
+ }
+ return retry;
+}
+
+#define IND_PER_BLOCK (BLOCK_SIZE / sizeof(sysv_zone_t))
+
+static int trunc_indirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
+{
+ unsigned long indtmp, indblock;
+ struct super_block * sb;
+ struct buffer_head * indbh;
+ unsigned int i;
+ sysv_zone_t * ind;
+ unsigned long tmp, block;
+ struct buffer_head * bh;
+ int retry = 0;
+
+ indblock = indtmp = *p;
+ if (convert)
+ indblock = from_coh_ulong(indblock);
+ if (!indblock)
+ return 0;
+ sb = inode->i_sb;
+ indbh = bread(inode->i_dev,indblock+sb->sv_block_base,BLOCK_SIZE);
+ if (indtmp != *p) {
+ brelse(indbh);
+ return 1;
+ }
+ if (!indbh) {
+ *p = 0;
+ *dirt = 1;
+ return 0;
+ }
+repeat:
+ if (inode->i_size < offset)
+ i = 0;
+ else
+ i = (inode->i_size - offset + BLOCK_SIZE-1) / BLOCK_SIZE;
+ for (; i < IND_PER_BLOCK; i++) {
+ ind = ((sysv_zone_t *) indbh->b_data) + i;
+ block = tmp = *ind;
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (!block)
+ continue;
+ bh = get_hash_table(inode->i_dev,block+sb->sv_block_base,BLOCK_SIZE);
+ if (i*BLOCK_SIZE + offset < inode->i_size) {
+ brelse(bh);
+ goto repeat;
+ }
+ if ((bh && bh->b_count != 1) || (tmp != *ind)) {
+ retry = 1;
+ brelse(bh);
+ continue;
+ }
+ *ind = 0;
+ indbh->b_dirt = 1;
+ brelse(bh);
+ sysv_free_block(sb,block);
+ }
+ for (i = 0; i < IND_PER_BLOCK; i++)
+ if (((sysv_zone_t *) indbh->b_data)[i])
+ goto done;
+ if ((indbh->b_count != 1) || (indtmp != *p)) {
+ brelse(indbh);
+ return 1;
+ }
+ *p = 0;
+ *dirt = 1;
+ sysv_free_block(sb,indblock);
+done:
+ brelse(indbh);
+ return retry;
+}
+
+static int trunc_dindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
+{
+ unsigned long indtmp, indblock;
+ struct super_block * sb;
+ struct buffer_head * indbh;
+ unsigned int i;
+ sysv_zone_t * ind;
+ unsigned long tmp, block;
+ int retry = 0;
+
+ indblock = indtmp = *p;
+ if (convert)
+ indblock = from_coh_ulong(indblock);
+ if (!indblock)
+ return 0;
+ sb = inode->i_sb;
+ indbh = bread(inode->i_dev,indblock+sb->sv_block_base,BLOCK_SIZE);
+ if (indtmp != *p) {
+ brelse(indbh);
+ return 1;
+ }
+ if (!indbh) {
+ *p = 0;
+ *dirt = 1;
+ return 0;
+ }
+ if (inode->i_size < offset)
+ i = 0;
+ else
+ i = (inode->i_size - offset + IND_PER_BLOCK*BLOCK_SIZE-1) / (IND_PER_BLOCK*BLOCK_SIZE);
+ for (; i < IND_PER_BLOCK; i++) {
+ ind = ((sysv_zone_t *) indbh->b_data) + i;
+ block = tmp = *ind;
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (!block)
+ continue;
+ retry |= trunc_indirect(inode,offset+i*IND_PER_BLOCK,ind,sb->sv_convert,&indbh->b_dirt);
+ }
+ for (i = 0; i < IND_PER_BLOCK; i++)
+ if (((sysv_zone_t *) indbh->b_data)[i])
+ goto done;
+ if ((indbh->b_count != 1) || (indtmp != *p)) {
+ brelse(indbh);
+ return 1;
+ }
+ *p = 0;
+ *dirt = 1;
+ sysv_free_block(sb,indblock);
+done:
+ brelse(indbh);
+ return retry;
+}
+
+static int trunc_tindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
+{
+ unsigned long indtmp, indblock;
+ struct super_block * sb;
+ struct buffer_head * indbh;
+ unsigned int i;
+ sysv_zone_t * ind;
+ unsigned long tmp, block;
+ int retry = 0;
+
+ indblock = indtmp = *p;
+ if (convert)
+ indblock = from_coh_ulong(indblock);
+ if (!indblock)
+ return 0;
+ sb = inode->i_sb;
+ indbh = bread(inode->i_dev,indblock+sb->sv_block_base,BLOCK_SIZE);
+ if (indtmp != *p) {
+ brelse(indbh);
+ return 1;
+ }
+ if (!indbh) {
+ *p = 0;
+ *dirt = 1;
+ return 0;
+ }
+ if (inode->i_size < offset)
+ i = 0;
+ else
+ i = (inode->i_size - offset + IND_PER_BLOCK*IND_PER_BLOCK*BLOCK_SIZE-1) / (IND_PER_BLOCK*IND_PER_BLOCK*BLOCK_SIZE);
+ for (; i < IND_PER_BLOCK; i++) {
+ ind = ((sysv_zone_t *) indbh->b_data) + i;
+ block = tmp = *ind;
+ if (sb->sv_convert)
+ block = from_coh_ulong(block);
+ if (!block)
+ continue;
+ retry |= trunc_dindirect(inode,offset+i*IND_PER_BLOCK*IND_PER_BLOCK,ind,sb->sv_convert,&indbh->b_dirt);
+ }
+ for (i = 0; i < IND_PER_BLOCK; i++)
+ if (((sysv_zone_t *) indbh->b_data)[i])
+ goto done;
+ if ((indbh->b_count != 1) || (indtmp != *p)) {
+ brelse(indbh);
+ return 1;
+ }
+ *p = 0;
+ *dirt = 1;
+ sysv_free_block(sb,indblock);
+done:
+ brelse(indbh);
+ return retry;
+}
+
+static int trunc_all(struct inode * inode)
+{
+ return trunc_direct(inode)
+ | trunc_indirect(inode,10*BLOCK_SIZE,&inode->u.sysv_i.i_data[10],0,&inode->i_dirt)
+ | trunc_dindirect(inode,(10+IND_PER_BLOCK)*BLOCK_SIZE,&inode->u.sysv_i.i_data[11],0,&inode->i_dirt)
+ | trunc_tindirect(inode,(10+IND_PER_BLOCK+IND_PER_BLOCK*IND_PER_BLOCK)*BLOCK_SIZE,&inode->u.sysv_i.i_data[12],0,&inode->i_dirt);
+}
+
+
+void sysv_truncate(struct inode * inode)
+{
+ /* If this is called from sysv_put_inode, we needn't worry about
+ * races as we are just losing the last reference to the inode.
+ * If this is called from another place, let's hope it's a regular
+ * file.
+ * Truncating symbolic links is strange. We assume we don't truncate
+ * a directory we are just modifying. We ensure we don't truncate
+ * a regular file we are just writing to, by use of a lock.
+ */
+ if (S_ISLNK(inode->i_mode))
+ printk("sysv_truncate: truncating symbolic link\n");
+ else if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
+ return;
+ if (inode->i_sb->sv_block_size_ratio_bits > 0) { /* block_size < BLOCK_SIZE ? */
+ coh_lock_inode(inode); /* do not write to the inode while we truncate */
+ while (coh_trunc_all(inode)) {
+ current->counter = 0;
+ schedule();
+ }
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ coh_unlock_inode(inode);
+ } else {
+ while (trunc_all(inode)) {
+ current->counter = 0;
+ schedule();
+ }
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+}
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index fbcb6d6..908144d 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -41,7 +41,7 @@ extern int open_inode(struct inode * inode, int mode);
extern void flush_old_exec(struct linux_binprm * bprm);
extern unsigned long change_ldt(unsigned long text_size,unsigned long * page);
-extern unsigned long * create_tables(char * p,int argc,int envc);
+extern unsigned long * create_tables(char * p,int argc,int envc,int ibcs);
extern unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
unsigned long p, int from_kmem);
diff --git a/include/linux/coff.h b/include/linux/coff.h
new file mode 100644
index 0000000..e376521
--- /dev/null
+++ b/include/linux/coff.h
@@ -0,0 +1,351 @@
+/* This file is derrived from the GAS 2.1.4 assembler control file.
+ The GAS product is under the GNU Public License, version 2 or later.
+ As such, this file is also uder that license.
+
+ If the file format changes in the COFF object, this file should be
+ subsequently updated to reflect the changes.
+
+ The actual loader module only uses a few of these structures. The full
+ set is documented here because I received the full set. If you wish
+ more information about COFF, then O'Rielly has a very excellent book.
+*/
+
+#define E_SYMNMLEN 8 /* Number of characters in a symbol name */
+#define E_FILNMLEN 14 /* Number of characters in a file name */
+#define E_DIMNUM 4 /* Number of array dimensions in auxiliary entry */
+
+/*
+ * These defines are byte order independant. There is no alignment of fields
+ * permitted in the structures. Therefore they are declared as characters
+ * and the values loaded from the character positions. It also makes it
+ * nice to have it "endian" independant.
+ */
+
+/* Load a short int from the following tables with little-endian formats */
+#define COFF_SHORT_L(ps) ((short)(((unsigned short)((unsigned char)ps[1])<<8)|\
+ ((unsigned short)((unsigned char)ps[0]))))
+
+/* Load a long int from the following tables with little-endian formats */
+#define COFF_LONG_L(ps) (((long)(((unsigned long)((unsigned char)ps[3])<<24) |\
+ ((unsigned long)((unsigned char)ps[2])<<16) |\
+ ((unsigned long)((unsigned char)ps[1])<<8) |\
+ ((unsigned long)((unsigned char)ps[0])))))
+
+/* Load a short int from the following tables with big-endian formats */
+#define COFF_SHORT_H(ps) ((short)(((unsigned short)((unsigned char)ps[0])<<8)|\
+ ((unsigned short)((unsigned char)ps[1]))))
+
+/* Load a long int from the following tables with big-endian formats */
+#define COFF_LONG_H(ps) (((long)(((unsigned long)((unsigned char)ps[0])<<24) |\
+ ((unsigned long)((unsigned char)ps[1])<<16) |\
+ ((unsigned long)((unsigned char)ps[2])<<8) |\
+ ((unsigned long)((unsigned char)ps[3])))))
+
+/* These may be overriden later by brain dead implementations which generate
+ a big-endian header with little-endian data. In that case, generate a
+ replacement macro which tests a flag and uses either of the two above
+ as appropriate. */
+
+#define COFF_LONG(v) COFF_LONG_L(v)
+#define COFF_SHORT(v) COFF_SHORT_L(v)
+
+/*** coff information for Intel 386/486. */
+
+/********************** FILE HEADER **********************/
+
+struct COFF_filehdr {
+ char f_magic[2]; /* magic number */
+ char f_nscns[2]; /* number of sections */
+ char f_timdat[4]; /* time & date stamp */
+ char f_symptr[4]; /* file pointer to symtab */
+ char f_nsyms[4]; /* number of symtab entries */
+ char f_opthdr[2]; /* sizeof(optional hdr) */
+ char f_flags[2]; /* flags */
+};
+
+/*
+ * Bits for f_flags:
+ *
+ * F_RELFLG relocation info stripped from file
+ * F_EXEC file is executable (i.e. no unresolved externel
+ * references)
+ * F_LNNO line nunbers stripped from file
+ * F_LSYMS local symbols stripped from file
+ * F_MINMAL this is a minimal object file (".m") output of fextract
+ * F_UPDATE this is a fully bound update file, output of ogen
+ * F_SWABD this file has had its bytes swabbed (in names)
+ * F_AR16WR this file has the byte ordering of an AR16WR
+ * (e.g. 11/70) machine
+ * F_AR32WR this file has the byte ordering of an AR32WR machine
+ * (e.g. vax and iNTEL 386)
+ * F_AR32W this file has the byte ordering of an AR32W machine
+ * (e.g. 3b,maxi)
+ * F_PATCH file contains "patch" list in optional header
+ * F_NODF (minimal file only) no decision functions for
+ * replaced functions
+ */
+
+#define COFF_F_RELFLG 0000001
+#define COFF_F_EXEC 0000002
+#define COFF_F_LNNO 0000004
+#define COFF_F_LSYMS 0000010
+#define COFF_F_MINMAL 0000020
+#define COFF_F_UPDATE 0000040
+#define COFF_F_SWABD 0000100
+#define COFF_F_AR16WR 0000200
+#define COFF_F_AR32WR 0000400
+#define COFF_F_AR32W 0001000
+#define COFF_F_PATCH 0002000
+#define COFF_F_NODF 0002000
+
+#define COFF_I386MAGIC 0x14c /* Linux's system */
+
+#if 0 /* Perhaps, someday, these formats may be used. */
+#define COFF_I386PTXMAGIC 0x154
+#define COFF_I386AIXMAGIC 0x175 /* IBM's AIX system */
+#define COFF_I386BADMAG(x) ((COFF_SHORT((x).f_magic) != COFF_I386MAGIC) \
+ && COFF_SHORT((x).f_magic) != COFF_I386PTXMAGIC \
+ && COFF_SHORT((x).f_magic) != COFF_I386AIXMAGIC)
+#else
+#define COFF_I386BADMAG(x) (COFF_SHORT((x).f_magic) != COFF_I386MAGIC)
+#endif
+
+#define COFF_FILHDR struct COFF_filehdr
+#define COFF_FILHSZ sizeof(COFF_FILHDR)
+
+/********************** AOUT "OPTIONAL HEADER" **********************/
+
+/* Linux COFF must have this "optional" header. Standard COFF has no entry
+ location for the "entry" point. They normally would start with the first
+ location of the .text section. This is not a good idea for linux. So,
+ the use of this "optional" header is not optional. It is required.
+
+ Do not be tempted to assume that the size of the optional header is
+ a constant and simply index the next byte by the size of this structure.
+ Use the 'f_opthdr' field in the main coff header for the size of the
+ structure actually written to the file!!
+*/
+
+typedef struct
+{
+ char magic[2]; /* type of file */
+ char vstamp[2]; /* version stamp */
+ char tsize[4]; /* text size in bytes, padded to FW bdry */
+ char dsize[4]; /* initialized data " " */
+ char bsize[4]; /* uninitialized data " " */
+ char entry[4]; /* entry pt. */
+ char text_start[4]; /* base of text used for this file */
+ char data_start[4]; /* base of data used for this file */
+}
+COFF_AOUTHDR;
+
+#define COFF_AOUTSZ (sizeof(COFF_AOUTHDR))
+
+#define COFF_STMAGIC 0401
+#define COFF_OMAGIC 0404
+#define COFF_JMAGIC 0407 /* dirty text and data image, can't share */
+#define COFF_DMAGIC 0410 /* dirty text segment, data aligned */
+#define COFF_ZMAGIC 0413 /* The proper magic number for executabls */
+#define COFF_SHMAGIC 0443 /* shared library header */
+
+/********************** SECTION HEADER **********************/
+
+struct COFF_scnhdr {
+ char s_name[8]; /* section name */
+ char s_paddr[4]; /* physical address, aliased s_nlib */
+ char s_vaddr[4]; /* virtual address */
+ char s_size[4]; /* section size */
+ char s_scnptr[4]; /* file ptr to raw data for section */
+ char s_relptr[4]; /* file ptr to relocation */
+ char s_lnnoptr[4]; /* file ptr to line numbers */
+ char s_nreloc[2]; /* number of relocation entries */
+ char s_nlnno[2]; /* number of line number entries */
+ char s_flags[4]; /* flags */
+};
+
+#define COFF_SCNHDR struct COFF_scnhdr
+#define COFF_SCNHSZ sizeof(COFF_SCNHDR)
+
+/*
+ * names of "special" sections
+ */
+
+#define COFF_TEXT ".text"
+#define COFF_DATA ".data"
+#define COFF_BSS ".bss"
+#define COFF_COMMENT ".comment"
+#define COFF_LIB ".lib"
+
+#define COFF_SECT_TEXT 0 /* Section for instruction code */
+#define COFF_SECT_DATA 1 /* Section for initialized globals */
+#define COFF_SECT_BSS 2 /* Section for un-initialized globals */
+#define COFF_SECT_REQD 3 /* Minimum number of sections for good file */
+
+#define COFF_STYP_REG 0x00 /* regular segment */
+#define COFF_STYP_DSECT 0x01 /* dummy segment */
+#define COFF_STYP_NOLOAD 0x02 /* no-load segment */
+#define COFF_STYP_GROUP 0x04 /* group segment */
+#define COFF_STYP_PAD 0x08 /* .pad segment */
+#define COFF_STYP_COPY 0x10 /* copy section */
+#define COFF_STYP_TEXT 0x20 /* .text segment */
+#define COFF_STYP_DATA 0x40 /* .data segment */
+#define COFF_STYP_BSS 0x80 /* .bss segment */
+#define COFF_STYP_INFO 0x200 /* .comment section */
+#define COFF_STYP_OVER 0x400 /* overlay section */
+#define COFF_STYP_LIB 0x800 /* library section */
+
+/*
+ * Shared libraries have the following section header in the data field for
+ * each library.
+ */
+
+struct COFF_slib {
+ char sl_entsz[4]; /* Size of this entry */
+ char sl_pathndx[4]; /* size of the header field */
+};
+
+#define COFF_SLIBHD struct COFF_slib
+#define COFF_SLIBSZ sizeof(COFF_SLIBHD)
+
+/********************** LINE NUMBERS **********************/
+
+/* 1 line number entry for every "breakpointable" source line in a section.
+ * Line numbers are grouped on a per function basis; first entry in a function
+ * grouping will have l_lnno = 0 and in place of physical address will be the
+ * symbol table index of the function name.
+ */
+
+struct COFF_lineno {
+ union {
+ char l_symndx[4]; /* function name symbol index, iff l_lnno == 0*/
+ char l_paddr[4]; /* (physical) address of line number */
+ } l_addr;
+ char l_lnno[2]; /* line number */
+};
+
+#define COFF_LINENO struct COFF_lineno
+#define COFF_LINESZ 6
+
+/********************** SYMBOLS **********************/
+
+#define COFF_E_SYMNMLEN 8 /* # characters in a short symbol name */
+#define COFF_E_FILNMLEN 14 /* # characters in a file name */
+#define COFF_E_DIMNUM 4 /* # array dimensions in auxiliary entry */
+
+/*
+ * All symbols and sections have the following definition
+ */
+
+struct COFF_syment
+{
+ union {
+ char e_name[E_SYMNMLEN]; /* Symbol name (first 8 characters) */
+ struct {
+ char e_zeroes[4]; /* Leading zeros */
+ char e_offset[4]; /* Offset if this is a header section */
+ } e;
+ } e;
+
+ char e_value[4]; /* Value (address) of the segment */
+ char e_scnum[2]; /* Section number */
+ char e_type[2]; /* Type of section */
+ char e_sclass[1]; /* Loader class */
+ char e_numaux[1]; /* Number of auxiliary entries which follow */
+};
+
+#define COFF_N_BTMASK (0xf) /* Mask for important class bits */
+#define COFF_N_TMASK (0x30) /* Mask for important type bits */
+#define COFF_N_BTSHFT (4) /* # bits to shift class field */
+#define COFF_N_TSHIFT (2) /* # bits to shift type field */
+
+/*
+ * Auxiliary entries because the main table is too limiting.
+ */
+
+union COFF_auxent {
+
+/*
+ * Debugger information
+ */
+
+ struct {
+ char x_tagndx[4]; /* str, un, or enum tag indx */
+ union {
+ struct {
+ char x_lnno[2]; /* declaration line number */
+ char x_size[2]; /* str/union/array size */
+ } x_lnsz;
+ char x_fsize[4]; /* size of function */
+ } x_misc;
+
+ union {
+ struct { /* if ISFCN, tag, or .bb */
+ char x_lnnoptr[4]; /* ptr to fcn line # */
+ char x_endndx[4]; /* entry ndx past block end */
+ } x_fcn;
+
+ struct { /* if ISARY, up to 4 dimen. */
+ char x_dimen[E_DIMNUM][2];
+ } x_ary;
+ } x_fcnary;
+
+ char x_tvndx[2]; /* tv index */
+ } x_sym;
+
+/*
+ * Source file names (debugger information)
+ */
+
+ union {
+ char x_fname[E_FILNMLEN];
+ struct {
+ char x_zeroes[4];
+ char x_offset[4];
+ } x_n;
+ } x_file;
+
+/*
+ * Section information
+ */
+
+ struct {
+ char x_scnlen[4]; /* section length */
+ char x_nreloc[2]; /* # relocation entries */
+ char x_nlinno[2]; /* # line numbers */
+ } x_scn;
+
+/*
+ * Transfer vector (branch table)
+ */
+
+ struct {
+ char x_tvfill[4]; /* tv fill value */
+ char x_tvlen[2]; /* length of .tv */
+ char x_tvran[2][2]; /* tv range */
+ } x_tv; /* info about .tv section (in auxent of symbol .tv)) */
+};
+
+#define COFF_SYMENT struct COFF_syment
+#define COFF_SYMESZ 18
+#define COFF_AUXENT union COFF_auxent
+#define COFF_AUXESZ 18
+
+#define COFF_ETEXT "etext"
+
+/********************** RELOCATION DIRECTIVES **********************/
+
+struct COFF_reloc {
+ char r_vaddr[4]; /* Virtual address of item */
+ char r_symndx[4]; /* Symbol index in the symtab */
+ char r_type[2]; /* Relocation type */
+};
+
+#define COFF_RELOC struct COFF_reloc
+#define COFF_RELSZ 10
+
+#define COFF_DEF_DATA_SECTION_ALIGNMENT 4
+#define COFF_DEF_BSS_SECTION_ALIGNMENT 4
+#define COFF_DEF_TEXT_SECTION_ALIGNMENT 4
+
+/* For new sections we havn't heard of before */
+#define COFF_DEF_SECTION_ALIGNMENT 4
diff --git a/include/linux/fs.h b/include/linux/fs.h
index ff67235..abc73b0 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -154,6 +154,7 @@ struct buffer_head {
#include <linux/iso_fs_i.h>
#include <linux/nfs_fs_i.h>
#include <linux/xia_fs_i.h>
+#include <linux/sysv_fs_i.h>
struct inode {
dev_t i_dev;
@@ -196,6 +197,7 @@ struct inode {
struct iso_inode_info isofs_i;
struct nfs_inode_info nfs_i;
struct xiafs_inode_info xiafs_i;
+ struct sysv_inode_info sysv_i;
} u;
};
@@ -230,6 +232,7 @@ struct file_lock {
#include <linux/iso_fs_sb.h>
#include <linux/nfs_fs_sb.h>
#include <linux/xia_fs_sb.h>
+#include <linux/sysv_fs_sb.h>
struct super_block {
dev_t s_dev;
@@ -254,6 +257,7 @@ struct super_block {
struct isofs_sb_info isofs_sb;
struct nfs_sb_info nfs_sb;
struct xiafs_sb_info xiafs_sb;
+ struct sysv_sb_info sysv_sb;
} u;
};
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index f193f2e..1da619d 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -28,7 +28,7 @@ int verify_area(int type, void * addr, unsigned long count);
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
-#if __GNUC__ < 2 || __GNUC_MINOR__ < 5
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
# define NORET_TYPE __volatile__
# define ATTRIB_NORET /**/
# define NORET_AND /**/
diff --git a/include/linux/major.h b/include/linux/major.h
index 2848f95..9990250 100644
--- a/include/linux/major.h
+++ b/include/linux/major.h
@@ -39,9 +39,12 @@
* 19 - UNUSED
* 20 - UNUSED
* 21 - scsi generic
- * 22 - UNUSED
+ * 22 - (at2disk)
* 23 - mitsumi cdrom
* 24 - sony535 cdrom
+ * 25 -
+ * 26 -
+ * 27 - qic117 tape
*/
#define UNNAMED_MAJOR 0
@@ -68,6 +71,7 @@
/* unused: 22 */
#define MITSUMI_CDROM_MAJOR 23
#define CDU535_CDROM_MAJOR 24
+#define QIC117_TAPE_MAJOR 27
/*
* Tests for SCSI devices.
diff --git a/include/linux/msg.h b/include/linux/msg.h
index 29c4676..da02269 100644
--- a/include/linux/msg.h
+++ b/include/linux/msg.h
@@ -60,7 +60,8 @@ struct msginfo {
#define MSGTQL MSGMNB /* number of system message headers */
#define MSGMAP MSGMNB /* number of entries in message map */
#define MSGSSZ 16 /* message segment size */
-#define MSGSEG ((MSGPOOL*1024)/ MSGSSZ) /* max no. of segments */
+#define __MSGSEG ((MSGPOOL*1024)/ MSGSSZ) /* max no. of segments */
+#define MSGSEG (__MSGSEG <= 0xffff ? __MSGSEG : 0xffff)
#ifdef __KERNEL__
diff --git a/include/linux/serial.h b/include/linux/serial.h
index 18244b1..6250c0d 100644
--- a/include/linux/serial.h
+++ b/include/linux/serial.h
@@ -39,7 +39,11 @@ struct async_struct {
int blocked_open; /* # of blocked opens */
long session; /* Session of opening process */
long pgrp; /* pgrp of opening process */
+ struct termios normal_termios;
+ struct termios callout_termios;
struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+ struct wait_queue *xmit_wait;
struct async_struct *next_port; /* For the linked list */
struct async_struct *prev_port;
};
@@ -85,9 +89,6 @@ struct async_struct {
#define UART_FCR_TRIGGER_8 0x80 /* Mask for trigger set at 8 */
#define UART_FCR_TRIGGER_14 0xC0 /* Mask for trigger set at 14 */
-#define UART_FCR_CLEAR_CMD (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)
-#define UART_FCR_SETUP_CMD (UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8)
-
/*
* These are the definitions for the Line Control Register
*
diff --git a/include/linux/signal.h b/include/linux/signal.h
index 992933a..21ad9e7 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -61,8 +61,8 @@ typedef unsigned int sigset_t; /* 32 bits */
* usage of signal stacks by using the (now obsolete) sa_restorer field in
* the sigaction structure as a stack pointer. This is now possible due to
* the changes in signal handling. LBT 010493.
- * SA_RESTART is a no-op, as restarting is the default anyway. Use the
- * SA_INTERRUPT flag to get interrupting signals..
+ * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the
+ * SA_RESTART flag to get restarting signals (which were the default long ago)
*/
#define SA_NOCLDSTOP 1
#define SA_STACK 0x08000000
diff --git a/include/linux/soundcard.h b/include/linux/soundcard.h
index 9af627b..0603dd3 100644
--- a/include/linux/soundcard.h
+++ b/include/linux/soundcard.h
@@ -37,7 +37,8 @@
* hsavolai@cs.helsinki.fi
*/
-#define SOUND_VERSION 200
+#define SOUND_VERSION 203
+#define VOXWARE
#include <sys/ioctl.h>
@@ -51,6 +52,7 @@
#define SNDCARD_GUS 4
#define SNDCARD_MPU401 5
#define SNDCARD_SB16 6
+#define SNDCARD_SB16MIDI 7
/***********************************
* IOCTL Commands for /dev/sequencer
@@ -67,9 +69,9 @@
*/
/* #define IOCTYPE (0xff<<8) */
#define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */
-#define IOC_VOID 0x20000000 /* no parameters */
-#define IOC_OUT 0x40000000 /* copy out parameters */
-#define IOC_IN 0x80000000 /* copy in parameters */
+#define IOC_VOID 0x00000000 /* no parameters */
+#define IOC_OUT 0x20000000 /* copy out parameters */
+#define IOC_IN 0x40000000 /* copy in parameters */
#define IOC_INOUT (IOC_IN|IOC_OUT)
/* the 0x20000000 is so we can distinguish new ioctl's from old */
#define _IO(x,y) ((int)(IOC_VOID|(x<<8)|y))
diff --git a/include/linux/sysv_fs.h b/include/linux/sysv_fs.h
new file mode 100644
index 0000000..5841e2d
--- /dev/null
+++ b/include/linux/sysv_fs.h
@@ -0,0 +1,437 @@
+#ifndef _LINUX_SYSV_FS_H
+#define _LINUX_SYSV_FS_H
+
+/*
+ * The SystemV/Coherent filesystem constants/structures/macros
+ */
+
+
+/* This code assumes
+ - a little endian processor like 386,
+ - sizeof(short) = 2, sizeof(int) = 4, sizeof(long) = 4,
+ - alignof(short) = 2, alignof(long) = 4.
+*/
+
+#ifdef __GNUC__
+#define __packed2__ __attribute__ ((packed, aligned(2)))
+#else
+#error I want gcc!
+#endif
+
+#include <linux/stat.h> /* declares S_IFLNK etc. */
+#include <linux/sched.h> /* declares wake_up() */
+#include <linux/sysv_fs_sb.h> /* defines the sv_... shortcuts */
+
+
+/* Layout on disk */
+/* ============== */
+
+
+/* The block size is sb->sv_block_size which may be smaller than BLOCK_SIZE. */
+
+/* zones (= data allocation units) are blocks */
+
+/* On Coherent FS, 32 bit quantities are stored using (I quote the Coherent
+ manual) a "canonical byte ordering". This is the PDP-11 byte ordering:
+ x = 2^24 * byte3 + 2^16 * byte2 + 2^8 * byte1 + byte0 is stored
+ as { byte2, byte3, byte0, byte1 }. We need conversions.
+*/
+
+typedef unsigned long coh_ulong;
+
+static inline coh_ulong to_coh_ulong (unsigned long x)
+{
+ return ((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16);
+}
+
+static inline unsigned long from_coh_ulong (coh_ulong x)
+{
+ return ((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16);
+}
+
+/* inode numbers are 16 bit */
+
+typedef unsigned short sysv_ino_t;
+
+/* Block numbers are 24 bit, sometimes stored in 32 bit.
+ On Coherent FS, they are always stored in PDP-11 manner: the least
+ significant 16 bits come last.
+*/
+
+typedef unsigned long sysv_zone_t;
+
+/* Among the blocks ... */
+/* Xenix FS, Coherent FS: block 0 is the boot block, block 1 the super-block.
+ SystemV FS: block 0 contains both the boot sector and the super-block. */
+/* The first inode zone is sb->sv_firstinodezone (1 or 2). */
+
+/* Among the inodes ... */
+/* 0 is non-existent */
+#define SYSV_BADBL_INO 1 /* inode of bad blocks file */
+#define SYSV_ROOT_INO 2 /* inode of root directory */
+
+
+/* Xenix super-block data on disk */
+#define XENIX_NICINOD 100 /* number of inode cache entries */
+#define XENIX_NICFREE 100 /* number of free block list chunk entries */
+struct xenix_super_block {
+ unsigned short s_isize; /* index of first data zone */
+ unsigned long s_fsize __packed2__; /* total number of zones of this fs */
+ /* the start of the free block list: */
+ unsigned short s_nfree; /* number of free blocks in s_free, <= XENIX_NICFREE */
+ unsigned long s_free[XENIX_NICFREE]; /* first free block list chunk */
+ /* the cache of free inodes: */
+ unsigned short s_ninode; /* number of free inodes in s_inode, <= XENIX_NICINOD */
+ sysv_ino_t s_inode[XENIX_NICINOD]; /* some free inodes */
+ /* locks, not used by Linux: */
+ char s_flock; /* lock during free block list manipulation */
+ char s_ilock; /* lock during inode cache manipulation */
+ char s_fmod; /* super-block modified flag */
+ char s_ronly; /* flag whether fs is mounted read-only */
+ unsigned long s_time __packed2__; /* time of last super block update */
+ unsigned long s_tfree __packed2__; /* total number of free zones */
+ unsigned short s_tinode; /* total number of free inodes */
+ short s_dinfo[4]; /* device information ?? */
+ char s_fname[6]; /* file system volume name */
+ char s_fpack[6]; /* file system pack name */
+ char s_clean; /* set to 0x46 when filesystem is properly unmounted */
+ char s_fill[371];
+ long s_magic; /* version of file system */
+ long s_type; /* type of file system: 1 for 512 byte blocks
+ 2 for 1024 byte blocks */
+};
+
+/* Xenix free list block on disk */
+struct xenix_freelist_chunk {
+ unsigned short fl_nfree; /* number of free blocks in fl_free, <= XENIX_NICFREE] */
+ unsigned long fl_free[XENIX_NICFREE] __packed2__;
+};
+
+/* SystemV FS comes in two variants:
+ * sysv2: System V Release 2 (e.g. Microport), structure elements aligned(2).
+ * sysv4: System V Release 4 (e.g. Consensys), structure elements aligned(4).
+ */
+#define SYSV_NICINOD 100 /* number of inode cache entries */
+#define SYSV_NICFREE 50 /* number of free block list chunk entries */
+
+/* SystemV4 super-block data on disk */
+struct sysv4_super_block {
+ unsigned short s_isize; /* index of first data zone */
+ unsigned long s_fsize; /* total number of zones of this fs */
+ /* the start of the free block list: */
+ unsigned short s_nfree; /* number of free blocks in s_free, <= SYSV_NICFREE */
+ unsigned long s_free[SYSV_NICFREE]; /* first free block list chunk */
+ /* the cache of free inodes: */
+ unsigned short s_ninode; /* number of free inodes in s_inode, <= SYSV_NICINOD */
+ sysv_ino_t s_inode[SYSV_NICINOD]; /* some free inodes */
+ /* locks, not used by Linux: */
+ char s_flock; /* lock during free block list manipulation */
+ char s_ilock; /* lock during inode cache manipulation */
+ char s_fmod; /* super-block modified flag */
+ char s_ronly; /* flag whether fs is mounted read-only */
+ unsigned long s_time; /* time of last super block update */
+ short s_dinfo[4]; /* device information ?? */
+ unsigned long s_tfree; /* total number of free zones */
+ unsigned short s_tinode; /* total number of free inodes */
+ char s_fname[6]; /* file system volume name */
+ char s_fpack[6]; /* file system pack name */
+ long s_fill[12];
+ long s_state; /* file system state */
+ long s_magic; /* version of file system */
+ long s_type; /* type of file system: 1 for 512 byte blocks
+ 2 for 1024 byte blocks */
+};
+
+/* SystemV4 free list block on disk */
+struct sysv4_freelist_chunk {
+ unsigned short fl_nfree; /* number of free blocks in fl_free, <= SYSV_NICFREE] */
+ unsigned long fl_free[SYSV_NICFREE];
+};
+
+/* SystemV2 super-block data on disk */
+struct sysv2_super_block {
+ unsigned short s_isize; /* index of first data zone */
+ unsigned long s_fsize __packed2__; /* total number of zones of this fs */
+ /* the start of the free block list: */
+ unsigned short s_nfree; /* number of free blocks in s_free, <= SYSV_NICFREE */
+ unsigned long s_free[SYSV_NICFREE]; /* first free block list chunk */
+ /* the cache of free inodes: */
+ unsigned short s_ninode; /* number of free inodes in s_inode, <= SYSV_NICINOD */
+ sysv_ino_t s_inode[SYSV_NICINOD]; /* some free inodes */
+ /* locks, not used by Linux: */
+ char s_flock; /* lock during free block list manipulation */
+ char s_ilock; /* lock during inode cache manipulation */
+ char s_fmod; /* super-block modified flag */
+ char s_ronly; /* flag whether fs is mounted read-only */
+ unsigned long s_time __packed2__; /* time of last super block update */
+ short s_dinfo[4]; /* device information ?? */
+ unsigned long s_tfree __packed2__; /* total number of free zones */
+ unsigned short s_tinode; /* total number of free inodes */
+ char s_fname[6]; /* file system volume name */
+ char s_fpack[6]; /* file system pack name */
+ long s_fill[14];
+ long s_state; /* file system state */
+ long s_magic; /* version of file system */
+ long s_type; /* type of file system: 1 for 512 byte blocks
+ 2 for 1024 byte blocks */
+};
+
+/* SystemV2 free list block on disk */
+struct sysv2_freelist_chunk {
+ unsigned short fl_nfree; /* number of free blocks in fl_free, <= SYSV_NICFREE] */
+ unsigned long fl_free[SYSV_NICFREE] __packed2__;
+};
+
+/* Coherent super-block data on disk */
+#define COH_NICINOD 100 /* number of inode cache entries */
+#define COH_NICFREE 64 /* number of free block list chunk entries */
+struct coh_super_block {
+ unsigned short s_isize; /* index of first data zone */
+ coh_ulong s_fsize __packed2__; /* total number of zones of this fs */
+ /* the start of the free block list: */
+ unsigned short s_nfree; /* number of free blocks in s_free, <= COH_NICFREE */
+ coh_ulong s_free[COH_NICFREE] __packed2__; /* first free block list chunk */
+ /* the cache of free inodes: */
+ unsigned short s_ninode; /* number of free inodes in s_inode, <= COH_NICINOD */
+ sysv_ino_t s_inode[COH_NICINOD]; /* some free inodes */
+ /* locks, not used by Linux: */
+ char s_flock; /* lock during free block list manipulation */
+ char s_ilock; /* lock during inode cache manipulation */
+ char s_fmod; /* super-block modified flag */
+ char s_ronly; /* flag whether fs is mounted read-only */
+ coh_ulong s_time __packed2__; /* time of last super block update */
+ coh_ulong s_tfree __packed2__; /* total number of free zones */
+ unsigned short s_tinode; /* total number of free inodes */
+ unsigned short s_interleave_m; /* interleave factor */
+ unsigned short s_interleave_n;
+ char s_fname[6]; /* file system volume name */
+ char s_fpack[6]; /* file system pack name */
+ unsigned long s_unique; /* zero, not used */
+};
+
+/* Coherent free list block on disk */
+struct coh_freelist_chunk {
+ unsigned short fl_nfree; /* number of free blocks in fl_free, <= COH_NICFREE] */
+ unsigned long fl_free[COH_NICFREE] __packed2__;
+};
+
+
+/* SystemV/Coherent inode data on disk */
+
+struct sysv_inode {
+ unsigned short i_mode;
+ unsigned short i_nlink;
+ unsigned short i_uid;
+ unsigned short i_gid;
+ unsigned long i_size;
+ union { /* directories, regular files, ... */
+ char i_addb[3*(10+1+1+1)+1]; /* zone numbers: max. 10 data blocks,
+ * then 1 indirection block,
+ * then 1 double indirection block,
+ * then 1 triple indirection block.
+ * Then maybe a "file generation number" ??
+ */
+ /* devices */
+ dev_t i_rdev;
+ /* named pipes on Coherent */
+ struct {
+ char p_addp[30];
+ short p_pnc;
+ short p_prx;
+ short p_pwx;
+ } i_p;
+ } i_a;
+ unsigned long i_atime; /* time of last access */
+ unsigned long i_mtime; /* time of last modification */
+ unsigned long i_ctime; /* time of creation */
+};
+
+/* The admissible values for i_mode are listed in <linux/stat.h> :
+ * #define S_IFMT 00170000 mask for type
+ * #define S_IFREG 0100000 type = regular file
+ * #define S_IFBLK 0060000 type = block device
+ * #define S_IFDIR 0040000 type = directory
+ * #define S_IFCHR 0020000 type = character device
+ * #define S_IFIFO 0010000 type = named pipe
+ * #define S_ISUID 0004000 set user id
+ * #define S_ISGID 0002000 set group id
+ * #define S_ISVTX 0001000 save swapped text even after use
+ * Additionally for SystemV:
+ * #define S_IFLNK 0120000 type = symbolic link
+ * #define S_IFNAM 0050000 type = XENIX special named file ??
+ * Additionally for Coherent:
+ * #define S_IFMPB 0070000 type = multiplexed block device ??
+ * #define S_IFMPC 0030000 type = multiplexed character device ??
+ *
+ * Since Coherent doesn't know about symbolic links, we use a kludgey
+ * implementation of symbolic links: i_mode = COH_KLUDGE_SYMLINK_MODE
+ * denotes a symbolic link. When a regular file should get this mode by
+ * accident, it is automatically converted to COH_KLUDGE_NOT_SYMLINK.
+ * We use S_IFREG because only regular files (and Coherent pipes...) can have
+ * data blocks with arbitrary contents associated with them, and S_ISVTX
+ * ("save swapped text after use") because it is unused on both Linux and
+ * Coherent: Linux does much more intelligent paging, and Coherent hasn't
+ * virtual memory at all.
+ * Same trick for Xenix.
+ */
+#define COH_KLUDGE_SYMLINK_MODE (S_IFREG | S_ISVTX)
+#define COH_KLUDGE_NOT_SYMLINK (S_IFREG | S_ISVTX | S_IRUSR) /* force read access */
+extern inline mode_t from_coh_imode(unsigned short mode)
+{
+ if (mode == COH_KLUDGE_SYMLINK_MODE)
+ return (S_IFLNK | 0777);
+ else
+ return mode;
+}
+extern inline unsigned short to_coh_imode(mode_t mode)
+{
+ if (S_ISLNK(mode))
+ return COH_KLUDGE_SYMLINK_MODE;
+ else if (mode == COH_KLUDGE_SYMLINK_MODE)
+ return COH_KLUDGE_NOT_SYMLINK;
+ else
+ return mode;
+}
+
+/* Admissible values for i_nlink: 0.._LINK_MAX */
+#define XENIX_LINK_MAX 126 /* ?? */
+#define SYSV_LINK_MAX 126 /* 127? 251? */
+#define COH_LINK_MAX 10000 /* max number of hard links to an inode */
+
+/* The number of inodes per block is
+ sb->sv_inodes_per_block = block_size / sizeof(struct sysv_inode) */
+/* The number of indirect pointers per block is
+ sb->sv_ind_per_block = block_size / sizeof(unsigned long) */
+
+
+/* SystemV/Coherent directory entry on disk */
+
+#define SYSV_NAMELEN 14 /* max size of name in struct sysv_dir_entry */
+
+struct sysv_dir_entry {
+ sysv_ino_t inode;
+ char name[SYSV_NAMELEN]; /* up to 14 characters, the rest are zeroes */
+};
+
+#define SYSV_DIRSIZE sizeof(struct sysv_dir_entry) /* size of every directory entry */
+
+
+/* Operations */
+/* ========== */
+
+
+/* identify the FS in memory */
+#define FSTYPE_XENIX 1
+#define FSTYPE_SYSV4 2
+#define FSTYPE_SYSV2 3
+#define FSTYPE_COH 4
+
+#define SYSV_MAGIC_BASE 0x012FF7B3
+
+#define XENIX_SUPER_MAGIC (SYSV_MAGIC_BASE+FSTYPE_XENIX)
+#define SYSV4_SUPER_MAGIC (SYSV_MAGIC_BASE+FSTYPE_SYSV4)
+#define SYSV2_SUPER_MAGIC (SYSV_MAGIC_BASE+FSTYPE_SYSV2)
+#define COH_SUPER_MAGIC (SYSV_MAGIC_BASE+FSTYPE_COH)
+
+/* Because the block size may be smaller than 1024 (which is the unit used by
+ the disk drivers and the buffer code), many functions must return a pointer
+ to the buffer data additionally to the buffer head pointer.
+*/
+#if 0
+struct bh_data {
+ struct buffer_head * bh;
+ char * bh_data;
+};
+#endif
+
+/* sysv_bread(sb,dev,block,...) would be equivalent to
+ bread(dev,block,BLOCK_SIZE)
+ if the block size were always 1024, which is the only one bread() supports.
+*/
+static inline struct buffer_head *
+sysv_bread (struct super_block *sb, int dev, unsigned int block, char* * data)
+{
+ struct buffer_head *bh;
+
+ if (!(bh = bread (dev, (block >> sb->sv_block_size_ratio_bits) + sb->sv_block_base, BLOCK_SIZE)))
+ return NULL;
+ *data = bh->b_data + ((block & sb->sv_block_size_ratio_1) << sb->sv_block_size_bits);
+ return bh;
+}
+
+
+/* locks - protect against simultaneous write and truncate */
+
+extern void _coh_wait_on_inode (struct inode * inode);
+
+extern inline void coh_wait_on_inode (struct inode * inode)
+{
+ if (inode->u.sysv_i.i_lock)
+ _coh_wait_on_inode(inode);
+}
+
+extern inline void coh_lock_inode (struct inode * inode)
+{
+ if (inode->u.sysv_i.i_lock)
+ _coh_wait_on_inode(inode);
+ inode->u.sysv_i.i_lock = 1;
+}
+
+extern inline void coh_unlock_inode (struct inode * inode)
+{
+ inode->u.sysv_i.i_lock = 0;
+ wake_up(&inode->u.sysv_i.i_wait);
+}
+
+
+/*
+ * Function prototypes
+ */
+
+extern int sysv_lookup(struct inode * dir,const char * name, int len,
+ struct inode ** result);
+extern int sysv_create(struct inode * dir,const char * name, int len, int mode,
+ struct inode ** result);
+extern int sysv_mkdir(struct inode * dir, const char * name, int len, int mode);
+extern int sysv_rmdir(struct inode * dir, const char * name, int len);
+extern int sysv_unlink(struct inode * dir, const char * name, int len);
+extern int sysv_symlink(struct inode * inode, const char * name, int len,
+ const char * symname);
+extern int sysv_link(struct inode * oldinode, struct inode * dir, const char * name, int len);
+extern int sysv_mknod(struct inode * dir, const char * name, int len, int mode, int rdev);
+extern int sysv_rename(struct inode * old_dir, const char * old_name, int old_len,
+ struct inode * new_dir, const char * new_name, int new_len);
+extern struct inode * sysv_new_inode(const struct inode * dir);
+extern void sysv_free_inode(struct inode * inode);
+extern unsigned long sysv_count_free_inodes(struct super_block *sb);
+extern int sysv_new_block(struct super_block * sb);
+extern void sysv_free_block(struct super_block * sb, unsigned int block);
+extern unsigned long sysv_count_free_blocks(struct super_block *sb);
+
+extern int sysv_bmap(struct inode *,int);
+
+extern struct buffer_head * sysv_getblk(struct inode *, unsigned int, int, char* *);
+extern struct buffer_head * sysv_file_bread(struct inode *, int, int, char* *);
+
+extern void sysv_truncate(struct inode *);
+extern void sysv_put_super(struct super_block *);
+extern struct super_block *sysv_read_super(struct super_block *,void *,int);
+extern void sysv_write_super(struct super_block *);
+extern void sysv_read_inode(struct inode *);
+extern int sysv_notify_change(int,struct inode *);
+extern void sysv_write_inode(struct inode *);
+extern void sysv_put_inode(struct inode *);
+extern void sysv_statfs(struct super_block *, struct statfs *);
+extern int sysv_sync_inode(struct inode *);
+extern int sysv_sync_file(struct inode *, struct file *);
+#if 0
+extern int sysv_mmap(struct inode *, struct file *, unsigned long, size_t, int, unsigned long);
+#endif
+
+extern struct inode_operations sysv_file_inode_operations;
+extern struct inode_operations sysv_file_inode_operations_with_bmap;
+extern struct inode_operations sysv_dir_inode_operations;
+extern struct inode_operations sysv_symlink_inode_operations;
+
+#endif
+
diff --git a/include/linux/sysv_fs_i.h b/include/linux/sysv_fs_i.h
new file mode 100644
index 0000000..87f0b62
--- /dev/null
+++ b/include/linux/sysv_fs_i.h
@@ -0,0 +1,20 @@
+#ifndef _SYSV_FS_I
+#define _SYSV_FS_I
+
+/*
+ * SystemV/Coherent FS inode data in memory
+ */
+struct sysv_inode_info {
+ unsigned long i_data[10+1+1+1]; /* zone numbers: max. 10 data blocks,
+ * then 1 indirection block,
+ * then 1 double indirection block,
+ * then 1 triple indirection block.
+ */
+ /* the following are only used if block_size < BLOCK_SIZE */
+ int i_lock; /* lock to protect against simultaneous */
+ struct wait_queue * i_wait; /* write and truncate */
+ /* */
+};
+
+#endif
+
diff --git a/include/linux/sysv_fs_sb.h b/include/linux/sysv_fs_sb.h
new file mode 100644
index 0000000..9692686
--- /dev/null
+++ b/include/linux/sysv_fs_sb.h
@@ -0,0 +1,104 @@
+#ifndef _SYSV_FS_SB
+#define _SYSV_FS_SB
+
+/*
+ * SystemV/Coherent super-block data in memory
+ * The SystemV/Coherent superblock contains dynamic data (it gets modified
+ * while the system is running). This is in contrast to the Minix and Berkeley
+ * filesystems (where the superblock is never modified). This affects the
+ * sync() operation: we must keep the superblock in a disk buffer and use this
+ * one as our "working copy".
+ */
+
+struct sysv_sb_info {
+ int s_type; /* file system type: FSTYPE_{XENIX|SYSV|COH} */
+ unsigned int s_block_size; /* zone size, = 512 or = 1024 */
+ unsigned int s_block_size_1; /* block_size - 1 */
+ unsigned int s_block_size_bits; /* log2(block_size) */
+ unsigned int s_block_size_ratio; /* BLOCK_SIZE / block_size */
+ unsigned int s_block_size_ratio_1; /* block_size_ratio - 1 */
+ unsigned int s_block_size_ratio_bits; /* log2(block_size_ratio) */
+ char s_convert; /* flag whether byte ordering requires conversion */
+ char s_kludge_symlinks; /* flag whether symlinks have a kludgey mode */
+ char s_truncate; /* if 1: names > SYSV_NAMELEN chars are truncated */
+ /* if 0: they are disallowed (ENAMETOOLONG) */
+ nlink_t s_link_max; /* max number of hard links to a file */
+ unsigned int s_inodes_per_block; /* number of inodes per block */
+ unsigned int s_inodes_per_block_1; /* inodes_per_block - 1 */
+ unsigned int s_inodes_per_block_bits; /* log2(inodes_per_block) */
+ unsigned int s_ind_per_block; /* number of indirections per block */
+ unsigned int s_ind_per_block_1; /* ind_per_block - 1 */
+ unsigned int s_ind_per_block_bits; /* log2(ind_per_block) */
+ unsigned int s_ind_per_block_2; /* ind_per_block ^ 2 */
+ unsigned int s_ind_per_block_2_1; /* ind_per_block ^ 2 - 1 */
+ unsigned int s_ind_per_block_2_bits; /* log2(ind_per_block^2) */
+ unsigned int s_ind_per_block_3; /* ind_per_block ^ 3 */
+ unsigned int s_toobig_block; /* 10 + ipb + ipb^2 + ipb^3 */
+ unsigned int s_block_base; /* physical block number of block 0 */
+ unsigned short s_fic_size; /* free inode cache size, NICINOD */
+ unsigned short s_flc_size; /* free block list chunk size, NICFREE */
+ /* The superblock is kept in a disk buffer: */
+ struct buffer_head *s_bh;
+ /* These are pointers into the disk buffer, to compensate for
+ different superblock layout. */
+ char * s_sbd; /* entire superblock data */
+ unsigned short *s_sb_fic_count; /* pointer to s_sbd->s_ninode */
+ unsigned short *s_sb_fic_inodes; /* pointer to s_sbd->s_inode */
+ unsigned short *s_sb_total_free_inodes; /* pointer to s_sbd->s_tinode */
+ unsigned short *s_sb_flc_count; /* pointer to s_sbd->s_nfree */
+ unsigned long *s_sb_flc_blocks; /* pointer to s_sbd->s_free */
+ unsigned long *s_sb_total_free_blocks;/* pointer to s_sbd->s_tfree */
+ unsigned long *s_sb_time; /* pointer to s_sbd->s_time */
+ /* We keep those superblock entities that don't change here;
+ this saves us an indirection and perhaps a conversion. */
+ unsigned long s_firstinodezone; /* index of first inode zone */
+ unsigned long s_firstdatazone; /* same as s_sbd->s_isize */
+ unsigned long s_ninodes; /* total number of inodes */
+ unsigned long s_ndatazones; /* total number of data zones */
+ unsigned long s_nzones; /* same as s_sbd->s_fsize */
+};
+/* The fields s_block_size_ratio, s_toobig_block, s_sbd are currently unused. */
+
+/* sv_ == u.sysv_sb.s_ */
+#define sv_type u.sysv_sb.s_type
+#define sv_block_size u.sysv_sb.s_block_size
+#define sv_block_size_1 u.sysv_sb.s_block_size_1
+#define sv_block_size_bits u.sysv_sb.s_block_size_bits
+#define sv_block_size_ratio u.sysv_sb.s_block_size_ratio
+#define sv_block_size_ratio_1 u.sysv_sb.s_block_size_ratio_1
+#define sv_block_size_ratio_bits u.sysv_sb.s_block_size_ratio_bits
+#define sv_convert u.sysv_sb.s_convert
+#define sv_kludge_symlinks u.sysv_sb.s_kludge_symlinks
+#define sv_truncate u.sysv_sb.s_truncate
+#define sv_link_max u.sysv_sb.s_link_max
+#define sv_inodes_per_block u.sysv_sb.s_inodes_per_block
+#define sv_inodes_per_block_1 u.sysv_sb.s_inodes_per_block_1
+#define sv_inodes_per_block_bits u.sysv_sb.s_inodes_per_block_bits
+#define sv_ind_per_block u.sysv_sb.s_ind_per_block
+#define sv_ind_per_block_1 u.sysv_sb.s_ind_per_block_1
+#define sv_ind_per_block_bits u.sysv_sb.s_ind_per_block_bits
+#define sv_ind_per_block_2 u.sysv_sb.s_ind_per_block_2
+#define sv_ind_per_block_2_1 u.sysv_sb.s_ind_per_block_2_1
+#define sv_ind_per_block_2_bits u.sysv_sb.s_ind_per_block_2_bits
+#define sv_ind_per_block_3 u.sysv_sb.s_ind_per_block_3
+#define sv_toobig_block u.sysv_sb.s_toobig_block
+#define sv_block_base u.sysv_sb.s_block_base
+#define sv_fic_size u.sysv_sb.s_fic_size
+#define sv_flc_size u.sysv_sb.s_flc_size
+#define sv_bh u.sysv_sb.s_bh
+#define sv_sbd u.sysv_sb.s_sbd
+#define sv_sb_fic_count u.sysv_sb.s_sb_fic_count
+#define sv_sb_fic_inodes u.sysv_sb.s_sb_fic_inodes
+#define sv_sb_total_free_inodes u.sysv_sb.s_sb_total_free_inodes
+#define sv_sb_flc_count u.sysv_sb.s_sb_flc_count
+#define sv_sb_flc_blocks u.sysv_sb.s_sb_flc_blocks
+#define sv_sb_total_free_blocks u.sysv_sb.s_sb_total_free_blocks
+#define sv_sb_time u.sysv_sb.s_sb_time
+#define sv_firstinodezone u.sysv_sb.s_firstinodezone
+#define sv_firstdatazone u.sysv_sb.s_firstdatazone
+#define sv_ninodes u.sysv_sb.s_ninodes
+#define sv_ndatazones u.sysv_sb.s_ndatazones
+#define sv_nzones u.sysv_sb.s_nzones
+
+#endif
+
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 0adc292..fc7f9bf 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -100,7 +100,7 @@ struct serial_struct {
#define ASYNC_HUP_NOTIFY 0x0001 /* Notify blocked open on hangups */
#define ASYNC_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
#define ASYNC_SAK 0x0004 /* Secure Attention Key (Orange book) */
-#define ASYNC_TERMIOS_RESTORE 0x0008 /* Restore termios when dialin unblocks */
+#define ASYNC_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
#define ASYNC_SPD_MASK 0x0030
#define ASYNC_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
@@ -122,6 +122,7 @@ struct serial_struct {
#define ASYNC_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
#define ASYNC_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
#define ASYNC_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
+#define ASYNC_CLOSING 0x08000000 /* Serial port is closing */
#define IS_A_CONSOLE(min) (((min) & 0xC0) == 0x00)
#define IS_A_SERIAL(min) (((min) & 0xC0) == 0x40)
@@ -214,8 +215,8 @@ struct tty_struct {
struct termios *termios;
int pgrp;
int session;
- unsigned char stopped:1, packet:1, lnext:1;
- unsigned char char_error:2;
+ unsigned char stopped:1, hw_stopped:1, packet:1, lnext:1;
+ unsigned char char_error:3;
unsigned char ctrl_status;
short line;
int disc;
@@ -232,6 +233,7 @@ struct tty_struct {
void (*set_termios)(struct tty_struct *tty, struct termios * old);
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
+ void (*hangup)(struct tty_struct *tty);
struct tty_struct *link;
unsigned char *write_data_ptr;
int write_data_cnt;
@@ -312,6 +314,7 @@ struct tty_ldisc {
#define TTY_RQ_THROTTLED 4
#define TTY_IO_ERROR 5
#define TTY_SLAVE_OPENED 6
+#define TTY_EXCLUSIVE 7
/*
* When a break, frame error, or parity error happens, these codes are
@@ -321,6 +324,7 @@ struct tty_ldisc {
#define TTY_BREAK 1
#define TTY_FRAME 2
#define TTY_PARITY 3
+#define TTY_OVERRUN 4
#define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
#define TTY_READ_FLUSH(tty) tty_read_flush((tty))
@@ -374,6 +378,7 @@ extern void tty_vhangup(struct tty_struct * tty);
extern void tty_unhangup(struct file *filp);
extern int tty_hung_up_p(struct file * filp);
extern void do_SAK(struct tty_struct *tty);
+extern void disassociate_ctty(int priv);
/* tty write functions */
diff --git a/kernel/Makefile b/kernel/Makefile
index c4a9adc..861cf85 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -35,10 +35,10 @@ sched.o: sched.c
$(CC) $(CFLAGS) $(PROFILING) -fno-omit-frame-pointer -c $<
ksyms.lst: ksyms.S ../include/linux/autoconf.h
- $(CPP) $(CFLAGS) ksyms.S > ksyms.lst
+ $(CPP) $(CFLAGS) $< > $@
-ksyms.s: ksyms.lst ksyms.sh
- sh ksyms.sh > ksyms.s
+ksyms.s: ksyms.sh ksyms.lst
+ sh $< > $@
ksyms.o: ksyms.s
diff --git a/kernel/exit.c b/kernel/exit.c
index 62e9fdd..0ec3217 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -452,24 +452,8 @@ fake_volatile:
kill_pg(p->pgrp,SIGCONT,1);
}
}
- if (current->leader) {
- struct task_struct *p;
- struct tty_struct *tty;
-
- if (current->tty >= 0) {
- tty = TTY_TABLE(current->tty);
- if (tty) {
- if (tty->pgrp > 0)
- kill_pg(tty->pgrp, SIGHUP, 1);
- tty->pgrp = -1;
- tty->session = 0;
- }
- }
- for_each_task(p) {
- if (p->session == current->session)
- p->tty = -1;
- }
- }
+ if (current->leader)
+ disassociate_ctty(1);
if (last_task_used_math == current)
last_task_used_math = NULL;
#ifdef DEBUG_PROC_TREE
diff --git a/kernel/ksyms.S b/kernel/ksyms.S
index 9532ee0..d30cbed 100644
--- a/kernel/ksyms.S
+++ b/kernel/ksyms.S
@@ -21,8 +21,6 @@ _schedule
#
# The next labels are needed for ftape driver.
#
- _ftape_big_buffer
- _xtime
- _do_floppy
- _tick
+_ftape_big_buffer
+_do_floppy
#endif
diff --git a/kernel/module.c b/kernel/module.c
index f6abcc7..e949327 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -97,7 +97,7 @@ sys_init_module(char *module_name, char *code, unsigned codesize,
memcpy_fromfs((char *)mp->addr + sizeof (int), code, codesize);
memset((char *)mp->addr + sizeof (int) + codesize, 0,
mp->size * 4096 - (codesize + sizeof (int)));
- printk( " init enty @ 0x%08lx, cleanup entry @ 0x%08lx\n",
+ printk( " init entry @ 0x%08lx, cleanup entry @ 0x%08lx\n",
(unsigned long) rt.init, (unsigned long) rt.cleanup);
mp->cleanup = rt.cleanup;
if ((*rt.init)() != 0)
diff --git a/mm/memory.c b/mm/memory.c
index 8b57ac5..3c2e49e 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -866,23 +866,25 @@ void do_no_page(unsigned long error_code, unsigned long address,
mpnt->vm_ops->nopage(error_code, mpnt, address);
return;
}
- ++tsk->min_flt;
- get_empty_page(tsk,address);
if (tsk != current)
- return;
+ goto ok_no_page;
if (address >= tsk->end_data && address < tsk->brk)
- return;
+ goto ok_no_page;
if (mpnt && mpnt == tsk->stk_vma &&
address - tmp > mpnt->vm_start - address &&
tsk->rlim[RLIMIT_STACK].rlim_cur > mpnt->vm_end - address) {
mpnt->vm_start = address;
- return;
+ goto ok_no_page;
}
tsk->tss.cr2 = address;
current->tss.error_code = error_code;
current->tss.trap_no = 14;
send_sig(SIGSEGV,tsk,1);
- return;
+ if (error_code & 4) /* user level access? */
+ return;
+ok_no_page:
+ ++tsk->min_flt;
+ get_empty_page(tsk,address);
}
/*
@@ -913,15 +915,14 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
do_no_page(error_code, address, current, user_esp);
return;
}
- if (error_code & PAGE_RW) {
- if (!wp_works_ok) {
- wp_works_ok = 1;
- pg0[0] = PAGE_SHARED;
- printk("This processor honours the WP bit even when in supervisor mode. Good.\n");
- return;
- }
- printk("Unable to handle kernel paging WP error");
- } else if (address < PAGE_SIZE) {
+ address -= TASK_SIZE;
+ if (wp_works_ok < 0 && address == 0 && (error_code & PAGE_PRESENT)) {
+ wp_works_ok = 1;
+ pg0[0] = PAGE_SHARED;
+ printk("This processor honours the WP bit even when in supervisor mode. Good.\n");
+ return;
+ }
+ if (address < PAGE_SIZE) {
printk("Unable to handle kernel NULL pointer dereference");
pg0[0] = PAGE_SHARED;
} else
@@ -1114,11 +1115,14 @@ void mem_init(unsigned long start_low_mem,
reservedpages << (PAGE_SHIFT-10),
datapages << (PAGE_SHIFT-10));
/* test if the WP bit is honoured in supervisor mode */
+ wp_works_ok = -1;
pg0[0] = PAGE_READONLY;
invalidate();
__asm__ __volatile__("movb 0,%%al ; movb %%al,0": : :"ax", "memory");
pg0[0] = 0;
invalidate();
+ if (wp_works_ok < 0)
+ wp_works_ok = 0;
return;
}
diff --git a/net/inet/raw.c b/net/inet/raw.c
index 7533532..3fe317c 100644
--- a/net/inet/raw.c
+++ b/net/inet/raw.c
@@ -19,6 +19,7 @@
* Alan Cox : Checks sk->broadcast.
* Alan Cox : Uses skb_free_datagram/skb_copy_datagram
* Alan Cox : Raw passes ip options too
+ * Gerhard Koerting: Pass the right part of the data!
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -112,6 +113,9 @@ raw_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
skb->sk = sk;
skb->len = len;
skb->dev = dev;
+ /* Undo IP's work and go get the IP header back [HACK] */
+ skb->h.raw = ((char *)(skb+1))+dev->hard_header_len;
+ skb->len += skb->h.iph->ihl*sizeof(long);
skb->saddr = daddr;
skb->daddr = saddr;
diff --git a/net/inet/route.c b/net/inet/route.c
index f82a097..ad0572b 100644
--- a/net/inet/route.c
+++ b/net/inet/route.c
@@ -392,8 +392,8 @@ rt_route(unsigned long daddr, struct options *opt)
rt->rt_use++;
return(rt);
}
- if (type==IS_BROADCAST && (rt->rt_dev->flags & IFF_BROADCAST) &&
- ip_addr_match(rt->rt_dev->pa_brdaddr, daddr)) {
+ if ((rt->rt_dev->flags & IFF_BROADCAST) &&
+ rt->rt_dev->pa_brdaddr == daddr) {
DPRINTF((DBG_RT, "%s (BCAST %s)\n",
rt->rt_dev->name, in_ntoa(rt->rt_dev->pa_brdaddr)));
rt->rt_use++;