VERSION = 1
PATCHLEVEL = 1
-SUBLEVEL = 88
+SUBLEVEL = 89
ARCH = i386
comment 'General setup'
-bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD n
+bool 'Normal floppy disk support' CONFIG_BLK_DEV_FD y
bool 'Normal harddisk support' CONFIG_BLK_DEV_HD n
bool 'XT harddisk support' CONFIG_BLK_DEV_XD n
bool 'Networking support' CONFIG_NET n
#define __ASSEMBLY__
#include <asm/system.h>
+#include <linux/fd.h>
#define halt .long PAL_halt
ret ($26)
.end rdusp
-
+.align 5
+.globl floppy_track_buffer
+floppy_track_buffer:
+ .space 512*2*MAX_BUFFER_SECTORS,1
action->flags = irqflags;
action->mask = 0;
action->name = devname;
- if (irq < 8 && irq) {
- cache_21 &= ~(1<<irq);
- outb(cache_21,0x21);
+ if (irq < 8) {
+ if (irq) {
+ cache_21 &= ~(1<<irq);
+ outb(cache_21,0x21);
+ }
} else {
cache_21 &= ~(1<<2);
cache_A1 &= ~(1<<(irq-8));
if (irq == 1)
irq = 7;
#endif
- printk("%d%d", irq, ack);
kstat.interrupts[irq]++;
action = irq_action + irq;
/* quick interrupts get executed with no extra overhead */
--- /dev/null
+/*
+ * Code common to all LCA chips.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+
+#include <asm/system.h>
+#include <asm/lca.h>
+
+/*
+ * BIOS32-style PCI interface:
+ */
+
+/*
+ * PCI BIOS32 interface:
+ */
+#define MAJOR_REV 0
+#define MINOR_REV 0
+
+#ifdef CONFIG_PCI
+
+#define mtpr_mces(v) \
+({ \
+ register unsigned long v0 asm ("0"); \
+ register unsigned long a0 asm ("16"); \
+ a0 = (v); \
+ asm volatile ("call_pal %1 # %0 %2" : "r="(v0) \
+ : "i"(PAL_mtpr_mces), "r"(a0) \
+ : "memory", "0", "1", "16", "22", "23", "24", "25"); \
+ v0; \
+})
+
+#define draina() asm volatile ("call_pal %0" :: "i"(PAL_draina))
+
+
+/*
+ * Given a bus, device, and function number, compute resulting
+ * configuration space address and setup the LCA_IOC_CONF register
+ * accordingly. It is therefore not safe to have concurrent
+ * invocations to configuration space access routines, but there
+ * really shouldn't be any need for this.
+ */
+static int
+mk_conf_addr(unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned long *pci_addr)
+{
+ unsigned long addr;
+
+ if (bus == 0) {
+ int device = device_fn >> 3;
+ int func = device_fn & 0x7;
+
+ /* type 0 configuration cycle: */
+
+ if (device > 12) {
+ return -1;
+ } /* if */
+
+ *((volatile unsigned long*) LCA_IOC_CONF) = 0;
+ addr = (1 << (11 + device)) | (func << 8) | where;
+ } else {
+ /* type 1 configuration cycle: */
+ *((volatile unsigned long*) LCA_IOC_CONF) = 1;
+ addr = (bus << 16) | (device_fn << 8) | where;
+ } /* if */
+ *pci_addr = addr;
+
+ return 0;
+}
+
+
+static unsigned int
+conf_read(unsigned long addr)
+{
+ unsigned long old_ipl, code, stat0;
+ unsigned int value;
+
+ old_ipl = swpipl(7); /* avoid getting hit by machine check */
+
+ /* reset status register to avoid loosing errors: */
+ stat0 = *((volatile unsigned long*)LCA_IOC_STAT0);
+ *((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
+ mb();
+
+ /* access configuration space: */
+
+ value = *((volatile unsigned int*)addr);
+ draina();
+
+ stat0 = *((unsigned long*)LCA_IOC_STAT0);
+ if (stat0 & LCA_IOC_STAT0_ERR) {
+ code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT)
+ & LCA_IOC_STAT0_CODE_MASK);
+ if (code != 1) {
+ printk("lca.c:conf_read: got stat0=%lx\n", stat0);
+ }
+
+ /* reset error status: */
+ *((volatile unsigned long*)LCA_IOC_STAT0) = stat0;
+ mb();
+ mtpr_mces(0x7); /* reset machine check */
+
+ value = 0xffffffff;
+ }
+ swpipl(old_ipl);
+
+ return value;
+}
+
+
+static void
+conf_write(unsigned long addr, unsigned int value)
+{
+}
+
+
+int
+pcibios_present (void)
+{
+ return 1; /* present if configured */
+}
+
+
+int
+pcibios_find_class (unsigned long class_code, unsigned short index,
+ unsigned char *bus, unsigned char *device_fn)
+{
+ pci_resource_t *dev;
+ unsigned long w;
+
+ for (dev = pci_device_list; dev; dev = dev->next) {
+ pcibios_read_config_dword(dev->bus, dev->dev_fn,
+ PCI_CLASS_REVISION, &w);
+ if ((w >> 8) == class_code) {
+ if (index == 0) {
+ *bus = dev->bus;
+ *device_fn = dev->dev_fn;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ --index;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+
+int
+pcibios_find_device (unsigned short vendor, unsigned short device_id,
+ unsigned short index, unsigned char *bus,
+ unsigned char *device_fn)
+{
+ unsigned long w, desired = (device_id << 16) | vendor;
+ pci_resource_t *dev;
+
+ if (vendor == 0xffff) {
+ return PCIBIOS_BAD_VENDOR_ID;
+ }
+
+ for (dev = pci_device_list; dev; dev = dev->next) {
+ pcibios_read_config_dword(dev->bus, dev->dev_fn,
+ PCI_VENDOR_ID, &w);
+ if (w == desired) {
+ if (index == 0) {
+ *bus = dev->bus;
+ *device_fn = dev->dev_fn;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ --index;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+
+int
+pcibios_read_config_byte (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned char *value)
+{
+ unsigned long addr = LCA_CONF;
+ unsigned long pci_addr;
+
+ *value = 0xff;
+
+ if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) {
+ return PCIBIOS_SUCCESSFUL;
+ } /* if */
+
+ addr |= (pci_addr << 5) + 0x00;
+
+ *value = conf_read(addr) >> ((where & 3) * 8);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int
+pcibios_read_config_word (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned short *value)
+{
+ unsigned long addr = LCA_CONF;
+ unsigned long pci_addr;
+
+ *value = 0xffff;
+
+ if (where & 0x1) {
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ } /* if */
+
+ if (mk_conf_addr(bus, device_fn, where, &pci_addr)) {
+ return PCIBIOS_SUCCESSFUL;
+ } /* if */
+
+ addr |= (pci_addr << 5) + 0x08;
+
+ *value = conf_read(addr) >> ((where & 3) * 8);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int
+pcibios_read_config_dword (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned long *value)
+{
+ unsigned long addr = LCA_CONF;
+ unsigned long pci_addr;
+
+ *value = 0xffffffff;
+
+ if (where & 0x3) {
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ } /* if */
+
+ if (mk_conf_addr(bus, device_fn, where, &pci_addr)) {
+ return PCIBIOS_SUCCESSFUL;
+ } /* if */
+
+ addr |= (pci_addr << 5) + 0x18;
+
+ *value = conf_read(addr);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+int
+pcibios_write_config_byte (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned char value)
+{
+ panic("pcibios_write_config_byte");
+}
+
+int
+pcibios_write_config_word (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned short value)
+{
+ panic("pcibios_write_config_word");
+}
+
+int
+pcibios_write_config_dword (unsigned char bus, unsigned char device_fn,
+ unsigned char where, unsigned long value)
+{
+ panic("pcibios_write_config_dword");
+}
+
+#endif /* CONFIG_PCI */
+
+
+unsigned long
+bios32_init(unsigned long memory_start, unsigned long memory_end)
+{
+#ifdef CONFIG_PCI
+ printk("LCA PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV);
+
+ probe_pci();
+
+#if 0
+ {
+ char buf[4096];
+
+ get_pci_list(buf);
+ printk("%s", buf);
+ }
+#endif
+
+#if 0
+ {
+ extern void NCR53c810_test(void);
+ NCR53c810_test();
+ }
+#endif
+#endif /* CONFIG_PCI */
+
+ return memory_start;
+} /* bios32_init */
+
+ /*** end of lca.c ***/
halt();
}
+void show_regs(struct pt_regs * regs)
+{
+ printk("\nPS: %04lx PC: %016lx\n", regs->ps, regs->pc);
+}
+
/*
* Do necessary setup to start up a newly executed thread.
*/
unsigned char aux_device_present;
-/*
- * XXXXX!! Warning Will Robinson.
- * Danger! Danger! This is bogus, I'll get it to link if it kills me
- */
-unsigned char floppy_track_buffer[256];
-
/*
* The format of "screen_info" is strange, and due to early
* i386-setup code. This is just enough to make the console
asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call");
+static int hlt_counter=0;
+
+void disable_hlt(void)
+{
+ hlt_counter++;
+}
+
+void enable_hlt(void)
+{
+ hlt_counter--;
+}
+
/*
* The idle loop on a i386..
*/
/* endless idle loop with no priority at all */
current->counter = -100;
for (;;) {
- if (hlt_works_ok && !need_resched)
+ if (hlt_works_ok && !hlt_counter && !need_resched)
__asm__("hlt");
schedule();
}
}
}
+void show_regs(struct pt_regs * regs)
+{
+ printk("\n");
+ printk("EIP: %04x:%08lx",0xffff & regs->cs,regs->eip);
+ if (regs->cs & 3)
+ printk(" ESP: %04x:%08lx",0xffff & regs->ss,regs->esp);
+ printk(" EFLAGS: %08lx\n",regs->eflags);
+ printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
+ regs->orig_eax,regs->ebx,regs->ecx,regs->edx);
+ printk("ESI: %08lx EDI: %08lx EBP: %08lx",
+ regs->esi, regs->edi, regs->ebp);
+ printk(" DS: %04x ES: %04x FS: %04x GS: %04x\n",
+ 0xffff & regs->ds,0xffff & regs->es,
+ 0xffff & regs->fs,0xffff & regs->gs);
+}
+
/*
* Do necessary setup to start up a newly executed thread.
*/
asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
{
+#ifndef CONFIG_IGNORE_NMI
printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n");
- printk("You probably have a hardware problem with your RAM chips\n");
+ printk("You probably have a hardware problem with your RAM chips or a\n");
+ printk("power saving mode enabled.\n");
+#endif
}
asmlinkage void do_debug(struct pt_regs * regs, long error_code)
set_vflags_short(popw(ssp, sp), regs);
return;
- /* int 3 */
- case 0xcc:
- IP(regs)++;
- do_int(regs, 3, ssp, sp);
- return;
-
/* int xx */
case 0xcd:
IP(regs) += 2;
bool 'Sparc V8 kernel' CONFIG_SPARC_V8 y
bool 'Sparc SMP support' CONFIG_LINUX_SMP n
-bool 'Networking support' CONFIG_NET y
+bool 'Networking support' CONFIG_NET n
bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
bool 'System V IPC' CONFIG_SYSVIPC y
+bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y
if [ "$CONFIG_NET" = "y" ]; then
comment 'Networking options'
comment 'Filesystems'
bool 'Standard (minix) fs support' CONFIG_MINIX_FS y
-bool 'Extended fs support' CONFIG_EXT_FS n
+bool 'Extended fs support' CONFIG_EXT_FS y
bool 'Second extended fs support' CONFIG_EXT2_FS y
-bool 'xiafs filesystem support' CONFIG_XIA_FS n
+bool 'xiafs filesystem support' CONFIG_XIA_FS y
bool 'msdos fs support' CONFIG_MSDOS_FS y
if [ "$CONFIG_MSDOS_FS" = "y" ]; then
bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
fi
-bool '/proc filesystem support' CONFIG_PROC_FS y
+bool '/proc filesystem support' CONFIG_PROC_FS n
if [ "$CONFIG_INET" = "y" ]; then
-bool 'NFS filesystem support' CONFIG_NFS_FS y
+bool 'NFS filesystem support' CONFIG_NFS_FS n
fi
if [ "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_CDU31A" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_BLK_DEV_IDECD" = "y" ]; then
bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS y
bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
fi
bool 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
-bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
+bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS y
comment 'character devices'
* Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
*/
-#include <asm/cprefix.h>
#include <asm/head.h>
#include <asm/asi.h>
+#include <asm/cprefix.h>
/* Here are macros for routines we do often, this allows me to inline this
* without making the code look real ugly. Well, the macro looks ugly too but
sethi %hi( C_LABEL(current) ), %g6; \
ld [%g6 + %lo( C_LABEL(current) )], %g6; \
ld [%g6 + THREAD_UWINDOWS], %g7; /* how many user wins are active? */ \
- subcc %g7, 0x0, %g0
+ subcc %g7, 0x0, %g0; \
bne 2f; /* If there are any, branch. */ \
save %g0, %g0, %g0; /* Save into that window either way. */ \
std %l0, [%sp]; /* If above shows only kernel windows */ \
or %g0, %l5, %g5; /* Restore the globals. */ \
or %g0, %l6, %g6; \
or %g0, %l7, %g7; \
-8: /* We are done when we get here. */ \
-
+8: nop; /* We are done when we get here. */ \
/* As if the last macro wasn't enough, we have to go through a very similar routine
* upon entry to most traps and interrupts. This is save away the current window
sub %fp, 0xb0, %sp; \
1: sethi %hi( C_LABEL(current) ), %l6; \
ld [%l6 + %lo( C_LABEL(current) )], %l6; \
- ld [%l6 + PCB_WIM], %l5; \
+ ld [%l6 + THREAD_WIM], %l5; \
and %l0, 0x1f, %l7; \
cmp %l5, %l7; \
ble,a 4f; \
sethi %hi( C_LABEL(eintstack) ), %l7; \
2: \
sub %l7, 0xb0, %sp; \
-3: \
+3:
.text
.align 4
/* Default trap handler */
.globl my_trap_handler
my_trap_handler:
+#if 1
+ jmp %l1
+ rett %l2
+ nop
+#else
rd %wim, %l4
or %g0, 0x1, %l5
sll %l5, %l0, %l5
nop ! click our heels three times, "no place like home"
jmp %l1
rett %l2
+#endif /* bogon */
+ .globl sparc_timer
+sparc_timer:
+ ENTER_IRQ
+ or %g0, 0x10, %o0
+ call C_LABEL(do_IRQ)
+ or %g0, %g0, %o1
+ jmp %l1
+ rett %l2
+ nop
+
/* This routine is optimized for kernel window fills. User fills take about two
* or three extra jumps on the average. We'll see how this works out.
*/
* see below.
*/
- .globl fill_window_entry
-fill_window_entry:
+ .align 4
+ .globl spill_window_entry
+spill_window_entry:
andcc %l0, 0x40, %g0 ! see if this is a user window fill
- bz,a fill_from_user
+ bz,a spill_from_user
nop
TRAP_WIN_CLEAN /* danger, danger... */
jmp %l1
rett %l2
-fill_from_user:
+spill_from_user:
sethi %hi( C_LABEL(current) ), %l6
ld [%l6 + %lo( C_LABEL(current) )], %l6
ld [%l6 + THREAD_WIM], %l5
* it the first time. :>
*/
- .globl spill_window_entry
-spill_window_entry:
+ .align 4
+ .globl fill_window_entry
+fill_window_entry:
wr %g0, 0, %wim ! Can not enter invalid register without this.
andcc %l0, 0x40, %g0 ! From user?
restore ! restore to where trap occurred
- bz spill_from_user
+ bz fill_from_user
restore ! enter invalid register, whee...
restore %g0, 0x1, %l1 ! enter one-past invalid register
rd %psr, %l0 ! this is the window we need to save
jmp %l1
rett %l2 ! are you as confused as I am?
-spill_from_user:
+fill_from_user:
andcc %sp, 0x7, %g0 ! check for alignment of user stack
- bne spill_bad_stack
+ bne fill_bad_stack
sra %sp, 0x1e, %l7
cmp %l7, 0x0
be,a 1f
andn %sp, 0xfff, %l7
cmp %l7, -1
- bne spill_bad_stack
+ bne fill_bad_stack
andn %sp, 0xfff, %l7
1: lda [%l7] ASI_PTE, %l7
srl %l7, 0x1d, %l7
andn %l7, 0x2, %l7
cmp %l7, 0x4
- bne spill_bad_stack
+ bne fill_bad_stack
and %sp, 0xfff, %l7
cmp %l7, 0xfc1
- bl,a spill_stack_ok
+ bl,a fill_stack_ok
restore %g0, 1, %l1
add %sp, 0x38, %l5
sra %sp, 0x1e, %l7
be,a 1f
andn %sp, 0xfff, %l7
cmp %l7, -1
- bne spill_bad_stack
+ bne fill_bad_stack
andn %sp, 0xfff, %l7
1: lda [%l7] ASI_PTE, %l7
srl %l7, 0x1d, %l7
andn %l7, 0x2, %l7
cmp %l7, 0x4
- be,a spill_stack_ok
+ be,a fill_stack_ok
restore %g0, 0x1, %l1
-spill_bad_stack:
+fill_bad_stack:
save %g0, %g0, %g0 ! save to where restore happened
save %g0, 0x1, %l4 ! save is an add remember? to trap window
sethi %hi( C_LABEL(current) ), %l6
jmp %l1
rett %l2
-spill_stack_ok:
+fill_stack_ok:
rd %psr, %l0
sll %l1, %l0, %l1
wr %l1, 0x0, %wim
jmp %l1
rett %l2
+ .align 4
.globl trap_entry
trap_entry:
TRAP_WIN_CLEAN
jmp %l1
rett %l2
+ .align 4
.globl linux_trap_nmi
linux_trap_nmi:
TRAP_WIN_CLEAN
jmp %l1
rett %l2
+ .align 4
.globl sparc_trap
sparc_trap:
TRAP_WIN_CLEAN
jmp %l1
rett %l2
+ .align 4
.globl leave_trap
leave_trap:
jmp %l1
.long C_LABEL(sys_setfsuid)
.long C_LABEL(sys_setfsgid)
.long C_LABEL(sys_llseek) /* 140 */
+ .align 4
_start: /* danger danger */
start:
C_LABEL(trapbase):
- b gokernel; WRITE_PAUSE ! we never get trap #0 it is special
+ b gokernel; nop; nop; nop; ! we never get trap #0 it is special
TRAP_ENTRY(0x1, my_trap_handler) /* Instruction Access Exception */
TRAP_ENTRY(0x2, my_trap_handler) /* Illegal Instruction */
TRAP_ENTRY(0x3, my_trap_handler) /* Privileged Instruction */
TRAP_ENTRY(0x4, my_trap_handler) /* Floating Point Disabled */
- TRAP_ENTRY(0x5, spill_window_entry) /* Window Overflow */
+ TRAP_ENTRY(0x5, spill_window_entry) /* Window Overflow */
TRAP_ENTRY(0x6, fill_window_entry) /* Window Underflow */
TRAP_ENTRY(0x7, my_trap_handler) /* Memory Address Not Aligned */
TRAP_ENTRY(0x8, my_trap_handler) /* Floating Point Exception */
TRAP_ENTRY_INTERRUPT(11) /* Interrupt Level 11 */
TRAP_ENTRY_INTERRUPT(12) /* Interrupt Level 12 */
TRAP_ENTRY_INTERRUPT(13) /* Interrupt Level 13 */
- TRAP_ENTRY_INTERRUPT(14) /* Interrupt Level 14 */
+ TRAP_ENTRY(0x27, sparc_timer) /* Interrupt Level 14 */
TRAP_ENTRY_INTERRUPT_NMI(15, linux_trap_nmi) /* Level 15 (nmi) */
TRAP_ENTRY(0x20, my_trap_handler) /* General Register Access Error */
TRAP_ENTRY(0xfe, my_trap_handler) /* Software Trap */
TRAP_ENTRY(0xff, my_trap_handler) /* Software Trap */
+ .skip 4096
+
C_LABEL(msgbufmapped):
.word 1
* to all its routines which allows some sanity during bootup.
*/
-#if 0 /* paranoid, need to fix this routine now */
sethi %hi(IE_reg_addr), %l0
or %l0, %lo(IE_reg_addr), %l0
+
+ set 0xf4000000, %l3
sethi %hi(INT_ENABLE_REG_PHYSADR), %l2
or %l2, %lo(INT_ENABLE_REG_PHYSADR), %l2
- srl %l2, %g5, %l1
+ srl %l2, %g5, %l2
+ or %l2, %l3, %l1
sta %l1, [%l0] ASI_PTE
- mov INTS_ALL_ENAB, %l1
+
+ or %g0, INTS_ENAB, %l1
+ nop
+ nop
+
stb %l1, [%l0]
-#endif /* paranoid, see above */
+
/* Aieee, now set PC and nPC, enable traps, give ourselves a stack and it's
* show-time!
*/
wr %g0, 0x0, %wim ! magical invalid window reg
WRITE_PAUSE ! see above
-
/* I keep the timer interrupt on so that BogoMIPS works and the prom
* keeps updating it's "jiffies" counter. 100HZ clock on sparcstations.
*/
* write. ;-( like this (PSR_PS | PSR_S | PSR_PIL)...
*/
- sethi %hi(0x1fc0), %g2
- or %g2, %lo(0x1fc0), %g2
+ sethi %hi(PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2
+ or %g2, %lo(PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2
wr %g2, 0x0, %psr
WRITE_PAUSE
wr %g0, 0x2, %wim ! window 1 invalid
WRITE_PAUSE
+
or %g0, 0x1, %g1
sethi %hi( C_LABEL(current) + THREAD_WIM), %g2
st %g1, [%g2 + %lo( C_LABEL(current) + THREAD_WIM)]
/* I want a kernel stack NOW! */
- set ( C_LABEL(init_user_stack) + 4096 - 96), %fp
- set ( C_LABEL(init_user_stack) + 4096), %sp
+ set ( C_LABEL(init_user_stack) + 4092 - 96 - 80), %fp
+ set ( C_LABEL(init_user_stack) + 4092), %sp
- /* now out stack is set up similarly to the way it is on the i386 */
+/* now out stack is set up similarly to the way it is on the i386 */
rd %psr, %l0
wr %l0, PSR_ET, %psr
WRITE_PAUSE
-
/*
* Maybe the prom zeroes out our BSS section, maybe it doesn't. I certainly
* don't know, do you?
/* Here we go */
+ set C_LABEL(trapbase), %g3
+ wr %g3, 0x0, %tbr
+ WRITE_PAUSE
+
+
/* First we call init_prom() to set up romvec, then off to start_kernel() */
/* XXX put this in arch_init() */
C_LABEL(empty_bad_page_table): .skip 0x1000
C_LABEL(empty_zero_page): .skip 0x1000
+ .align 4
+diagstr: .asciz "DIAG\n"
+ .align 4
/* ioport.c: I/O access on the Sparc. Work in progress.. Most of the things
* in this file are for the sole purpose of getting the kernel
- * through the compiler. :-)
+ * throught the compiler. :-)
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
* sa_restorer is the unused
*/
-#include <asm/ptrace.h>
-#include <asm/system.h>
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
#include <linux/linkage.h>
#include <linux/kernel_stat.h>
#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+#include <asm/psr.h>
void disable_irq(unsigned int irq_nr)
{
unsigned long flags;
-
+ unsigned char *int_reg;
+
save_flags(flags);
+ cli();
+
+ /* We have mapped the irq enable register in head.S and all we
+ * have to do here is frob the bits.
+ */
+
+ int_reg = (char *) IRQ_ENA_ADR;
+
+ switch(irq_nr)
+ {
+ case 1:
+ *int_reg = ((*int_reg) & (~(0x02)));
+ break;
+ case 4:
+ *int_reg = ((*int_reg) & (~(0x04)));
+ break;
+ case 6:
+ *int_reg = ((*int_reg) & (~(0x08)));
+ break;
+ case 8:
+ *int_reg = ((*int_reg) & (~(0x10)));
+ break;
+ case 10:
+ *int_reg = ((*int_reg) & (~(0x20)));
+ break;
+ case 14:
+ *int_reg = ((*int_reg) & (~(0x80)));
+ break;
+ default:
+ printk("AIEEE, Illegal interrupt disable requested irq=%d\n",
+ (int) irq_nr);
+ break;
+ };
+
restore_flags(flags);
return;
}
void enable_irq(unsigned int irq_nr)
{
unsigned long flags;
-
+ unsigned int *int_reg;
+
save_flags(flags);
+ cli();
+
+ /* We have mapped the irq enable register in head.S and all we
+ * have to do here is frob the bits.
+ */
+
+ int_reg = (unsigned int *) IRQ_ENA_ADR;
+
+ switch(irq_nr)
+ {
+ case 1:
+ *int_reg = ((*int_reg) | 0x02);
+ break;
+ case 4:
+ *int_reg = ((*int_reg) | 0x04);
+ break;
+ case 6:
+ *int_reg = ((*int_reg) | 0x08);
+ break;
+ case 8:
+ *int_reg = ((*int_reg) | 0x10);
+ break;
+ case 10:
+ *int_reg = ((*int_reg) | 0x20);
+ break;
+ case 14:
+ *int_reg = ((*int_reg) | 0x80);
+ break;
+ default:
+ printk("AIEEE, Illegal interrupt enable requested irq=%d\n",
+ (int) irq_nr);
+ break;
+ };
+
restore_flags(flags);
return;
}
+/*
+ * Initial irq handlers.
+ */
+struct irqaction {
+ void (*handler)(int, struct pt_regs *);
+ unsigned long flags;
+ unsigned long mask;
+ const char *name;
+};
+
+static struct irqaction irq_action[16] = {
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }
+};
+
+
int get_irq_list(char *buf)
{
- int len = 0;
-
+ int i, len = 0;
+ struct irqaction * action = irq_action;
+
+ for (i = 0 ; i < 16 ; i++, action++) {
+ if (!action->handler)
+ continue;
+ len += sprintf(buf+len, "%2d: %8d %c %s\n",
+ i, kstat.interrupts[i],
+ (action->flags & SA_INTERRUPT) ? '+' : ' ',
+ action->name);
+ }
return len;
}
+void free_irq(unsigned int irq)
+{
+ struct irqaction * action = irq + irq_action;
+ unsigned long flags;
+
+ if (irq > 14) { /* 14 irq levels on the sparc */
+ printk("Trying to free IRQ%d\n", irq);
+ return;
+ }
+ if (!action->handler) {
+ printk("Trying to free free IRQ%d\n", irq);
+ return;
+ }
+ save_flags(flags);
+ cli();
+ disable_irq(irq);
+ action->handler = NULL;
+ action->flags = 0;
+ action->mask = 0;
+ action->name = NULL;
+ restore_flags(flags);
+}
+
+#if 0
+static void handle_nmi(struct pt_regs * regs)
+{
+ printk("NMI, probably due to bus-parity error.\n");
+ printk("PC=%08lx, SP=%08lx\n", regs->pc, regs->sp);
+}
+#endif
+
+static void unexpected_irq(int irq, struct pt_regs * regs)
+{
+ int i;
+
+ printk("IO device interrupt, irq = %d\n", irq);
+ printk("PC = %08lx NPC = %08lx SP=%08lx\n", regs->pc,
+ regs->npc, regs->sp);
+ printk("Expecting: ");
+ for (i = 0; i < 16; i++)
+ if (irq_action[i].handler)
+ printk("[%s:%d] ", irq_action[i].name, i);
+ printk("AIEEE\n");
+}
+
+static inline void handler_irq(int irq, struct pt_regs * regs)
+{
+ struct irqaction * action = irq + irq_action;
+
+ if (!action->handler) {
+ unexpected_irq(irq, regs);
+ return;
+ }
+ action->handler(irq, regs);
+}
+
/*
* do_IRQ handles IRQ's that have been installed without the
* SA_INTERRUPT flag: it uses the full signal-handling return
*/
asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
{
+ struct irqaction *action = irq + irq_action;
+
kstat.interrupts[irq]++;
+ action->handler(irq, regs);
return;
}
asmlinkage void do_fast_IRQ(int irq)
{
kstat.interrupts[irq]++;
+ printk("Got FAST_IRQ number %04lx\n", (long unsigned int) irq);
return;
}
-#define SA_PROBE SA_ONESHOT
-
-/*
- * Using "struct sigaction" is slightly silly, but there
- * are historical reasons and it works well, so..
- */
-static int irqaction(unsigned int irq, struct sigaction * new_sa)
-{
- unsigned long flags;
-
- save_flags(flags);
- restore_flags(flags);
- return 0;
-}
-int request_irq(unsigned int irq, void (*handler)(int),
- unsigned long flags, const char * devname)
-{
- return irqaction(irq, (struct sigaction *) 0);
-}
-
-void free_irq(unsigned int irq)
+int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
+ unsigned long irqflags, const char * devname)
{
+ struct irqaction *action;
unsigned long flags;
+ if(irq > 14) /* Only levels 1-14 are valid on the Sparc. */
+ return -EINVAL;
+
+ if(irq == 0) /* sched_init() requesting the timer IRQ */
+ irq = 14;
+
+ action = irq + irq_action;
+
+ if(action->handler)
+ return -EBUSY;
+
+ if(!handler)
+ return -EINVAL;
+
save_flags(flags);
+ cli();
+
+ action->handler = handler;
+ action->flags = irqflags;
+ action->mask = 0;
+ action->name = devname;
+
+ enable_irq(irq);
+
restore_flags(flags);
- return;
+ return 0;
}
unsigned int probe_irq_on (void)
void init_IRQ(void)
{
- int i;
-
- for (i = 0; i < 16 ; i++)
- set_intr_gate(0x20+i,bad_interrupt[i]);
-
return;
}
#include <linux/kernel.h>
#include <asm/vac-ops.h>
+#include <asm/io.h>
+#include <asm/vaddrs.h>
+#include <asm/param.h>
+#include <asm/clock.h>
/* #define DEBUG_PROBING */
void
probe_clock(int fchild)
{
- /* TODO :> I just can't stomach it right now... */
+ register int node, type;
+ register char *node_str;
+
+ /* This will basically traverse the node-tree of the prom to see
+ * which timer chip is on this machine.
+ */
+
+ printk("Probing timer chip... ");
+
+ type = 0;
+ for(node = fchild ; ; )
+ {
+ node_str = get_str_from_prom(node, "model", promstr_buf);
+ if(strcmp(node_str, "mk48t02") == 0)
+ {
+ type = 2;
+ break;
+ }
+
+ if(strcmp(node_str, "mk48t08") == 0)
+ {
+ type = 8;
+ break;
+ }
+
+ node = node_get_sibling(node);
+ if(node == fchild)
+ {
+ printk("Aieee, could not find timer chip type\n");
+ return;
+ }
+ }
+
+ printk("%s\n", node_str);
+ printk("At OBIO address: 0x%x Virtual address: 0x%x\n",
+ (unsigned int) 0xf3000000, (unsigned int) TIMER_STRUCT);
+
+ mapioaddr((unsigned long) 0xf3000000,
+ (unsigned long) TIMER_STRUCT);
+
+ TIMER_STRUCT->timer_limit14=(((10000) << 10) | 0x80000000);
+ TIMER_STRUCT->timer_limit10=(((10000) << 10) | 0x80000000);
+
return;
}
halt();
}
+void show_regs(struct pt_regs * regs)
+{
+ printk("\nSP: %08lx PC: %08lx NPC: %08lx\n", regs->sp, regs->pc,
+ regs->npc);
+}
+
/*
* Do necessary setup to start up a newly executed thread.
*/
#include <asm/openprom.h>
-#define DEBUG_PROMOPS
+/* #define DEBUG_PROMOPS */
#define MAX_PR_LEN 16 /* exotic hardware probably overshoots this */
int prom_node_root; /* initialized in init_prom */
printk("calling probe_devices...\n");
probe_devices(); /* cpu/fpu, mmu probes */
+ swpipl(13);
+
*memory_start_p = (((unsigned long) &end));
}
/* load up the trap table */
+#if 0 /* not yet */
__asm__("wr %0, 0x0, %%tbr\n\t"
"nop; nop; nop\n\t" : :
"r" (trapbase));
+#endif
return;
}
#include <asm/segment.h>
#include <asm/openprom.h>
#include <asm/page.h>
+#include <asm/pgtable.h>
extern unsigned long pg0[1024]; /* page table for 0-4MB for everybody */
extern void die_if_kernel(char *,struct pt_regs *,long);
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/vac-ops.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
extern void scsi_mem_init(unsigned long);
extern void sound_mem_init(void);
* ZERO_PAGE is a special page that is used for zero-initialized
* data and COW.
*/
-unsigned long __bad_pagetable(void)
+pte_t *__bad_pagetable(void)
{
memset((void *) EMPTY_PGT, 0, PAGE_SIZE);
- return EMPTY_PGT;
+ return (pte_t *) EMPTY_PGT;
}
-unsigned long __bad_page(void)
+pte_t __bad_page(void)
{
memset((void *) EMPTY_PGE, 0, PAGE_SIZE);
- return EMPTY_PGE;
+ return pte_mkdirty(mk_pte((unsigned long) EMPTY_PGE, PAGE_SHARED));
}
unsigned long __zero_page(void)
* unmaps the bootup page table (as we're now in KSEG, so we don't need it).
*
* The bootup sequence put the virtual page table into high memory: that
- * means that we can change the L1 page table by just using VL1p below.
+ * means that we cah change the L1 page table by just using VL1p below.
*/
unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
for(a=0; a<b; a++)
{
- for(i=1; i<num_contexts; i++)
+ for(i=0; i<num_contexts; i++)
{
/* map the kernel virt_addrs */
(*(romvec->pv_setctxt))(i, (char *) c, a);
- c += 4096;
}
+ c += 0x40000;
}
/* Ok, since now mapped in all contexts, we can free up
}
}
-#if 0 /* bogosity */
invalidate(); /* flush the virtual address cache */
-#endif /* bletcherous */
printk("\nCurrently in context - ");
for(i=0; i<num_contexts; i++)
*/
#include <asm/vac-ops.h>
+#include <asm/page.h>
/* Flush all VAC entries for the current context */
while(entries_left-- >=0)
{
hw_flush_vac_context_entry(address);
- address += 4096;
+ address += PAGE_SIZE;
}
}
else
while(entries_left-- >=0)
{
hw_flush_vac_segment_entry(address);
- address += 4096;
+ address += PAGE_SIZE;
}
}
else
message, that the ISO9660-filesystem is not supported by your kernel, when
you try to mount the CD-ROM drive, you have to recompile your kernel.
+If you do *not* have an Aztech/Orchid/Okano/Wearnes drive and want to bypass
+drive detection during Linux boot up, start with boot parameter aztcd=0.
+
+Joe Nardone has compiled a boot disk with the Aztech driver for installing
+Slackware from CDROM. You can find the disk images at 'sunsite.unc.edu';
+see file 'aztech.gz.README' for instructions on how to use it.
4. RECOMPILING YOUR KERNEL
If your kernel is not yet configured for the AZTECH driver and the ISO9660-
V0.4 W.Zimmermann: fixed some bugs. Dec. 17, 1994
V0.5 W.Zimmermann: clean 'scanf' commands without compiler warnings
Jan. 6, 1995
+ V0.6 W.Zimmermann: volume control (still experimental). Jan. 24, 1995
*/
#include <stdio.h>
printf(" PLAY TRACK t PAUSE p RESUME r\n");
printf(" NEXT TRACK n REPEAT LAST l HELP h\n");
printf(" SUB CHANNEL c TRACK INFO i PLAY AT a\n");
- printf(" READ d READ RAW w \n");
+ printf(" READ d READ RAW w VOLUME v\n");
}
int main(void)
union { struct cdrom_msf msf;
unsigned char buf[2336];
} azt;
+ struct cdrom_volctrl volctrl;
- printf("\nMini-Audio CD-Player V0.5 (C) 1994,1995 W.Zimmermann\n");
+ printf("\nMini-Audio CD-Player V0.6 (C) 1994,1995 W.Zimmermann\n");
handle=open("/dev/cdrom",O_RDWR);
ioctl(handle,CDROMRESUME);
entry.cdte_track=arg1;
if (entry.cdte_track<first) entry.cdte_track=first;
if (entry.cdte_track>last) entry.cdte_track=last;
- entry.cdte_format=CDROM_MSF;
+ entry.cdte_format=CDROM_MSF;
if (ioctl(handle,cmd,&entry))
{ printf("Drive error or invalid track no.\n");
}
}
break;
#endif
+ case 'v': cmd=CDROMVOLCTRL;
+ printf("--Channel 0 Left (0-255): ");
+ scanf("%d",&arg1);
+ printf("--Channel 1 Right (0-255): ");
+ scanf("%d",&arg2);
+ volctrl.channel0=arg1;
+ volctrl.channel1=arg2;
+ volctrl.channel2=0;
+ volctrl.channel3=0;
+ if (ioctl(handle,cmd,&volctrl))
+ { printf("Drive error or unsupported command\n");
+ }
+ break;
case 'q': if (close(handle)) printf("Drive Error: CLOSE\n");
exit(0);
case 'h': help();
-#define AZT_VERSION "V0.8"
-/* $Id: aztcd.c,v 0.80 1995/01/21 19:54:53 root Exp $
+#define AZT_VERSION "V0.9"
+/* $Id: aztcd.c,v 0.90 1995/02/02 18:14:17 root Exp $
linux/drivers/block/aztcd.c - AztechCD268 CDROM driver
Copyright (C) 1994,1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
based on Mitsumi CDROM driver by Martin Hariss and preworks by
- Eberhard Moenkeberg.
+ Eberhard Moenkeberg; contains contributions by Joe Nardone and Robby
+ Schirmer.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Modified the SET_TIMER and CLEAR_TIMER macros to comply with
the new timer scheme.
W.Zimmermann, Jan. 21, 1995
+ V0.90 Included CDROMVOLCTRL, but with my Aztech drive I can only turn
+ the channels on and off. If it works better with your drive,
+ please mail me. Also implemented ACMD_CLOSE for CDROMSTART.
+ W.Zimmermann, Jan. 24, 1995
NOTE:
Points marked with ??? are questionable !
*/
struct cdrom_tocentry entry;
struct azt_Toc *tocPtr;
struct cdrom_subchnl subchnl;
+ struct cdrom_volctrl volctrl;
#ifdef AZT_DEBUG
printk("aztcd: starting aztcd_ioctl - Command:%x\n",cmd);
switch (cmd)
{
- case CDROMSTART: /* Spin up the drive */
- /* Don't think we can do this. Even if we could,
- * I think the drive times out and stops after a while
- * anyway. For now, ignore it.
- */
+ case CDROMSTART: /* Spin up the drive. Don't know, what to do,
+ at least close the tray */
+#ifdef AZT_PRIVATE_IOCTLS
+ if (aztSendCmd(ACMD_CLOSE)) return -1;
+ STEN_LOW_WAIT;
+#endif
break;
case CDROMSTOP: /* Spin down the drive */
if (aztSendCmd(ACMD_STOP)) return -1;
aztAudioStatus = CDROM_AUDIO_NO_STATUS;
break;
case CDROMPAUSE: /* Pause the drive */
-/* if (aztAudioStatus != CDROM_AUDIO_PLAY) return -EINVAL;
-*/
+ if (aztAudioStatus != CDROM_AUDIO_PLAY) return -EINVAL;
+
if (aztGetQChannelInfo(&qInfo) < 0)
{ /* didn't get q channel info */
aztAudioStatus = CDROM_AUDIO_NO_STATUS;
return -EINVAL;
memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);
break;
- case CDROMVOLCTRL: /* Volume control */
- /*
- * This is not working yet. Setting the volume by itself does
- * nothing. Following the 'set' by a 'play' results in zero
- * volume. Something to work on for the next release.
- */
- break;
+ case CDROMVOLCTRL: /* Volume control
+ * With my Aztech CD268-01A volume control does not work, I can only
+ turn the cannels on (any value !=0) or off (value==0). Maybe it
+ works better with your drive */
+ st=verify_area(VERIFY_READ,(void *) arg, sizeof(volctrl));
+ if (st) return (st);
+ memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl));
+ azt_Play.start.min = 0x21;
+ azt_Play.start.sec = 0x84;
+ azt_Play.start.frame = volctrl.channel0;
+ azt_Play.end.min = volctrl.channel1;
+ azt_Play.end.sec = volctrl.channel2;
+ azt_Play.end.frame = volctrl.channel3;
+ sendAztCmd(ACMD_SET_VOLUME, &azt_Play);
+ STEN_LOW_WAIT;
+ break;
case CDROMEJECT:
/* all drives can at least stop! */
if (aztAudioStatus == CDROM_AUDIO_PLAY)
static unsigned int fake_change = 0;
static int initialising=1;
+#define FLOPPY0_TYPE ((CMOS_READ(0x10) >> 4) & 15)
+#define FLOPPY1_TYPE (CMOS_READ(0x10) & 15)
+
+/*
+ * Again, the CMOS information doesn't work on the alpha..
+ */
+#ifdef __alpha__
+#undef FLOPPY0_TYPE
+#undef FLOPPY1_TYPE
+#define FLOPPY0_TYPE 6
+#define FLOPPY1_TYPE 0
+#endif
+
#ifdef CONFIG_FLOPPY_2_FDC
#define N_FDC 2
}
/*USETF(FD_DISK_NEWCHANGE);*/
return 1;
- } else if(jiffies >= DRS->select_date+DP->select_delay){
+ } else {
UDRS->last_checked=jiffies;
UCLEARF(FD_DISK_NEWCHANGE);
}
return 0;
}
+static int hlt_disabled=0;
+static void floppy_disable_hlt(void)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ if(!hlt_disabled){
+ hlt_disabled=1;
+#ifdef HAVE_DISABLE_HLT
+ disable_hlt();
+#endif
+ }
+ restore_flags(flags);
+}
+
+static void floppy_enable_hlt(void)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ if(hlt_disabled){
+ hlt_disabled=0;
+#ifdef HAVE_DISABLE_HLT
+ enable_hlt();
+#endif
+ }
+ restore_flags(flags);
+}
+
+
static void setup_DMA(void)
{
#ifdef CONFIG_FLOPPY_SANITY
set_dma_count(FLOPPY_DMA, raw_cmd.length);
enable_dma(FLOPPY_DMA);
sti();
+ floppy_disable_hlt();
}
/* sends a command byte to the fdc */
{
void (*handler)(void) = DEVICE_INTR;
+ floppy_enable_hlt();
CLEAR_INTR;
if ( fdc >= N_FDC || FDCS->address == -1){
/* we don't even know which FDC is the culprit */
floppy_tq.routine = (void *)(void *) empty;
del_timer( &fd_timer);
+ floppy_enable_hlt();
disable_dma(FLOPPY_DMA);
/* avoid dma going to a random drive after shutdown */
sizeof( struct floppy_drive_params ));
}
printk("Floppy drive(s): ");
- set_base_type(0, (CMOS_READ(0x10) >> 4) & 15);
- if (CMOS_READ(0x10) & 15) {
+ set_base_type(0, FLOPPY0_TYPE);
+ if (FLOPPY1_TYPE) {
printk(", ");
- set_base_type(1, CMOS_READ(0x10) & 15);
+ set_base_type(1, FLOPPY1_TYPE);
}
printk("\n");
}
return FDC_82077; /* Revised 82077AA passes all the tests */
} /* get_fdc_version */
-#ifndef FD_MODULE
/* lilo configuration */
static void invert_dcl(int *ints)
{
void floppy_setup(char *str, int *ints)
{
int i;
+ if(!str)
+ return;
for(i=0; i< ARRAY_SIZE(config_params); i++){
if (strcmp(str,config_params[i].name) == 0 ){
config_params[i].fn(ints);
}
printk("unknown floppy parameter %s\n", str);
}
-#endif
+#define FLOPPY_SETUP
#ifdef FD_MODULE
static
#if N_FDC > 1
set_dor(1, ~8, 0);
#endif
-
+ floppy_enable_hlt();
#ifdef CONFIG_FLOPPY_SANITY
for(drive=0; drive < N_FDC * 4; drive++)
if( motor_off_timer[drive].next )
struct cdrom_tocentry entry;
struct mcd_Toc *tocPtr;
struct cdrom_subchnl subchnl;
-#if 0
struct cdrom_volctrl volctrl;
-#endif
if (!ip)
return -EINVAL;
return 0;
case CDROMVOLCTRL: /* Volume control */
- /*
- * This is not working yet. Setting the volume by itself does
- * nothing. Following the 'set' by a 'play' results in zero
- * volume. Something to work on for the next release.
- */
-#if 0
st = verify_area(VERIFY_READ, (void *) arg, sizeof(volctrl));
if (st)
return st;
memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
-printk("VOL %d %d\n", volctrl.channel0 & 0xFF, volctrl.channel1 & 0xFF);
outb(MCMD_SET_VOLUME, MCDPORT(0));
outb(volctrl.channel0, MCDPORT(0));
- outb(0, MCDPORT(0));
+ outb(255, MCDPORT(0));
outb(volctrl.channel1, MCDPORT(0));
- outb(1, MCDPORT(0));
+ outb(255, MCDPORT(0));
i = getMcdStatus(MCD_STATUS_DELAY);
if (i < 0)
return -EIO;
{
- int a, b, c, d;
+ char a, b, c, d;
getValue(&a);
getValue(&b);
getValue(&c);
getValue(&d);
- printk("%02X %02X %02X %02X\n", a, b, c, d);
}
- outb(0xF8, MCDPORT(0));
- i = getMcdStatus(MCD_STATUS_DELAY);
- printk("F8 -> %02X\n", i & 0xFF);
-#endif
return 0;
case CDROMEJECT:
should happen before the receiver is shutdown; this is
done by reversing the values of close_wait and
close_wait2. In the case of a very slow device, the
- timeouts for close_wait or close_wait2 should be lengthed.
+ timeouts for close_wait or close_wait2 should be lengthened.
If either value is set to 0, the kernel will wait forever
for all of the data to be transmitted.
}
}
+static inline void memcpyw(unsigned short *to, unsigned short *from,
+ unsigned int count)
+{
+ count /= 2;
+ while (count) {
+ count--;
+ writew(readw(from++), to++);
+ }
+}
+
int vc_cons_allocated(unsigned int i)
{
return (i < MAX_NR_CONSOLES && vc_cons[i].d);
ol += (oll - ll) * osr;
while (ol < scr_end) {
- memcpy((void *) nl, (void *) ol, rlth);
+ memcpyw((unsigned short *) nl, (unsigned short *) ol, rlth);
if (rrem)
memsetw((void *)(nl + rlth), video_erase_char, rrem);
ol += osr;
static void get_scrmem(int currcons)
{
- memcpy((void *)vc_scrbuf[currcons], (void *)origin, video_screen_size);
+ memcpyw((unsigned short *)vc_scrbuf[currcons],
+ (unsigned short *)origin, video_screen_size);
origin = video_mem_start = (unsigned long)vc_scrbuf[currcons];
scr_end = video_mem_end = video_mem_start + video_screen_size;
pos = origin + y*video_size_row + (x<<1);
if (video_mem_term - video_mem_base < offset + video_screen_size)
offset = 0; /* strange ... */
- memcpy((void *)(video_mem_base + offset), (void *) origin, video_screen_size);
+ memcpyw((unsigned short *)(video_mem_base + offset),
+ (unsigned short *) origin, video_screen_size);
video_mem_start = video_mem_base;
video_mem_end = video_mem_term;
origin = video_mem_base + offset;
put_fs_byte(*sptr++,buf++);
break;
case 1:
- put_fs_byte((char)x,buf++); put_fs_byte((char)y,buf++);
- memcpy_tofs(buf,(char *)origin,2*chcount);
+ put_fs_byte((char)x,buf++); put_fs_byte((char)y,buf++);
+/*XXX*/ memcpy_tofs(buf,(char *)origin,2*chcount);
break;
case 2:
gotoxy(currcons, get_fs_byte(buf+2), get_fs_byte(buf+3));
buf+=4; /* ioctl#, console#, x,y */
- memcpy_fromfs((char *)origin,buf,2*chcount);
+/*XXX*/ memcpy_fromfs((char *)origin,buf,2*chcount);
break;
}
return(0);
panic("Couldn't register Cyclom callout driver\n");
bh_base[CYCLADES_BH].routine = do_cyclades_bh;
+ enable_bh(CYCLADES_BH);
for (index = 0; index < 16; index++) {
IRQ_cards[index] = 0;
extern inline void set_leds(void)
{
- /* con_init calls (indirectly) set_leds before kbd_init
- has been called; ignore these early calls */
- if (bh_base[KEYBOARD_BH].routine)
- mark_bh(KEYBOARD_BH);
+ mark_bh(KEYBOARD_BH);
}
extern inline int vc_kbd_mode(struct kbd_struct * kbd, int flag)
static void show_ptregs(void)
{
-#ifdef __i386__
- if (!pt_regs)
- return;
- printk("\n");
- printk("EIP: %04x:%08lx",0xffff & pt_regs->cs,pt_regs->eip);
- if (pt_regs->cs & 3)
- printk(" ESP: %04x:%08lx",0xffff & pt_regs->ss,pt_regs->esp);
- printk(" EFLAGS: %08lx\n",pt_regs->eflags);
- printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
- pt_regs->orig_eax,pt_regs->ebx,pt_regs->ecx,pt_regs->edx);
- printk("ESI: %08lx EDI: %08lx EBP: %08lx",
- pt_regs->esi, pt_regs->edi, pt_regs->ebp);
- printk(" DS: %04x ES: %04x FS: %04x GS: %04x\n",
- 0xffff & pt_regs->ds,0xffff & pt_regs->es,
- 0xffff & pt_regs->fs,0xffff & pt_regs->gs);
-#endif
+ if (pt_regs)
+ show_regs(pt_regs);
}
static void hold(void)
resend = 0;
reply_expected = 1;
outb_p(data, 0x60);
- for(i=0; i<0x20000; i++) {
+ for(i=0; i<0x200000; i++) {
inb_p(0x64); /* just as a delay */
if (acknowledge)
return 1;
outb(0x1,0x60);
while (inb(0x64) & 2)
/* nothing */;
- send_data(0xf0); /* Select scan code */
- send_data(0x01); /* type 1 */
-#endif
+ if (!send_data(0xf0) || !send_data(0x01))
+ printk("Scanmode 1 change failed\n");
+#endif
mark_bh(KEYBOARD_BH);
+ enable_bh(KEYBOARD_BH);
return kmem_start;
}
struct async_struct * info;
bh_base[SERIAL_BH].routine = do_serial_bh;
+ enable_bh(SERIAL_BH);
timer_table[RS_TIMER].fn = rs_timer;
timer_table[RS_TIMER].expires = 0;
#ifdef CONFIG_AUTO_IRQ
arg = get_fs_long((unsigned long *) arg);
return tty_set_ldisc(tty, arg);
case TIOCLINUX:
- if ((current->tty != tty ||
- tty->driver.type != TTY_DRIVER_TYPE_CONSOLE) && !suser())
+ if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
+ return -EINVAL;
+ if (current->tty != tty && !suser())
return -EPERM;
retval = verify_area(VERIFY_READ, (void *) arg, 1);
if (retval)
are unique to this package and simply may be moved into place. The
others are modified versions of pre-existing files and must
be incorporated more carefully. However, the regions of modified code
- within these files are small and are bracketted by the preprocessor
+ within these files are small and are bracketed by the preprocessor
symbol CONFIG_WAVELAN, so incorporation should be straightforward.
4. If you encounter any problems send me some email.
* This is an extension to the Linux operating system, and is covered by the
* same Gnu Public License that covers that work.
*
- * Alphacode 0.51 (94/08/19) for Linux 1.1.47 (or later)
- * Copyrights (c) 1994 by Michael Hipp (mhipp@student.uni-tuebingen.de)
+ * Alphacode 0.62 (95/01/19) for Linux 1.1.82 (or later)
+ * Copyrights (c) 1994,1995 by M.Hipp (Michael.Hipp@student.uni-tuebingen.de)
* [feel free to mail ....]
*
* CAN YOU PLEASE REPORT ME YOUR PERFORMANCE EXPERIENCES !!.
+ *
+ * If you find a bug, please report me:
+ * The kernelpanic output and any kmsg from the ni52 driver
+ * the ni5210-driver-version and the linux-kernel version
+ * how many shared memory (memsize) on the netcard,
+ * bootprom: yes/no, base_addr, mem_start
+ * maybe the ni5210-card revision and the i82586 version
*
* autoprobe for: base_addr: 0x300,0x280,0x360,0x320,0x340
- * mem_start: 0xd0000,0xd4000,0xd8000 (8K and 16K)
+ * mem_start: 0xc8000,0xd0000,0xd4000,0xd8000 (8K and 16K)
*
* sources:
* skeleton.c from Donald Becker
* I have also done a look in the following sources: (mail me if you need them)
* crynwr-packet-driver by Russ Nelson
* Garret A. Wollman's (fourth) i82586-driver for BSD
- * (before getting an i82596 manual, the existing drivers helped
+ * (before getting an i82596 (yes 596 not 586) manual, the existing drivers helped
* me a lot to understand this tricky chip.)
*
- * Known Bugs:
+ * Known Problems:
* The internal sysbus seems to be slow. So we often lose packets because of
* overruns while receiving from a fast remote host.
* This can slow down TCP connections. Maybe the newer ni5210 cards are better.
+ *
+ * IMPORTANT NOTE:
+ * On fast networks, it's a (very) good idea to have 16K shared memory. With
+ * 8K, we can store only 4 receive frames, so it can (easily) happen that a remote
+ * machine 'overruns' our system.
+ *
+ * Known i82586 bugs (I'm sure, there are many more!):
+ * Running the NOP-mode, the i82586 sometimes seems to forget to report
+ * every xmit-interrupt until we restart the CU.
+ * Another MAJOR bug is, that the RU sometimes seems to ignore the EL-Bit
+ * in the RBD-Struct which indicates an end of the RBD queue.
+ * Instead, the RU fetches another (randomly selected and
+ * usually used) RBD and begins to fill it. (Maybe, this happens only if
+ * the last buffer from the previous RFD fits exact into the queue and
+ * the next RFD can't fetch an initial RBD. Anyone knows more? )
*/
/*
+ * 19.Jan.95: verified (MH)
+ *
+ * 19.Sep.94: Added Multicast support (not tested yet) (MH)
+ *
+ * 18.Sep.94: Workarround for 'EL-Bug'. Removed flexible RBD-handling.
+ * Now, every RFD has exact one RBD. (MH)
+ *
+ * 14.Sep.94: added promiscous mode, a few cleanups (MH)
+ *
* 19.Aug.94: changed request_irq() parameter (MH)
*
* 20.July.94: removed cleanup bugs, removed a 16K-mem-probe-bug (MH)
* 26.March.94: patches for Linux 1.0 and iomem-auto-probe (MH)
*
* 30.Sep.93: Added nop-chain .. driver now runs with only one Xmit-Buff, too (MH)
+ *
+ * < 30.Sep.93: first versions
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
-#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/malloc.h>
#include "ni52.h"
-#define DEBUG /* debug on */
-
-/*
-#define DEBUG1
-#define DEBUG2
-#define DEBUG3
-*/
-
-#define SYSBUSVAL 1
+#define DEBUG /* debug on */
+#define SYSBUSVAL 1 /* 8 Bit */
#define ni_attn586() {outb(0,dev->base_addr+NI52_ATTENTION);}
#define ni_reset586() {outb(0,dev->base_addr+NI52_RESET);}
#define make24(ptr32) ((char *) (ptr32) - p->base)
#define make16(ptr32) ((unsigned short) ((unsigned long) (ptr32) - (unsigned long) p->memtop ))
-/******************* how to calc the buffers *****************************
-
-IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, do also a
---------------- #define ONLY_ONE_XMIT_BUF
- btw: it seems, that only the ONLY_ONE_XMIT_BUF Mode is stable
+/******************* how to calculate the buffers *****************************
+ * IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, the driver works
+ * --------------- in a different (more stable?) mode. Only in this mode it's
+ * possbile to configure the driver with 'NO_NOPCOMMANDS'
sizeof(scp)=12; sizeof(scb)=16; sizeof(iscp)=8;
sizeof(scp)+sizeof(iscp)+sizeof(scb) = 36 = INIT
sizeof(tbd) = 8; sizeof(transmit_cmd) = 16;
sizeof(nop_cmd) = 8;
-examples:
----------
-
-->cfg1: NUM_RECV_FRAMES=16, NUM_RECV_BUFFS=48, RECV_BUFF_SIZE=256,
- NUM_XMIT_BUFFS=2 ,XMIT_BUFF_SIZE=1514
-
-NUM_RECV_FRAMES * sizeof(rfd) = 384;
-NUM_RECV_BUFFS * ( sizeof(rbd) + RECV_BUFF_SIZE) = 12864
-NUM_XMIT_BUFFS * ( sizeof(tbd+transmit_cmd+nop_cmd) + XMIT_BUFF_SIZE) = 3092
-INIT = 36
---------------------
-16358 (36 bytes left!)
-
-************************
-
-->cfg2: NUM_RECV_FRAMES=9, NUM_RECV_BUFFS=18, RECV_BUFF_SIZE=256,
- NUM_XMIT_BUFFS=2 ,XMIT_BUFF_SIZE=1514
-
-NUM_RECV_FRAMES * sizeof(rfd) = 216
-NUM_RECV_BUFFS * ( sizeof(rbd) + RECV_BUFF_SIZE) = 4824
-NUM_XMIT_BUFFS * ( sizeof(tbd+transmit_cmd+nop_cmd) + XMIT_BUFF_SIZE) = 3092
-INIT = 36
-------------------
-8180 (24 bytes left!)
-
-->cfg3: NUM_RECV_FRAMES=7, NUM_RECV_BUFFS=24, RECV_BUFF_SIZE=256,
- NUM_XMIT_BUFFS=1, XMIT_BUFF_SIZE=1514
- 168 + 6432 + 1538 + 36 + 16 = 8190
-
-***************************************************************************/
-
-#if 0
-/* config-1 for 16Kram card */
-# define NUM_RECV_FRAMES 16 /* number of frames to allow for receive */
-# define NUM_RECV_BUFFS 48 /* number of buffers to allocate */
-# define RECV_BUFF_SIZE 256 /* size of each buffer, POWER OF 2 & EVEN*/
-# define XMIT_BUFF_SIZE 1514 /* length of transmit buffer (EVEN) */
-# define NUM_XMIT_BUFFS 2 /* number of Xmit-Buffs */
-#elif 0
-/* config-2 for 8Kram card */
-# define NUM_RECV_FRAMES 9
-# define NUM_RECV_BUFFS 18
-# define RECV_BUFF_SIZE 256
-# define XMIT_BUFF_SIZE 1514
-# define NUM_XMIT_BUFFS 2
-#elif 1
-/*
- * config-3 for 8Kram card ___use_this_config____ seems to be stable
- */
-# define NUM_RECV_FRAMES 7
-# define NUM_RECV_BUFFS 24
-# define RECV_BUFF_SIZE 256
-# define XMIT_BUFF_SIZE 1514
-# define NUM_XMIT_BUFFS 1
-# define ONLY_ONE_XMIT_BUF
-# define NO_NOPCOMMANDS
-#elif 0
-/*
- * cfg-4 for 16K, ONLY_ONE_XMIT_BUF
- */
-# define NUM_RECV_FRAMES 20
-# define NUM_RECV_BUFFS 27
-# define RECV_BUFF_SIZE 512
-# define XMIT_BUFF_SIZE 1514
-# define NUM_XMIT_BUFFS 1
-# define ONLY_ONE_XMIT_BUF
-#else
-# define NUM_RECV_FRAMES 4
-# define NUM_RECV_BUFFS 4
-# define RECV_BUFF_SIZE 1536
-# define XMIT_BUFF_SIZE 1536
-# define NUM_XMIT_BUFFS 1
-# define ONLY_ONE_XMIT_BUF
-# define NO_NOPCOMMANDS
-#endif
+ * if you don't know the driver, better do not change this values: */
+
+#define RECV_BUFF_SIZE 1524 /* slightly oversized */
+#define XMIT_BUFF_SIZE 1524 /* slightly oversized */
+#define NUM_XMIT_BUFFS 1 /* config for both, 8K and 16K shmem */
+#define NUM_RECV_BUFFS_8 4 /* config for 8K shared mem */
+#define NUM_RECV_BUFFS_16 9 /* config for 16K shared mem */
+#define NO_NOPCOMMANDS /* only possible with NUM_XMIT_BUFFS=1 */
+
+/**************************************************************************/
#define DELAY(x) {int i=jiffies; \
if(loops_per_sec == 1) \
__delay((loops_per_sec>>5)*x); \
}
+/* a much shorter delay: */
+#define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); }
+
+/* wait for command with timeout: */
+#define WAIT_4_SCB_CMD() { int i; \
+ for(i=0;i<1024;i++) { \
+ if(!p->scb->cmd) break; \
+ DELAY_16(); \
+ if(i == 1023) { \
+ printk("%s: scb_cmd timed out .. resetting i82586\n",dev->name); \
+ ni_reset586(); } } }
+
extern void autoirq_setup(int waittime);
-extern int autoirq_report(int waittime);
+extern int autoirq_report(int waittime);
extern void *irq2dev_map[16];
-#ifndef HAVE_PORTRESERVE
-#define check_region(ioaddr, size) 0
-#define request_region(ioaddr, size,name) do ; while (0)
-#endif
-
#define NI52_TOTAL_SIZE 16
#define NI52_ADDR0 0x02
#define NI52_ADDR1 0x07
#define NI52_ADDR2 0x01
+#ifndef HAVE_PORTRESERVE
+#define check_region(ioaddr, size) 0
+#define request_region(ioaddr, size,name) do ; while (0)
+#endif
+
static int ni52_probe1(struct device *dev,int ioaddr);
-static void ni52_interrupt(int irq, struct pt_regs *regs);
+static void ni52_interrupt(int irq,struct pt_regs *reg_ptr);
static int ni52_open(struct device *dev);
static int ni52_close(struct device *dev);
static int ni52_send_packet(struct sk_buff *,struct device *);
-static struct enet_statistics *ni52_get_stats(struct device *dev);
-static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+static struct enet_statistics *ni52_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
/* helper-functions */
-static int init586(struct device *dev);
+static int init586(struct device *dev,int num_addrs,void *addrs);
static int check586(struct device *dev,char *where,unsigned size);
static void alloc586(struct device *dev);
static void startrecv586(struct device *dev);
{
struct enet_statistics stats;
unsigned long base;
- char *memtop,*max_cbuff32,*min_cbuff32,*max_cbuff24;
- volatile struct rbd_struct *rbd_last;
+ char *memtop;
volatile struct rfd_struct *rfd_last,*rfd_top,*rfd_first;
volatile struct scp_struct *scp; /* volatile is important */
volatile struct iscp_struct *iscp; /* volatile is important */
volatile struct scb_struct *scb; /* volatile is important */
volatile struct tbd_struct *xmit_buffs[NUM_XMIT_BUFFS];
volatile struct transmit_cmd_struct *xmit_cmds[NUM_XMIT_BUFFS];
-#ifdef ONLY_ONE_XMIT_BUF
+#if (NUM_XMIT_BUFFS == 1)
volatile struct nop_cmd_struct *nop_cmds[2];
#else
volatile struct nop_cmd_struct *nop_cmds[NUM_XMIT_BUFFS];
#endif
- volatile int nop_point;
+ volatile int nop_point,num_recv_buffs;
volatile char *xmit_cbuffs[NUM_XMIT_BUFFS];
volatile int xmit_count,xmit_last;
};
static int ni52_open(struct device *dev)
{
alloc586(dev);
- init586(dev);
+ init586(dev,0,NULL);
startrecv586(dev);
if(request_irq(dev->irq, &ni52_interrupt,0,"ni52"))
p->memtop = where + size;
p->scp = (struct scp_struct *)(p->base + SCP_DEFAULT_ADDRESS);
memset((char *)p->scp,0, sizeof(struct scp_struct));
- p->scp->sysbus = SYSBUSVAL; /* 1 = 8Bit-Bus */
+ p->scp->sysbus = SYSBUSVAL; /* 1 = 8Bit-Bus, 0 = 16 Bit */
iscp_addrs[0] = where;
iscp_addrs[1]= (char *) p->scp - sizeof(struct iscp_struct);
ni_attn586();
DELAY(2); /* wait a while... */
- if(p->iscp->busy)
+ if(p->iscp->busy) /* i82586 clears 'busy' after succesful init */
return 0;
}
return 1;
{
struct priv *p = (struct priv *) dev->priv;
+ ni_reset586();
+ DELAY(2);
+
p->scp = (struct scp_struct *) (p->base + SCP_DEFAULT_ADDRESS);
p->scb = (struct scb_struct *) (dev->mem_start);
p->iscp = (struct iscp_struct *) ((char *)p->scp - sizeof(struct iscp_struct));
ni_reset586();
ni_attn586();
-#ifdef DEBUG
DELAY(2);
if(p->iscp->busy)
printk("%s: Init-Problems (alloc).\n",dev->name);
-#endif
memset((char *)p->scb,0,sizeof(struct scb_struct));
}
static int ni52_probe1(struct device *dev,int ioaddr)
{
- long memaddrs[] = { 0xd0000,0xd2000,0xd4000,0xd6000,0xd8000, 0 };
+ long memaddrs[] = { 0xd0000,0xd2000,0xc8000,0xca000,0xd4000,0xd6000,0xd8000, 0 };
int i,size;
for(i=0;i<ETH_ALEN;i++)
*/
if(dev->mem_start != 0) /* no auto-mem-probe */
{
- size = 0x4000;
+ size = 0x4000; /* check for 16K mem */
if(!check586(dev,(char *) dev->mem_start,size)) {
- size = 0x2000;
+ size = 0x2000; /* check for 8K mem */
if(!check586(dev,(char *) dev->mem_start,size)) {
printk("?memprobe, Can't find memory at 0x%lx!\n",dev->mem_start);
return ENODEV;
return ENODEV;
}
dev->mem_start = memaddrs[i];
- size = 0x2000;
+ size = 0x2000; /* check for 8K mem */
if(check586(dev,(char *)dev->mem_start,size)) /* 8K-check */
break;
- size = 0x4000;
+ size = 0x4000; /* check for 16K mem */
if(check586(dev,(char *)dev->mem_start,size)) /* 16K-check */
break;
}
}
-
+ dev->mem_end = dev->mem_start + size; /* set mem_end showed by 'ifconfig' */
+
((struct priv *) (dev->priv))->base = dev->mem_start + size - 0x01000000;
alloc586(dev);
+ /* set number of receive-buffs according to memsize */
+ if(size == 0x2000)
+ ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_8;
+ else
+ ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_16;
+
printk("Memaddr: 0x%lx, Memsize: %d, ",dev->mem_start,size);
if(dev->irq < 2)
* needs a correct 'allocated' memory
*/
-static int init586(struct device *dev)
+static int init586(struct device *dev,int num_addrs,void *addrs)
{
void *ptr;
unsigned long s;
volatile struct configure_cmd_struct *cfg_cmd;
volatile struct iasetup_cmd_struct *ias_cmd;
volatile struct tdr_cmd_struct *tdr_cmd;
+ volatile struct mcsetup_cmd_struct *mc_cmd;
ptr = (void *) ((char *)p->scb + sizeof(struct scb_struct));
cfg_cmd = (struct configure_cmd_struct *)ptr; /* configure-command */
-
- cfg_cmd->byte_cnt = 0x04; /* number of cfg bytes */
- cfg_cmd->fifo = 0xc8; /* fifo-limit (8=tx:32/rx:64) | monitor */
- cfg_cmd->sav_bf = 0x40; /* hold or discard bad recv frames (bit 7) */
- cfg_cmd->adr_len = 0x2e; /* addr_len |!src_insert |pre-len |loopback */
- cfg_cmd->cmd_status = 0;
- cfg_cmd->cmd_cmd = CMD_CONFIGURE | CMD_LAST;
- cfg_cmd->cmd_link = 0xffff;
+ cfg_cmd->cmd_status = 0;
+ cfg_cmd->cmd_cmd = CMD_CONFIGURE | CMD_LAST;
+ cfg_cmd->cmd_link = 0xffff;
+ cfg_cmd->byte_cnt = 0x0a; /* number of cfg bytes */
+ cfg_cmd->fifo = 0x08; /* fifo-limit (8=tx:32/rx:64) */
+ cfg_cmd->sav_bf = 0x40; /* hold or discard bad recv frames (bit 7) */
+ cfg_cmd->adr_len = 0x2e; /* addr_len |!src_insert |pre-len |loopback */
+ cfg_cmd->priority = 0x00;
+ cfg_cmd->ifs = 0x60;
+ cfg_cmd->time_low = 0x00;
+ cfg_cmd->time_high = 0xf2;
+ cfg_cmd->promisc = (num_addrs < 0) ? 1 : 0; /* promisc on/off */
+ cfg_cmd->carr_coll = 0x00;
+
p->scb->cbl_offset = make16(cfg_cmd);
p->scb->cmd = CUC_START; /* cmd.-unit start */
/*
* alloc nop/xmit-cmds
*/
-#ifdef ONLY_ONE_XMIT_BUF
+#if (NUM_XMIT_BUFFS == 1)
for(i=0;i<2;i++)
{
p->nop_cmds[i] = (struct nop_cmd_struct *)ptr;
- p->nop_cmds[i]->cmd_cmd = 0;
+ p->nop_cmds[i]->cmd_cmd = CMD_NOP;
p->nop_cmds[i]->cmd_status = 0;
p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i]));
- ptr += sizeof(struct nop_cmd_struct);
+ ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
}
p->xmit_cmds[0] = (struct transmit_cmd_struct *)ptr; /* transmit cmd/buff 0 */
- ptr += sizeof(struct transmit_cmd_struct);
+ ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
#else
for(i=0;i<NUM_XMIT_BUFFS;i++)
{
p->nop_cmds[i] = (struct nop_cmd_struct *)ptr;
- p->nop_cmds[i]->cmd_cmd = 0;
+ p->nop_cmds[i]->cmd_cmd = CMD_NOP;
p->nop_cmds[i]->cmd_status = 0;
p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i]));
- ptr += sizeof(struct nop_cmd_struct);
- p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /* transmit cmd/buff 0 */
- ptr += sizeof(struct transmit_cmd_struct);
+ ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
+ p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /*transmit cmd/buff 0*/
+ ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
}
#endif
ptr = alloc_rfa(dev,(void *)ptr); /* init receive-frame-area */
- /*
- * alloc xmit-buffs
- */
+ /*
+ * Multicast setup
+ */
+
+ if(num_addrs > 0)
+ { /* I don't understand this: do we really need memory after the init? */
+ int len = ((char *) p->iscp - (char *) ptr - 8) / 6;
+ if(len <= 0)
+ {
+ printk("%s: Ooooops, no memory for MC-Setup!\n",dev->name);
+ }
+ else
+ {
+ if(len < num_addrs)
+ {
+ num_addrs = len;
+ printk("%s: Sorry, can only apply %d MC-Addresse(s).\n",dev->name,num_addrs);
+ }
+ mc_cmd = (struct mcsetup_cmd_struct *) ptr;
+ mc_cmd->cmd_status = 0;
+ mc_cmd->cmd_cmd = CMD_MCSETUP | CMD_LAST;
+ mc_cmd->cmd_link = 0xffff;
+ mc_cmd->mc_cnt = num_addrs * 6;
+ for(i=0;i<num_addrs;i++)
+ memcpy((char *) mc_cmd->mc_list[i],((char (*)[6]) addrs)[i],6);
+ p->scb->cbl_offset = make16(mc_cmd);
+ p->scb->cmd = CUC_START;
+ ni_attn586();
+ s = jiffies;
+ while(!(mc_cmd->cmd_status & STAT_COMPL))
+ if(jiffies - s > 30)
+ break;
+ if(!(mc_cmd->cmd_status & STAT_COMPL))
+ printk("%s: Can't apply multicast-address-list.\n",dev->name);
+ }
+ }
+
+ /*
+ * alloc xmit-buffs / init xmit_cmds
+ */
for(i=0;i<NUM_XMIT_BUFFS;i++)
{
p->xmit_cbuffs[i] = (char *)ptr; /* char-buffs */
- ptr += XMIT_BUFF_SIZE;
+ ptr = (char *) ptr + XMIT_BUFF_SIZE;
p->xmit_buffs[i] = (struct tbd_struct *)ptr; /* TBD */
- ptr += sizeof(struct tbd_struct);
+ ptr = (char *) ptr + sizeof(struct tbd_struct);
if((void *)ptr > (void *)p->iscp)
{
printk("%s: not enough shared-mem for your configuration!\n",dev->name);
memset((char *)(p->xmit_cmds[i]) ,0, sizeof(struct transmit_cmd_struct));
memset((char *)(p->xmit_buffs[i]),0, sizeof(struct tbd_struct));
p->xmit_cmds[i]->cmd_status = STAT_COMPL;
+ p->xmit_cmds[i]->cmd_cmd = CMD_XMIT | CMD_INT;
p->xmit_cmds[i]->tbd_offset = make16((p->xmit_buffs[i]));
p->xmit_buffs[i]->next = 0xffff;
p->xmit_buffs[i]->buffer = make24((p->xmit_cbuffs[i]));
}
-
+
p->xmit_count = 0;
p->xmit_last = 0;
#ifndef NO_NOPCOMMANDS
p->scb->cbl_offset = make16(p->nop_cmds[0]);
p->scb->cmd = CUC_START;
ni_attn586();
- while(p->scb->cmd);
+ WAIT_4_SCB_CMD();
#else
-/*
- p->nop_cmds[0]->cmd_link = make16(p->nop_cmds[1]);
- p->nop_cmds[1]->cmd_link = make16(p->xmit_cmds[0]);
-*/
p->xmit_cmds[0]->cmd_link = 0xffff;
p->xmit_cmds[0]->cmd_cmd = CMD_XMIT | CMD_LAST | CMD_INT;
#endif
}
/******************************************************
- * This is a helper routine for ni52_nr_int() and init586().
+ * This is a helper routine for ni52_rnr_int() and init586().
* It sets up the Receive Frame Area (RFA).
*/
int i;
struct priv *p = (struct priv *) dev->priv;
- memset((char *) rfd,0,sizeof(struct rfd_struct)*NUM_RECV_FRAMES);
+ memset((char *) rfd,0,sizeof(struct rfd_struct)*p->num_recv_buffs);
p->rfd_first = rfd;
- for(i = 0; i < NUM_RECV_FRAMES; i++)
- rfd[i].next = make16(rfd + (i+1) % NUM_RECV_FRAMES);
- rfd[NUM_RECV_FRAMES-1].last = RFD_LAST; /* set EOL (no RU suspend) */
+ for(i = 0; i < p->num_recv_buffs; i++)
+ rfd[i].next = make16(rfd + (i+1) % p->num_recv_buffs);
+ rfd[p->num_recv_buffs-1].last = RFD_SUSP; /* RU suspend */
- ptr = (char *) (rfd + NUM_RECV_FRAMES);
+ ptr = (void *) (rfd + p->num_recv_buffs);
rbd = (struct rbd_struct *) ptr;
- ptr += sizeof(struct rbd_struct)*NUM_RECV_BUFFS;
+ ptr = (void *) (rbd + p->num_recv_buffs);
/* clr descriptors */
- memset((char *) rbd,0,sizeof(struct rbd_struct)*NUM_RECV_BUFFS);
+ memset((char *) rbd,0,sizeof(struct rbd_struct)*p->num_recv_buffs);
- p->min_cbuff32 = ptr;
- for(i=0;i<NUM_RECV_BUFFS;i++)
+ for(i=0;i<p->num_recv_buffs;i++)
{
- rbd[i].next = make16((rbd + (i+1) % NUM_RECV_BUFFS));
+ rbd[i].next = make16((rbd + (i+1) % p->num_recv_buffs));
rbd[i].size = RECV_BUFF_SIZE;
rbd[i].buffer = make24(ptr);
- ptr += RECV_BUFF_SIZE;
+ ptr = (char *) ptr + RECV_BUFF_SIZE;
}
- rbd[NUM_RECV_BUFFS-1].size |= RBD_LAST; /* set eol */
- p->max_cbuff32 = ptr;
- p->max_cbuff24 = make24(p->max_cbuff32);
-
+
p->rfd_top = p->rfd_first;
- p->rfd_last = p->rfd_first + NUM_RECV_FRAMES - 1;
+ p->rfd_last = p->rfd_first + p->num_recv_buffs - 1;
- p->rbd_last = rbd + NUM_RECV_BUFFS - 1;
-
p->scb->rfa_offset = make16(p->rfd_first);
p->rfd_first->rbd_offset = make16(rbd);
* Interrupt Handler ...
*/
-static void ni52_interrupt(int irq, struct pt_regs *regs)
+static void ni52_interrupt(int irq,struct pt_regs *reg_ptr)
{
struct device *dev = (struct device *) irq2dev_map[irq];
unsigned short stat;
- int pd = 0;
struct priv *p;
-#ifdef DEBUG2
- printk("(1)");
-#endif
-
if (dev == NULL) {
- printk ("ni52-interrupt: irq %d for unknown device.\n", irq);
+ printk ("ni52-interrupt: irq %d for unknown device.\n",(int) -(((struct pt_regs *)reg_ptr)->orig_eax+2));
return;
}
p = (struct priv *) dev->priv;
- if(dev->interrupt)
- {
- printk("(ni52-I)");
- return;
- }
-
dev->interrupt = 1;
while((stat=p->scb->status & STAT_MASK))
p->scb->cmd = stat;
ni_attn586(); /* ack inter. */
- if(pd)
- printk("ni52-%04x/%04x-",(int) stat,(int) p->scb->status); /* debug */
+ if(stat & STAT_CX) /* command with I-bit set complete */
+ ni52_xmt_int(dev);
- if(stat & (STAT_FR | STAT_RNR))
+ if(stat & STAT_FR) /* received a frame */
ni52_rcv_int(dev);
- if(stat & STAT_CX)
- ni52_xmt_int(dev);
-
#ifndef NO_NOPCOMMANDS
- if(stat & STAT_CNA)
-#else
- if( (stat & STAT_CNA) && !(stat & STAT_CX) )
-#endif
- printk("%s: oops! CU has left active state. stat: %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
-
- if(stat & STAT_RNR)
+ if(stat & STAT_CNA) /* CU went 'not ready' */
{
- printk("%s: rnr: %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
- ni52_rnr_int(dev);
- pd = 1; /* local debug on */
+ if(dev->start)
+ printk("%s: oops! CU has left active state. stat: %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
}
-
-#ifdef DEBUG2
- pd++;
#endif
- while(p->scb->cmd)
- {
- int i; /* wait for ack. (ni52_xmt_int can be faster than ack!!) */
- for(i=0;i<200;i++);
- }
- }
-
-#ifdef DEBUG
- {
- static int old_ovr=0;
- int l;
- if((l = p->scb->ovrn_errs - old_ovr))
+ if(stat & STAT_RNR) /* RU went 'not ready' */
{
- if(l > 0)
- p->stats.rx_over_errors += l;
+ if(p->scb->status & RU_SUSPEND) /* special case: RU_SUSPEND */
+ {
+ WAIT_4_SCB_CMD();
+ p->scb->cmd = RUC_RESUME;
+ ni_attn586();
+ }
else
- old_ovr=0;
+ {
+ printk("%s: Receiver-Unit went 'NOT READY': %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
+ ni52_rnr_int(dev);
+ }
}
+ WAIT_4_SCB_CMD(); /* wait for ack. (ni52_xmt_int can be faster than ack!!) */
+ if(p->scb->cmd) /* timed out? */
+ break;
}
-#endif
-
-#ifdef DEBUG2
- printk("(2)");
-#endif
dev->interrupt = 0;
}
static void ni52_rcv_int(struct device *dev)
{
int status;
- unsigned short totlen,pnt;
+ unsigned short totlen;
struct sk_buff *skb;
- struct rbd_struct *rbd,*rbd_first;
+ struct rbd_struct *rbd;
struct priv *p = (struct priv *) dev->priv;
for(;(status = p->rfd_top->status) & STAT_COMPL;)
{
- rbd = rbd_first = (struct rbd_struct *) make32(p->rfd_top->rbd_offset);
+ rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset);
-#ifdef DEBUG1
- {
- struct rbd_struct *rbd1 = rbd;
- if(rbd1==p->rbd_last)
- printk("L");
- printk("S:%04x/%x/%02x >",(int) rbd1->status,(int) rbd1->size>>12,(int)((unsigned long) rbd1 & 0xff));
- rbd1 = (struct rbd_struct *) make32(rbd1->next);
- for(;rbd1 != rbd_first;rbd1 = (struct rbd_struct *) make32(rbd1->next))
- {
- if(rbd1 == p->rbd_last)
- printk("L:");
- printk("%04x/%x-",(int) rbd1->status>>12,(int) rbd1->size>>12);
- }
- printk("< ");
- }
+ if(status & STAT_OK) /* frame received without error? */
{
- struct rfd_struct *rfd1 = p->rfd_top;
- if(rfd1==p->rfd_last)
- printk("L");
- printk("S:%04x/%x/%02x >",(int) rfd1->status,(int) rfd1->last>>12,(int)((unsigned long) rfd1 & 0xff));
- rfd1 = (struct rfd_struct *) make32(rfd1->next);
- for(;rfd1 != p->rfd_top;rfd1 = (struct rfd_struct *) make32(rfd1->next))
+ if( (totlen = rbd->status) & RBD_LAST) /* the first and the last buffer? */
{
- if(rfd1 == p->rfd_last)
- printk("L:");
- printk("%x/%x-",(int) rfd1->status>>12,(int) rfd1->last>>12);
- }
- printk("<\n");
- }
-#endif
-
- p->rfd_top->status = 0;
- p->rfd_top->last = RFD_LAST;
- p->rfd_last->last = 0; /* delete RFD_LAST, no RU suspend */
- p->rfd_last = p->rfd_top;
- p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next);
-
- if(status & RFD_ERRMASK)
- printk("%s: RFD-Error ... status: %04x.\n",dev->name,status);
-
- if(status & STAT_OK)
- {
- for(totlen=0; !(rbd->status & RBD_LAST); rbd=(struct rbd_struct *) make32(rbd->next)) {
- totlen += RECV_BUFF_SIZE;
+ totlen &= RBD_MASK; /* length of this frame */
rbd->status = 0;
- }
- totlen += rbd->status & RBD_MASK;
- rbd->status = 0;
-
- skb = (struct sk_buff *) alloc_skb(totlen, GFP_ATOMIC);
-
- if (skb != NULL) /* copy header */
- {
- skb->len = totlen;
- skb->dev = dev;
-
- if(rbd->buffer < rbd_first->buffer)
+ skb = (struct sk_buff *) alloc_skb(totlen, GFP_ATOMIC);
+ if(skb != NULL)
{
- pnt = p->max_cbuff24 - rbd_first->buffer;
- memcpy( (char *) skb->data,p->max_cbuff32-pnt,pnt);
- memcpy( (char *) skb->data+pnt,p->min_cbuff32,totlen-pnt);
+ skb->len = totlen;
+ skb->dev = dev;
+ memcpy( (char *) skb->data,(char *) p->base+(unsigned long) rbd->buffer, totlen);
+ netif_rx(skb);
+ p->stats.rx_packets++;
}
else
- memcpy( (char *) skb->data,(char *) p->base+(unsigned long) rbd_first->buffer, totlen);
-
- rbd->size |= RBD_LAST;
- p->rbd_last->size &= ~RBD_LAST;
- p->rbd_last = rbd;
-
- netif_rx(skb);
- p->stats.rx_packets++;
+ p->stats.rx_dropped++;
}
else
{
- rbd->size |= RBD_LAST;
- p->rbd_last->size &= ~RBD_LAST;
- p->rbd_last = rbd;
+ printk("%s: received oversized frame.\n",dev->name);
+ p->stats.rx_dropped++;
}
}
else /* frame !(ok), only with 'save-bad-frames' */
printk("%s: oops! rfd-error-status: %04x\n",dev->name,status);
p->stats.rx_errors++;
}
+ p->rfd_top->status = 0;
+ p->rfd_top->last = RFD_SUSP;
+ p->rfd_last->last = 0; /* delete RU_SUSP */
+ p->rfd_last = p->rfd_top;
+ p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next); /* step to next RFD */
}
}
/**********************************************************
- * I never got this error , (which should occur if someone
- * wants to blast your machine) so I couldn't debug it for now.
- * but we _try_ to fix the receiver not ready int.
+ * handle 'Receiver went not ready'.
*/
static void ni52_rnr_int(struct device *dev)
p->stats.rx_errors++;
- while(p->scb->cmd); /* wait for the last cmd */
- p->scb->cmd = RUC_ABORT;
+ WAIT_4_SCB_CMD(); /* wait for the last cmd */
+ p->scb->cmd = RUC_ABORT; /* usually the RU is in the 'no ressource'-state .. abort it now. */
ni_attn586();
- while(p->scb->cmd); /* wait for accept cmd. */
+ WAIT_4_SCB_CMD(); /* wait for accept cmd. */
alloc_rfa(dev,(char *)p->rfd_first);
- startrecv586(dev); /* restart */
+ startrecv586(dev); /* restart RU */
+
+ printk("%s: Receive-Unit restarted. Status: %04x\n",dev->name,p->scb->status);
+
}
/**********************************************************
int status;
struct priv *p = (struct priv *) dev->priv;
-/*
- if(!(p->xmit_cmds[0]->cmd_status & STAT_COMPL))
- return;
-*/
+ status = p->xmit_cmds[p->xmit_last]->cmd_status;
+ if(!(status & STAT_COMPL))
+ printk("%s: strange .. xmit-int without a 'COMPLETE'\n",dev->name);
- if( (status=p->xmit_cmds[p->xmit_last]->cmd_status) & STAT_OK)
+ if(status & STAT_OK)
{
p->stats.tx_packets++;
p->stats.collisions += (status & TCMD_MAXCOLLMASK);
- dev->tbusy = 0;
- mark_bh(NET_BH);
}
else
{
else if(status & TCMD_LOSTCTS)
printk("%s: loss of CTS detected.\n",dev->name);
else if(status & TCMD_UNDERRUN) {
+ p->stats.tx_fifo_errors++;
printk("%s: DMA underrun detected.\n",dev->name);
}
else if(status & TCMD_MAXCOLL) {
}
}
-#ifndef ONLY_ONE_XMIT_BUF
+#if (NUM_XMIT_BUFFS != 1)
if( (++p->xmit_last) == NUM_XMIT_BUFFS)
p->xmit_last = 0;
#endif
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
}
/***********************************************************
p->scb->rfa_offset = make16(p->rfd_first);
p->scb->cmd = RUC_START;
- ni_attn586(); /* start cmd. */
- while(p->scb->cmd); /* wait for accept cmd. (no timeout!!) */
-
- DELAY(2); /* isn't necess. */
-
- p->scb->cmd = p->scb->status & STAT_MASK;
- ni_attn586(); /* ack interr */
+ ni_attn586(); /* start cmd. */
+ WAIT_4_SCB_CMD(); /* wait for accept cmd. (no timeout!!) */
}
/******************************************************
static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
{
- int len;
+ int len,i;
#ifndef NO_NOPCOMMANDS
int next_nop;
#endif
if(dev->tbusy)
{
int tickssofar = jiffies - dev->trans_start;
-
- if (tickssofar < 30)
+ if (tickssofar < 5)
return 1;
+ if(p->scb->status & CU_ACTIVE) /* COMMAND-UNIT active? */
+ {
+ dev->tbusy = 0;
#ifdef DEBUG
- printk("%s: xmitter timed out, try to restart! stat: %04x\n",dev->name,p->scb->status);
- printk("%s: command-stats: %04x %04x\n",dev->name,p->xmit_cmds[0]->cmd_status,p->xmit_cmds[1]->cmd_status);
+ printk("%s: strange ... timeout with CU active?!?\n",dev->name);
+ printk("%s: X0: %04x N0: %04x N1: %04x %d\n",dev->name,(int)p->xmit_cmds[0]->cmd_status,(int)p->nop_cmds[0]->cmd_status,(int)p->nop_cmds[1]->cmd_status,(int)p->nop_point);
#endif
-
- ni52_close(dev);
- ni52_open(dev);
+ p->scb->cmd = CUC_ABORT;
+ ni_attn586();
+ WAIT_4_SCB_CMD();
+ p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]);
+ p->scb->cmd = CUC_START;
+ ni_attn586();
+ WAIT_4_SCB_CMD();
+ dev->trans_start = jiffies;
+ return 0;
+ }
+ else
+ {
+#ifdef DEBUG
+ printk("%s: xmitter timed out, try to restart! stat: %04x\n",dev->name,p->scb->status);
+ printk("%s: command-stats: %04x %04x\n",dev->name,p->xmit_cmds[0]->cmd_status,p->xmit_cmds[1]->cmd_status);
+#endif
+ ni52_close(dev);
+ ni52_open(dev);
+ }
dev->trans_start = jiffies;
+ return 0;
}
if(skb == NULL)
if (skb->len <= 0)
return 0;
+ if(skb->len > XMIT_BUFF_SIZE)
+ {
+ printk("%s: Sorry, max. framelength is %d bytes. The length of your frame is %ld bytes.\n",dev->name,XMIT_BUFF_SIZE,skb->len);
+ return 0;
+ }
if (set_bit(0, (void*)&dev->tbusy) != 0)
printk("%s: Transmitter access conflict.\n", dev->name);
memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len);
len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
-#ifdef ONLY_ONE_XMIT_BUF
+#if (NUM_XMIT_BUFFS == 1)
# ifdef NO_NOPCOMMANDS
p->xmit_buffs[0]->size = TBD_LAST | len;
- p->xmit_cmds[0]->cmd_status = 0;
- p->scb->cbl_offset = make16(p->xmit_cmds[0]);
- p->scb->cmd = CUC_START;
-
- dev->trans_start = jiffies;
- ni_attn586();
- while(p->scb->cmd)
- for(len=0;len<256;len++);
-
- /* DELAY(1); */ /* TEST;TEST;TEST */
+ for(i=0;i<16;i++)
+ {
+ p->scb->cbl_offset = make16(p->xmit_cmds[0]);
+ p->scb->cmd = CUC_START;
+ p->xmit_cmds[0]->cmd_status = 0;
+
+ ni_attn586();
+ dev->trans_start = jiffies;
+ if(!i)
+ dev_kfree_skb(skb,FREE_WRITE);
+ WAIT_4_SCB_CMD();
+ if( (p->scb->status & CU_ACTIVE)) /* test it, because CU sometimes doesn't start immediately */
+ break;
+ if(p->xmit_cmds[0]->cmd_status)
+ break;
+ if(i==15)
+ printk("%s: Can't start transmit-command.\n",dev->name);
+ }
# else
next_nop = (p->nop_point + 1) & 0x1;
p->xmit_buffs[0]->size = TBD_LAST | len;
- p->xmit_cmds[0]->cmd_cmd = CMD_XMIT | CMD_INT;
- p->xmit_cmds[0]->cmd_status = 0;
p->xmit_cmds[0]->cmd_link = p->nop_cmds[next_nop]->cmd_link
= make16((p->nop_cmds[next_nop]));
- p->nop_cmds[next_nop]->cmd_status = 0;
+ p->xmit_cmds[0]->cmd_status = p->nop_cmds[next_nop]->cmd_status = 0;
p->nop_cmds[p->nop_point]->cmd_link = make16((p->xmit_cmds[0]));
dev->trans_start = jiffies;
p->nop_point = next_nop;
+ dev_kfree_skb(skb,FREE_WRITE);
# endif
#else
p->xmit_buffs[p->xmit_count]->size = TBD_LAST | len;
if( (next_nop = p->xmit_count + 1) == NUM_XMIT_BUFFS )
next_nop = 0;
- p->xmit_cmds[p->xmit_count]->cmd_cmd = CMD_XMIT | CMD_INT;
p->xmit_cmds[p->xmit_count]->cmd_status = 0;
p->xmit_cmds[p->xmit_count]->cmd_link = p->nop_cmds[next_nop]->cmd_link
= make16((p->nop_cmds[next_nop]));
if(p->xmit_count != p->xmit_last)
dev->tbusy = 0;
sti();
+ dev_kfree_skb(skb,FREE_WRITE);
#endif
}
-
- dev_kfree_skb(skb,FREE_WRITE);
-
return 0;
}
+/*******************************************
+ * Someone wanna have the statistics
+ */
+
static struct enet_statistics *ni52_get_stats(struct device *dev)
{
struct priv *p = (struct priv *) dev->priv;
-#ifdef DEBUG3
- printk("ni52: errs, crc %d, align %d, resource %d, ovrn %d.\n",(int) p->scb->crc_errs,(int) p->scb->aln_errs,(int) p->scb->rsc_errs,(int) p->scb->ovrn_errs);
-#endif
+ unsigned short crc,aln,rsc,ovrn;
+
+ crc = p->scb->crc_errs; /* get error-statistic from the ni82586 */
+ p->scb->crc_errs -= crc;
+ aln = p->scb->aln_errs;
+ p->scb->aln_errs -= aln;
+ rsc = p->scb->rsc_errs;
+ p->scb->rsc_errs -= rsc;
+ ovrn = p->scb->ovrn_errs;
+ p->scb->ovrn_errs -= ovrn;
+
+ p->stats.rx_crc_errors += crc;
+ p->stats.rx_fifo_errors += ovrn;
+ p->stats.rx_frame_errors += aln;
+ p->stats.rx_dropped += rsc;
+
return &p->stats;
}
+/********************************************************
+ * Set MC list ..
+ */
+
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs)
{
-/*
- struct priv *p = (struct priv *) dev->priv;
- volatile struct configure_cmd_struct *cfg_cmd;
-*/
-
- if(!num_addrs)
- printk("%s: Currently, the Ni52 driver doesn't support promiscuous or multicast mode.\n",dev->name);
-
-#if 0
- p->scb->cmd = CUC_SUSPEND;
- ni_attn586();
- while(p->scb->cmd);
- p->scb->cmd = RUC_SUSPEND;
- ni_attn586();
- while(p->scb->cmd);
-
- cfg_cmd = (struct configure_cmd_struct *) p->xmit_cbuffs[0]; /* we're using a transmitcommand */
-
- cfg_cmd->cmd_status = 0;
- cfg_cmd->cmd_cmd = CMD_CONFIGURE | CMD_LAST;
- cfg_cmd->cmd_link = 0xffff;
-
- cfg_cmd->byte_cnt = 0x0a; /* number of cfg bytes */
- cfg_cmd->fifo = 0x08; /* fifo-limit (8=tx:32/rx:64) */
- cfg_cmd->sav_bf = 0x40; /* hold or discard bad recv frames (bit 7) */
- cfg_cmd->adr_len = 0x2e; /* addr_len |!src_insert |pre-len |loopback */
- cfg_cmd->priority = 0x00;
- cfg_cmd->ifd = 0x60;
- cfg_cmd->time_low = 0x00;
- cfg_cmd->time_high = 0xf2;
- cfg_cmd->promisc = 0x01; /* promisc on */
- cfg_cmd->carr_coll = 0x00;
-
- p->scb->cbl_offset = make16(cfg_cmd);
-
- p->scb->cmd = CUC_START; /* cmd.-unit start */
- ni_attn586();
- while(p->scb->cmd);
-
- p->scb->cbl_offset = p->nop_cmds[0]->cmd_link = make16(p->nop_cmds[0]);
- p->scb->cmd = CUC_START;
- ni_atthn586();
- while(p->scb->cmd);
- p->scb->cmd = RUC_RESUME;
- ni_atthn586();
- while(p->scb->cmd);
-#endif
+ if(!dev->start && !num_addrs)
+ {
+ printk("%s: Can't apply promiscous/multicastmode to a not running interface.\n",dev->name);
+ return;
+ }
+ dev->start = 0;
+ alloc586(dev);
+ init586(dev,num_addrs,addrs);
+ startrecv586(dev);
+ dev->start = 1;
}
+
+/*
+ * END: linux/drivers/net/ni52.c
+ */
unsigned short cmd_cmd;
unsigned short cmd_link;
unsigned short mc_cnt; /* number of bytes in the MC-List */
- unsigned char mc_list[16][6]; /* the list for 16 entries */
+ unsigned char mc_list[0][6]; /* pointer to 6 bytes entries */
};
/*
#define NEW_TTY_DRIVERS /* */
#define OPTIMIZE_FLAG_TIME ((HZ * 3)/2) /* */
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/ppp.h>
-#include <ip.h>
-#include <tcp.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
#include "slhc.h"
PRINTKN (2,(KERN_INFO "ppp: channel %s open\n", ppp->dev->name));
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
return (ppp->line);
}
PRINTKN (2,(KERN_INFO "ppp: channel %s going down for IP packets!\n",
dev->name));
CHECK_PPP(-ENXIO);
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
return 0;
}
set_fs (old_fs);
}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+static struct device dev_ppp[PPP_NRUNIT] = {
+ {
+ "ppp0", /* ppp */
+ 0, 0, 0, 0, /* memory */
+ 0, 0, /* base, irq */
+ 0, 0, 0, NULL, ppp_init,
+ },
+ { "ppp1" , 0, 0, 0, 0, 1, 0, 0, 0, 0, NULL, ppp_init },
+ { "ppp2" , 0, 0, 0, 0, 2, 0, 0, 0, 0, NULL, ppp_init },
+ { "ppp3" , 0, 0, 0, 0, 3, 0, 0, 0, 0, NULL, ppp_init },
+};
+
+int
+init_module(void)
+{
+ int err;
+ int i;
+
+ for (i = 0; i < PPP_NRUNIT; i++) {
+ if ((err = register_netdev(&dev_ppp[i]))) {
+ if (err == -EEXIST) {
+ printk("PPP: devices already present. Module not loaded.\n");
+ }
+ return err;
+ }
+ }
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int i;
+
+ if (MOD_IN_USE) {
+ printk("PPP: device busy, remove delayed\n");
+ return;
+ }
+ for (i = 0; i < PPP_NRUNIT; i++) {
+ unregister_netdev(&dev_ppp[i]);
+ }
+ if ((i = tty_register_ldisc(N_PPP, NULL))) {
+ printk("PPP: can't unregister line discipline (err = %d)\n", i);
+ }
+}
+
+#endif
#include <linux/if_arp.h>
#include "slip.h"
#ifdef CONFIG_INET
-#include "ip.h"
-#include "tcp.h"
+#include <linux/ip.h>
+#include <linux/tcp.h>
#include "slhc.h"
#endif
unsigned char psa_univ_local_sel; /* Universal Local Selection */
#define PSA_UNIVERSAL 0 /* Universal (factory) */
#define PSA_LOCAL 1 /* Local */
- unsigned char psa_comp_number; /* Compatability Number: */
+ unsigned char psa_comp_number; /* Compatibility Number: */
#define PSA_COMP_PC_AT_915 0 /* PC-AT 915 MHz */
#define PSA_COMP_PC_MC_915 1 /* PC-MC 915 MHz */
#define PSA_COMP_PC_AT_2400 2 /* PC-AT 2.4 GHz */
#define MMW_LOOPT_SEL_LT3C 0x02 /* looptest 3c */
#define MMW_LOOPT_SEL_LT3D 0x01 /* looptest 3d */
unsigned char mmw_jabber_enable; /* jabber timer enable */
- unsigned char mmw_freeze; /* freeze / unfreeeze signal level */
+ unsigned char mmw_freeze; /* freeze / unfreeze signal level */
unsigned char mmw_anten_sel; /* antenna selection */
#define MMW_ANTEN_SEL_SEL 0x01 /* direct antenna selection */
#define MMW_ANTEN_SEL_ALG_EN 0x02 /* antenna selection algorithm enable */
unsigned char mmw_unused2[1]; /* unused */
unsigned char mmw_thr_pre_set; /* level threshold preset */
unsigned char mmw_decay_prm; /* decay parameters */
- unsigned char mmw_decay_updat_prm; /* decay update parameterz */
+ unsigned char mmw_decay_updat_prm; /* decay update parameters */
unsigned char mmw_quality_thr; /* quality (z-quotient) threshold */
unsigned char mmw_netw_id_l; /* NWID low order byte */
unsigned char mmw_netw_id_h; /* NWID high order byte */
ifdef CONFIG_SCSI_U14_34F
SCSI_OBJS := $(SCSI_OBJS) u14-34f.o
SCSI_SRCS := $(SCSI_SRCS) u14-34f.c
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) u14-34f.o
endif
ifdef CONFIG_SCSI_DEBUG
ifdef CONFIG_SCSI_EATA
SCSI_OBJS := $(SCSI_OBJS) eata.o
SCSI_SRCS := $(SCSI_SRCS) eata.c
+else
+SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) eata.o
endif
*
*/
-static void NCR5380_intr (int irq) {
+static void NCR5380_intr (int irq, struct pt_regs * regs) {
NCR5380_local_declare();
struct Scsi_Host *instance;
int done;
instance->hostdata;
unsigned char msgout = NOP;
int sink = 0;
- int len, transfersize;
+ int len;
+#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
+ int transfersize;
+#endif
unsigned char *data;
unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
} else
cmd->SCp.this_residual -= transfersize - len;
} else
-#endif /* defined(REAL_DMA) || defined(REAL_DMA_POLL) */
+#endif /* defined(PSEUDO_DMA) || defined(REAL_DMA_POLL) */
NCR5380_transfer_pio(instance, &phase,
(int *) &cmd->SCp.this_residual, (unsigned char **)
&cmd->SCp.ptr);
#endif
static void NCR5380_init (struct Scsi_Host *instance, int flags);
static void NCR5380_information_transfer (struct Scsi_Host *instance);
-static void NCR5380_intr (int irq);
+static void NCR5380_intr (int irq, struct pt_regs * regs);
static void NCR5380_main (void);
static void NCR5380_print_options (struct Scsi_Host *instance);
#ifndef NCR5380_abort
/*
* eata.c - Low-level driver for EATA/DMA SCSI host adapters.
*
+ * 28 Jan 1995 rev. 1.14 for linux 1.1.86
+ * Added module support.
+ * Log and do a retry when a disk drive returns a target status
+ * different from zero on a recovered error.
+ *
+ * 24 Jan 1995 rev. 1.13 for linux 1.1.85
+ * Use optimized board configuration, with a measured performance
+ * increase in the range 10%-20% on i/o throughput.
+ *
* 16 Jan 1995 rev. 1.12 for linux 1.1.81
* Fix mscp structure comments (no functional change).
* Display a message if check_region detects a port address
* for EISA boards, it just prints a warning message.
*
* 30 Nov 1994 rev. 1.09 for linux 1.1.68
- * Redo i/o on target status CONDITION_GOOD for TYPE_DISK only.
+ * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only.
* Added optional support for using a single board at a time.
*
* 18 Nov 1994 rev. 1.08 for linux 1.1.64
* the driver sets host->block = TRUE for all ISA boards.
*/
+#if defined(MODULE)
+#include <linux/module.h>
+#endif
+
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#define ISA 0
#define ESA 1
+#undef FORCE_CONFIG
+
#undef DEBUG_DETECT
#undef DEBUG_INTERRUPT
#undef DEBUG_STATISTICS
#define IN_USE 1
#define LOCKED 2
#define IN_RESET 3
+#define IGNORE 4
#define NO_IRQ 0xff
-#define MAXLOOP 20000
+#define NO_DMA 0xff
+#define MAXLOOP 200000
#define REG_CMD 7
#define REG_STATUS 7
#define REG_LM 3
#define REG_MID 4
#define REG_MSB 5
-#define REG_REGION 9
+#define REGION_SIZE 9
#define EISA_RANGE 0xf000
#define BSY_ASSERTED 0x80
#define DRQ_ASSERTED 0x08
#define ABSY_ASSERTED 0x01
#define IRQ_ASSERTED 0x02
-#define READ_CONFIG_PIO 0xF0
-#define SET_CONFIG_PIO 0xF1
-#define SEND_CP_PIO 0xF2
-#define RECEIVE_SP_PIO 0xF3
-#define TRUNCATE_XFR_PIO 0xF4
-#define RESET_PIO 0xF9
-#define READ_CONFIG_DMA 0xFD
-#define SET_CONFIG_DMA 0xFE
-#define SEND_CP_DMA 0xFF
+#define READ_CONFIG_PIO 0xf0
+#define SET_CONFIG_PIO 0xf1
+#define SEND_CP_PIO 0xf2
+#define RECEIVE_SP_PIO 0xf3
+#define TRUNCATE_XFR_PIO 0xf4
+#define RESET_PIO 0xf9
+#define READ_CONFIG_DMA 0xfd
+#define SET_CONFIG_DMA 0xfe
+#define SEND_CP_DMA 0xff
#define ASOK 0x00
#define ASST 0x01
/* "EATA", in Big Endian format */
#define EATA_SIGNATURE 0x41544145
+/* Number of valid bytes in the board config structure for EATA 2.0x */
+#define EATA_2_0A_SIZE 28
+#define EATA_2_0B_SIZE 30
+
/* Board info structure */
struct eata_info {
- ulong data_len; /* Number of valid bytes after this field (30) */
+ ulong data_len; /* Number of valid bytes after this field */
ulong sign; /* ASCII "EATA" signature */
unchar :4, /* unused low nibble */
- version:4; /* EATA version */
+ version:4; /* EATA version, should be 0x1 */
unchar ocsena:1, /* Overlap Command Support Enabled */
tarsup:1, /* Target Mode Supported */
:2,
ata:1, /* This is an ATA device */
haaval:1; /* Host Adapter Address Valid */
ushort cp_pad_len; /* Number of pad bytes after cp_len */
- ulong host_addr; /* Host Adapter SCSI ID */
+ unchar host_addr[3]; /* Host Adapter SCSI ID for channels 2, 1, 0 */
+ unchar reserved;
ulong cp_len; /* Number of valid bytes in cp */
ulong sp_len; /* Number of valid bytes in sp */
ushort queue_size; /* Max number of cp that can be queued */
second:1, /* 1 if this is a secondary (not primary) controller */
drqx:2; /* DRQ Index (0=DMA0, 1=DMA7, 2=DMA6, 3=DMA5) */
unchar sync; /* 1 if scsi target id 7...0 is running sync scsi */
- ushort ipad[250];
+
+ /* Structure extension defined in EATA 2.0B */
+ unchar isaena:1, /* ISA i/o addressing is disabled/enabled */
+ forcaddr:1, /* Port address has been forced */
+ :6;
+ unchar max_id:5, /* Max number of SCSI target IDs */
+ max_chan:3; /* Max SCSI channel number on this board */
+
+ ushort ipad[249];
};
/* Board config structure */
unsigned int multicount; /* Total ... in second ihdlr loop */
int board_number; /* Number of this board */
char board_name[16]; /* Name of this board */
+ char board_id[256]; /* data from INQUIRY on this board */
int in_reset; /* True if board is doing a reset */
int target_time_out[MAX_TARGET]; /* N. of timeout errors on target */
int target_reset[MAX_TARGET]; /* If TRUE redo operation on target */
unsigned char subversion; /* Bus type, either ISA or ESA */
+ unsigned char protocol_rev; /* EATA 2.0 rev., 'A' or 'B' or 'C' */
struct mssp sp[MAX_MAILBOXES]; /* Returned status for this board */
};
static inline int port_detect(ushort *port_base, unsigned int j,
Scsi_Host_Template * tpnt) {
unsigned char irq, dma_channel, subversion;
+ unsigned char protocol_rev;
struct eata_info info;
- struct eata_config config;
char *board_status;
/* Allowed DMA channels for ISA (0 indicates reserved) */
sprintf(name, "%s%d", driver_name, j);
- if(check_region(*port_base, REG_REGION)) {
- printk("%s: address 0x%03x already in use, detaching.\n",
+ if(check_region(*port_base, REGION_SIZE)) {
+ printk("%s: address 0x%03x in use, skipping probe.\n",
name, *port_base);
return FALSE;
}
if (read_pio(*port_base, (ushort *)&info, (ushort *)&info.ipad[0]))
return FALSE;
- /* check the controller "EATA" signature */
+ /* Check the controller "EATA" signature */
if (info.sign != EATA_SIGNATURE) return FALSE;
+ if (ntohl(info.data_len) < EATA_2_0A_SIZE) {
+ printk("%s: config structure size (%ld bytes) too short, detaching.\n",
+ name, ntohl(info.data_len));
+ return FALSE;
+ }
+ else if (ntohl(info.data_len) == EATA_2_0A_SIZE)
+ protocol_rev = 'A';
+ else if (ntohl(info.data_len) == EATA_2_0B_SIZE)
+ protocol_rev = 'B';
+ else
+ protocol_rev = 'C';
+
+ if (protocol_rev != 'A' && info.max_chan > 0)
+ printk("%s: warning, only scsi channel 0 is supported.\n", name);
+
irq = info.irq;
if (*port_base & EISA_RANGE) {
if (!info.haaval || info.ata || info.drqvld || !info.dmasup) {
- printk("%s: unusable EISA board found, detaching.\n", name);
+ printk("%s: unusable EISA board found (%d%d%d%d), detaching.\n",
+ name, info.haaval, info.ata, info.drqvld, info.dmasup);
return FALSE;
}
subversion = ESA;
- dma_channel = 0;
+ dma_channel = NO_DMA;
}
else {
if (!info.haaval || info.ata || !info.drqvld || !info.dmasup) {
- printk("%s: unusable ISA board found, detaching.\n", name);
+ printk("%s: unusable ISA board found (%d%d%d%d), detaching.\n",
+ name, info.haaval, info.ata, info.drqvld, info.dmasup);
return FALSE;
}
return FALSE;
}
+#if defined (FORCE_CONFIG)
+ {
+ struct eata_config config;
+
/* Set board configuration */
memset((char *)&config, 0, sizeof(struct eata_config));
config.len = (ushort) htons((ushort)510);
printk("%s: busy timeout sending configuration, detaching.\n", name);
return FALSE;
}
+ }
+#endif
sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
sh[j]->io_port = *port_base;
+ sh[j]->n_io_port = REGION_SIZE;
sh[j]->dma_channel = dma_channel;
sh[j]->irq = irq;
sh[j]->sg_tablesize = (ushort) ntohs(info.scatt_size);
- sh[j]->this_id = (ushort) ntohl(info.host_addr);
+ sh[j]->this_id = (ushort) info.host_addr[3];
sh[j]->can_queue = (ushort) ntohs(info.queue_size);
sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
/* Register the I/O space that we use */
- request_region(sh[j]->io_port, REG_REGION, driver_name);
+ request_region(sh[j]->io_port, REGION_SIZE, driver_name);
memset(HD(j), 0, sizeof(struct hostdata));
HD(j)->subversion = subversion;
+ HD(j)->protocol_rev = protocol_rev;
HD(j)->board_number = j;
irqlist[irq] = j;
if (HD(j)->subversion == ESA)
sh[j]->unchecked_isa_dma = FALSE;
else {
+
+#if !defined(MODULE)
+ /* The module code does not checkin/checkout in the blocking list yet */
sh[j]->block = sh[j];
+#endif
+
sh[j]->unchecked_isa_dma = TRUE;
disable_dma(dma_channel);
clear_dma_ff(dma_channel);
strcpy(BN(j), name);
- printk("%s: %s, ID %d, PORT 0x%03x, IRQ %u, DMA %u, SG %d, "\
- "Mbox %d, CmdLun %d.\n", BN(j), board_status, sh[j]->this_id,
- sh[j]->io_port, sh[j]->irq,
- sh[j]->dma_channel, sh[j]->sg_tablesize,
- sh[j]->can_queue, sh[j]->cmd_per_lun);
+ printk("%s: 2.0%c, %s, ID %d, PORT 0x%03x, IRQ %u, DMA %u, SG %d, "\
+ "Mbox %d, CmdLun %d.\n", BN(j), HD(j)->protocol_rev, board_status,
+ sh[j]->this_id, sh[j]->io_port, sh[j]->irq, sh[j]->dma_channel,
+ sh[j]->sg_tablesize, sh[j]->can_queue, sh[j]->cmd_per_lun);
/* DPT PM2012 does not allow to detect sg_tablesize correctly */
if (sh[j]->sg_tablesize > MAX_SGLIST || sh[j]->sg_tablesize < 2) {
}
#if defined (DEBUG_DETECT)
+ if (protocol_rev != 'A')
+ printk("%s: EATA 2.0%c, isaena %u, forcaddr %u, max_id %u,"\
+ " max_chan %u.\n", name, protocol_rev, info.isaena,
+ info.forcaddr, info.max_id, info.max_chan);
+
printk("%s: Version 0x%x, SYNC 0x%x, infol %ld, cpl %ld spl %ld.\n",
name, info.version, info.sync, ntohl(info.data_len),
ntohl(info.cp_len), ntohl(info.sp_len));
ushort io_port[] = {
0x1c88, 0x2c88, 0x3c88, 0x4c88, 0x5c88, 0x6c88, 0x7c88, 0x8c88,
0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88,
- 0x330, 0x230, 0x1f0, 0x170, 0x0
+ 0x1f0, 0x170, 0x330, 0x230, 0x0
};
ushort *port_base = io_port;
static void eata_interrupt_handler(int irq, struct pt_regs * regs) {
Scsi_Cmnd *SCpnt;
- unsigned int i, j, k, flags, status, loops, total_loops = 0;
+ unsigned int i, j, k, flags, status, tstatus, loops, total_loops = 0;
struct mssp *spp;
struct mscp *cpp;
spp->eoc = FALSE;
- if (HD(j)->cp_stat[i] == LOCKED) {
+ if (HD(j)->cp_stat[i] == IGNORE) {
+ HD(j)->cp_stat[i] = FREE;
+ continue;
+ }
+ else if (HD(j)->cp_stat[i] == LOCKED) {
HD(j)->cp_stat[i] = FREE;
printk("%s: ihdlr, mbox %d unlocked, count %d.\n",
BN(j), i, HD(j)->iocount);
" irq %d.\n", BN(j), i, SCpnt->pid,
*(unsigned int *)SCpnt->host_scribble, irq);
+ tstatus = status_byte(spp->target_status);
+
switch (spp->adapter_status) {
- case ASOK: /* status OK */
+ case ASOK: /* status OK */
- /* Fix a "READ CAPACITY failed" error on some disk drives */
- if (spp->target_status == INTERMEDIATE_GOOD
- && SCpnt->device->type != TYPE_TAPE)
+ /* Forces a reset if a disk drive keeps returning BUSY */
+ if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
status = DID_ERROR << 16;
/* If there was a bus reset, redo operation on each target */
- else if (spp->target_status == CONDITION_GOOD
- && SCpnt->device->type == TYPE_DISK
- && HD(j)->target_reset[SCpnt->target])
+ else if (tstatus != GOOD
+ && SCpnt->device->type == TYPE_DISK
+ && HD(j)->target_reset[SCpnt->target])
status = DID_BUS_BUSY << 16;
+
+ /* Works around a flaw in scsi.c */
+ else if (tstatus == CHECK_CONDITION
+ && SCpnt->device->type == TYPE_DISK
+ && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+ status = DID_BUS_BUSY << 16;
+
+ else if (tstatus == CHECK_CONDITION
+ && (SCpnt->device->type == TYPE_DISK
+ || SCpnt->device->type == TYPE_ROM)
+ && (SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION)
+ status = DID_ERROR << 16;
+
else
status = DID_OK << 16;
- if (spp->target_status == 0)
+ if (tstatus == GOOD)
HD(j)->target_reset[SCpnt->target] = FALSE;
+ if (spp->target_status && (SCpnt->device->type == TYPE_DISK
+ || SCpnt->device->type == TYPE_ROM))
+ printk("%s: ihdlr, target %d:%d, pid %ld, target_status "\
+ "0x%x, sense key 0x%x.\n", BN(j),
+ SCpnt->target, SCpnt->lun, SCpnt->pid,
+ spp->target_status, SCpnt->sense_buffer[2]);
+
HD(j)->target_time_out[SCpnt->target] = 0;
break;
- case ASST: /* Selection Time Out */
- case 0x02: /* Command Time Out */
+ case ASST: /* Selection Time Out */
+ case 0x02: /* Command Time Out */
if (HD(j)->target_time_out[SCpnt->target] > 1)
status = DID_ERROR << 16;
}
break;
- case 0x03: /* SCSI Bus Reset Received */
+ case 0x03: /* SCSI Bus Reset Received */
+ case 0x04: /* Initial Controller Power-up */
if (SCpnt->device->type != TYPE_TAPE)
status = DID_BUS_BUSY << 16;
HD(j)->target_reset[k] = TRUE;
break;
- case 0x07: /* Bus Parity Error */
- case 0x0c: /* Controller Ram Parity */
- case 0x04: /* Initial Controller Power-up */
- case 0x05: /* Unexpected Bus Phase */
- case 0x06: /* Unexpected Bus Free */
- case 0x08: /* SCSI Hung */
- case 0x09: /* Unexpected Message Reject */
- case 0x0a: /* SCSI Bus Reset Stuck */
- case 0x0b: /* Auto Request-Sense Failed */
+ case 0x07: /* Bus Parity Error */
+ case 0x0c: /* Controller Ram Parity */
+ case 0x05: /* Unexpected Bus Phase */
+ case 0x06: /* Unexpected Bus Free */
+ case 0x08: /* SCSI Hung */
+ case 0x09: /* Unexpected Message Reject */
+ case 0x0a: /* SCSI Bus Reset Stuck */
+ case 0x0b: /* Auto Request-Sense Failed */
default:
status = DID_ERROR << 16;
break;
restore_flags(flags);
return;
}
+
+#if defined(MODULE)
+Scsi_Host_Template driver_template = EATA;
+
+#include "scsi_module.c"
+#endif
#include <linux/scsicam.h>
-#define EATA_VERSION "1.12.00"
+#define EATA_VERSION "1.14.03"
int eata_detect(Scsi_Host_Template *);
int eata_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
#define EATA { NULL, /* Ptr for modules */ \
NULL, /* usage count for modules */ \
- "EATA/DMA 2.0A rev. " EATA_VERSION " by " \
+ "EATA/DMA 2.0 rev. " EATA_VERSION " by " \
"Dario_Ballabio@milano.europe.dg.com.", \
eata_detect, \
NULL, /* Release */ \
0, /* sg_tablesize, reset by detect */ \
0, /* cmd_per_lun, reset by detect */ \
0, /* number of boards present */ \
- 0, /* unchecked isa dma, reset by detect */ \
+ 1, /* unchecked isa dma, reset by detect */ \
ENABLE_CLUSTERING \
}
#endif
* -supports all PCI based EATA-DMA boards *
* -supports multiple HBAs with & without IRQ sharing *
* -supports all SCSI channels on multi channel boards *
+ * -displays (more or less useful) infos in /proc/scsi *
+ * -can be loaded as module *
* *
* (c)1993,94,95 Michael Neuffer *
* neuffer@goofy.zdv.uni-mainz.de *
* very helpful and tried to give me all the infos and *
* support I need. *
* *
- * Thanks also to Greg Hosler who did a lot of testing and *
- * found quite a number of bugs during the development. *
+ * Thanks also to Greg Hosler who did a lot of testing and *
+ * found quite a number of bugs during the development. *
************************************************************
- * last change: 95/01/24 *
+ * last change: 95/01/30 *
************************************************************/
/* Look in eata_dma.h for configuration information */
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include "scsi.h"
#include "sd.h"
+#if 0
+#include "eata_dma_proc.c"
+#endif
+
static uint ISAbases[] =
{0x1F0, 0x170, 0x330, 0x230};
static unchar EISAbases[] =
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
static uint registered_HBAs = 0;
static struct Scsi_Host *last_HBA = NULL;
+static struct Scsi_Host *first_HBA = NULL;
static unchar reg_IRQ[] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static unchar reg_IRQL[] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
static struct eata_sp status[MAXIRQ]; /* Statuspacket array */
+static uint internal_command_finished = TRUE;
+
static struct geom_emul geometry; /* Drive 1 & 2 geometry */
-#if DEBUG_EATA
static ulong int_counter = 0;
static ulong queue_counter = 0;
-#endif
+
+void eata_scsi_done (Scsi_Cmnd * SCpnt)
+{
+ return;
+}
+
+int eata_release(struct Scsi_Host *sh)
+{
+ if (sh->irq && reg_IRQ[sh->irq] == 1) free_irq(sh->irq);
+ else reg_IRQ[sh->irq]--;
+ if (SD(sh)->channel == 0) {
+ if (sh->dma_channel != 0xff) free_dma(sh->dma_channel);
+ if (sh->io_port && sh->n_io_port)
+ release_region(sh->io_port, sh->n_io_port);
+ }
+ return(TRUE);
+}
const char *eata_info(struct Scsi_Host *host)
{
save_flags(flags);
cli();
- for (x = 1, sh = last_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) {
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->prev) {
if (sh->irq != irq)
continue;
if (!(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ))
continue;
- DBG(DEBUG_EATA, int_counter++);
+ int_counter++;
sp=&SD(sh)->sp;
break;
}
cmd->result = result | scsi_stat;
- if (scsi_stat == CHECK_CONDITION &&
+ if (in_scan_scsis && scsi_stat == CHECK_CONDITION &&
(cmd->sense_buffer[2] & 0xf) == UNIT_ATTENTION)
cmd->result |= (DRIVER_SENSE << 24);
#if DBG_INTR2
if (scsi_stat || result || hba_stat || eata_stat != 0x50)
- printk("eata_stat: %#x hba_stat: %#.2x,scsi_stat: %#.2x, sense_key: %#x, "
- "result: %#.8x\n", eata_stat, hba_stat,
+ printk("eata_stat: %#x hba_stat: %#.2x,scsi_stat: %#.2x, "
+ "sense_key: %#x, result: %#.8x\n", eata_stat, hba_stat,
scsi_stat,cmd->sense_buffer[2] & 0xf, cmd->result);
DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
#endif
cp->status = FREE; /* now we can release the slot */
restore_flags(flags);
-
- cmd->scsi_done(cmd);
-
+ if(cmd->scsi_done != eata_scsi_done) cmd->scsi_done(cmd);
+ else internal_command_finished = TRUE;
save_flags(flags);
cli();
}
save_flags(flags);
cli();
- DBG(DEBUG_EATA, queue_counter++);
+ queue_counter++;
+ if (done == (void *)eata_scsi_done) {
+ if (internal_command_finished == TRUE)
+ internal_command_finished = FALSE;
+ else
+ cmd->result = (DID_ERROR << 16) + QUEUE_FULL;
+ }
+
hd = HD(cmd);
sh = cmd->host;
cmd->scsi_done = (void *)done;
- if (cmd->cmnd[0] == WRITE_6 || cmd->cmnd[0] == WRITE_10)
+ switch (cmd->cmnd[0]) {
+ case CHANGE_DEFINITION: case COMPARE: case COPY:
+ case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:
+ case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:
+ case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case WRITE_6: case WRITE_10: case WRITE_VERIFY:
+ case 0x3f: case 0x41: case 0xb1:
+ case 0xb0: case 0xb2: case 0xaa:
+ case 0xae: case 0x24: case 0x38:
+ case 0x3d: case 0xb6:
+ case 0xea: /* alternate number for WRITE LONG */
cp->DataOut = TRUE; /* Output mode */
- else
+ break;
+ case 0x00:
+ default:
cp->DataIn = TRUE; /* Input mode */
+ }
+ if (done == (void *) eata_scsi_done)
+ cp->Interpret = TRUE; /* Interpret command */
+
if (cmd->use_sg) {
cp->scatter = TRUE; /* SG mode */
cp->cp_dataDMA = htonl((long)&cp->sg_list);
} else {
cp->scatter = FALSE;
cp->cp_datalen = htonl(cmd->request_bufflen);
- cp->cp_dataDMA = htonl((int)cmd->request_buffer);
+ cp->cp_dataDMA = htonl((ulong)cmd->request_buffer);
}
cp->Auto_Req_Sen = TRUE;
printk("eata_queue target %d, pid %ld, HBA busy, returning DID_ERROR, done.\n",
cmd->target, cmd->pid);
restore_flags(flags);
- done(cmd);
+ if(done != (void *)eata_scsi_done) done(cmd);
return (0);
}
DBG(DBG_QUEUE,printk("Queued base %#.4lx pid: %ld target: %x lun: %x slot %d irq %d\n",
}
/* hard reset the HBA */
- inb((uint) (cmd->host->base) + HA_RSTATUS); /* This might cause trouble */
+ inb((uint) (cmd->host->base) + HA_RSTATUS); /* This might cause trouble */
eata_send_command(0, (uint) cmd->host->base, EATA_CMD_RESET);
DBG(DBG_ABNORM, printk("eata_reset: board reset done, enabling interrupts.\n"));
}
}
-
char * get_board_data(ulong base, uint irq, uint id)
{
struct eata_ccb cp;
if (--loop == 0)
return (FALSE);
- DBG(DBG_PIO && DBG_PROBE, printk("Issuing PIO READ CONFIG to HBA at %lx\n",
+ DBG(DBG_PIO && DBG_PROBE,printk("Issuing PIO READ CONFIG to HBA at %lx\n",
(long)base));
eata_send_command(0, (uint) base, EATA_CMD_PIO_READ_CONFIG);
loop = R_LIMIT;
return (FALSE);
}
+ if ((buff = get_board_data((uint)base, gc->IRQ, gc->scsi_id[3])) == NULL){
+ printk("HBA at %#lx didn't react on INQUIRY. Sorry.\n", (ulong) base);
+ return (FALSE);
+ }
+
+ if(gc->HAA_valid == FALSE || ntohl(gc->len) == 0x1c || ntohl(gc->len) == 0x1e)
+ gc->MAX_CHAN = 0;
+
+ if(strncmp("PM2322", &buff[16], 6) && strncmp("PM3021", &buff[16], 6)
+ && strncmp("PM3222", &buff[16], 6) && strncmp("PM3224", &buff[16], 6))
+ gc->MAX_CHAN = 0;
+
/* if gc->DMA_valid it must be a PM2011 and we have to register it */
- dma_channel = (8 - gc->DMA_channel) & 7;
+ dma_channel = 0xff;
if (gc->DMA_valid) {
- if (request_dma(dma_channel, "DPT_PM2011")) {
+ if (request_dma(dma_channel = (8 - gc->DMA_channel) & 7, "DPT_PM2011")) {
printk("Unable to allocate DMA channel %d for HBA PM2011.\n",
dma_channel);
return (FALSE);
}
- }
-
+ }
+
if (!reg_IRQ[gc->IRQ]) { /* Interrupt already registered ? */
if (!request_irq(gc->IRQ, eata_int_handler, SA_INTERRUPT, "EATA-DMA")){
- reg_IRQ[gc->IRQ]++;
+ reg_IRQ[gc->IRQ] += (gc->MAX_CHAN+1);
if (!gc->IRQ_TR)
reg_IRQL[gc->IRQ] = TRUE; /* IRQ is edge triggered */
" if the IRQ is edge triggered. Sorry.\n");
return (FALSE);
} else
- reg_IRQ[gc->IRQ]++;
+ reg_IRQ[gc->IRQ] += (gc->MAX_CHAN+1);
}
request_region(base, 9, "eata_dma");
- if ((buff = get_board_data((uint)base, gc->IRQ, gc->scsi_id[3])) == NULL){
- printk("HBA at %#lx didn't react on INQUIRY. Sorry.\n", (ulong) base);
- return (FALSE);
- }
-
if(ntohs(gc->queuesiz) == 0) {
gc->queuesiz = ntohs(64);
printk("Warning: Queue size had to be corrected.\n"
"This might be a PM2012 with a defective Firmware\n");
}
- if(gc->HAA_valid == FALSE || ntohl(gc->len) == 0x1c || ntohl(gc->len) == 0x1e)
- gc->MAX_CHAN = 0;
-
- if(strncmp("PM2322", &buff[16], 6) && strncmp("PM3021", &buff[16], 6)
- && strncmp("PM3222", &buff[16], 6) && strncmp("PM3224", &buff[16], 6))
- gc->MAX_CHAN = 0;
size = sizeof(hostdata) + ((sizeof(struct eata_ccb) * ntohs(gc->queuesiz))/
(gc->MAX_CHAN + 1));
memset(hd->ccb, 0, (sizeof(struct eata_ccb) * ntohs(gc->queuesiz)) /
(gc->MAX_CHAN + 1));
-
+ memset(hd->reads, 0, sizeof(ulong) * 26);
+
strncpy(SD(sh)->vendor, &buff[8], 8);
SD(sh)->vendor[8] = 0;
strncpy(SD(sh)->name, &buff[16], 17);
SD(sh)->EATA_revision = '?';
}
sh->base = (char *) base;
- sh->irq = gc->IRQ;
- if (gc->DMA_valid)
- sh->dma_channel = dma_channel;
- else
- sh->dma_channel = 0;
-
+ sh->io_port = (ushort) base;
+ sh->n_io_port = 9;
+ sh->irq = gc->IRQ;
+ sh->dma_channel = dma_channel;
sh->this_id = gc->scsi_id[3 - i];
-
sh->can_queue = ntohs(gc->queuesiz) / (gc->MAX_CHAN + 1);
if (gc->OCS_enabled == TRUE) {
printk("Warning: SG size had to be corrected.\n"
"This might be a PM2012 with a defective Firmware\n");
}
- sh->loaded_as_module = 0; /* Not yet supported */
hd->channel = i;
if(hd->prev != NULL)
SD(hd->prev)->next = sh;
last_HBA = sh;
-
+ if (first_HBA == NULL)
+ first_HBA = sh;
registered_HBAs++;
}
return (1);
for (l = 0; l < MAXISA; l++) {
if (ISAbases[l]) {
- if (get_conf_PIO((struct eata_register *)ISAbases[l], buf) == TRUE){
+ if (get_conf_PIO((struct eata_register *)ISAbases[l],buf) == TRUE){
ret = ISAbases[l];
ISAbases[l] = 0;
return (ret);
}
if (!(error = pcibios_read_config_dword(pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_0, &base))) {
+ PCI_BASE_ADDRESS_0, &base))){
/* Check if the address is valid */
if (base & 0x01) {
if (reg_IRQ[i])
request_irq(i, eata_int_handler, SA_INTERRUPT, "EATA-DMA");
- HBA_ptr = last_HBA;
- for (i = 1; i < registered_HBAs; i++)
- HBA_ptr = SD(HBA_ptr)->prev;
+ HBA_ptr = first_HBA;
printk("Registered HBAs:\n");
printk("HBA no. Boardtype: Revis: EATA: Bus: BaseIO: IRQ: DMA: Ch: ID: Pr: QS: SG: CPL:\n");
for (i = 1; i <= registered_HBAs; i++) {
- printk("scsi%-2d: %.10s v%s 2.0%c ", HBA_ptr->host_no,
- SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
- SD(HBA_ptr)->EATA_revision);
- if(SD(HBA_ptr)->bustype == 'P') printk("PCI ");
- else if(SD(HBA_ptr)->bustype == 'E') printk("EISA");
- else printk("ISA ");
- printk(" %#.4x %2d %2d %d %d %d %2d %2d %2d\n",
- (uint) HBA_ptr->base, HBA_ptr->irq, HBA_ptr->dma_channel, SD(HBA_ptr)->channel,
- HBA_ptr->this_id, SD(HBA_ptr)->primary, HBA_ptr->can_queue,
- HBA_ptr->sg_tablesize, HBA_ptr->cmd_per_lun);
+ printk("scsi%-2d: %.10s v%s 2.0%c %s %#.4x %2d %2x %d %d %d %2d %2d %2d\n",
+ HBA_ptr->host_no, SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
+ SD(HBA_ptr)->EATA_revision, (SD(HBA_ptr)->bustype == 'P')?
+ "PCI ":(SD(HBA_ptr)->bustype == 'E')?"EISA":"ISA ",
+ (uint) HBA_ptr->base, HBA_ptr->irq, HBA_ptr->dma_channel,
+ SD(HBA_ptr)->channel, HBA_ptr->this_id, SD(HBA_ptr)->primary,
+ HBA_ptr->can_queue, HBA_ptr->sg_tablesize, HBA_ptr->cmd_per_lun);
HBA_ptr = SD(HBA_ptr)->next;
}
DBG(DPT_DEBUG,DELAY(1200));
return (registered_HBAs);
}
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = EATA_DMA;
+#include "scsi_module.c"
+#endif
+
* Header file for eata_dma.c Linux EATA-DMA SCSI driver *
* (c) 1993,94,95 Michael Neuffer *
*********************************************************
-* last change: 95/01/16 *
+* last change: 95/01/30 *
********************************************************/
#include <linux/scsicam.h>
#define VER_MAJOR 2
-#define VER_MINOR 1
-#define VER_SUB "0i"
+#define VER_MINOR 3
+#define VER_SUB "0a"
/************************************************************************
* Here you can configure your drives that are using a non-standard *
#define DBG_QUEUE 0 /* Trace command queueing. */
#define DBG_INTR 0 /* Trace interrupt service routine. */
#define DBG_INTR2 0 /* Trace interrupt service routine. */
+#define DBG_PROC 0 /* Debug proc-fs related statistics */
#define DBG_REGISTER 0 /* */
#define DBG_ABNORM 1 /* Debug abnormal actions (reset, abort)*/
NULL, NULL, \
"EATA (Extended Attachment) driver", \
eata_detect, \
- NULL, \
+ eata_release, \
eata_info, \
eata_command, \
eata_queue, \
int eata_queue(Scsi_Cmnd *, void *(done)(Scsi_Cmnd *));
int eata_abort(Scsi_Cmnd *);
int eata_reset(Scsi_Cmnd *);
-
+int eata_release(struct Scsi_Host *);
/*********************************************
* Misc. definitions *
#define MAX_PCI_BUS 16 /* Maximum # Of Busses Allowed */
#define SG_SIZE 64
-#define C_P_L_DIV 8 /* 1 <= C_P_L_DIV <= 8 */
-#define C_P_L_CURRENT_MAX 2 /* Until this limit is removed */
+
+#define C_P_L_CURRENT_MAX 10 /* Until this limit in the mm is removed
+ * Kernels < 1.1.86 died horrible deaths
+ * if you used values >2. The memory management
+ * of pl.86 seems to cope with 10.
+ */
+#define C_P_L_DIV 4 /* 1 <= C_P_L_DIV <= 8
+ * You can use this parameter to fine-tune
+ * the driver. Depending on the number of
+ * devices and their ablilty to queue commands
+ * you will get the best results with a value
+ * ~= numdevices-(devices_unable_to_queue_commands/2)
+ * The reason for this is that the disk driver tents
+ * to flood the queue, so that other drivers have
+ * problems to queue commands themselves. This can
+ * for example result in the effect that the tape
+ * stops during disk accesses.
+ */
#define FREE 0
#define USED 1
unchar data_reg[2]; /* R, couldn't figure this one out */
unchar cp_addr[4]; /* W, CP address register */
union {
- unchar command; /* W, command code: [read|set] conf, send CP*/
+ unchar command; /* W, command code: [read|set] conf, send CP*/
struct reg_bit status; /* R, see register_bit1 */
unchar statusunchar;
} ovr;
};
-struct eata_sp
-{
+struct eata_sp {
unchar hba_stat:7, /* HBA status */
EOC:1; /* True if command finished */
unchar scsi_stat; /* Target SCSI status */
unchar msg[12];
};
-typedef struct hstd{
+typedef struct hstd {
char vendor[9];
char name[18];
char revision[6];
unchar bustype; /* bustype of HBA */
unchar channel; /* no. of scsi channel */
unchar state; /* state of HBA */
+ unchar primary; /* true if primary */
+ ulong reads[13];
+ ulong writes[13];
unchar t_state[MAXTARGET]; /* state of Target (RESET,..) */
uint t_timeout[MAXTARGET]; /* timeouts on target */
- unchar primary; /* true if primary */
uint last_ccb; /* Last used ccb */
struct Scsi_Host *next;
struct Scsi_Host *prev;
struct drive_geom_emul drv[2]; /* drive structures */
};
-struct lun_map {
- unchar id:5,
- chan:3;
- unchar lun;
-};
-
-typedef struct emul_pp {
- unchar p_code:6,
- null:1,
- p_save:1;
- unchar p_length;
- ushort cylinder;
- unchar heads;
- unchar sectors;
- unchar null2;
- unchar s_lunmap:4,
- ems:1;
- ushort drive_type; /* In Little Endian ! */
- struct lun_map lunmap[4];
-}emulpp;
-
-
-
#endif /* _EATA_H */
retval->host_wait = NULL;
retval->last_reset = 0;
retval->irq = 0;
+ retval->dma_channel = 0xff;
+ retval->io_port = 0;
retval->forbidden_addr = 0;
retval->forbidden_size = 0;
retval->hostt = tpnt;
unsigned int scsi_init(void);
extern struct Scsi_Host * scsi_register(Scsi_Host_Template *, int j);
extern void scsi_unregister(struct Scsi_Host * i);
+extern int scsicam_bios_param (Disk *, int, int *);
#define BLANK_HOST {"", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
Use at your own risk. Support Tort Reform so you won't have to read all
these silly disclaimers.
- Copyright 1994, Tom Zerucha.
+ Copyright 1994, Tom Zerucha.
zerucha@shell.portal.com
Additional Code, and much appreciated help by
Michael A. Griffith
grif@cs.ucr.edu
+ Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA
+ help respectively, and for suffering through my foolishness during the
+ debugging process.
+
Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994
(you can reference it, but it is incomplete and inaccurate in places)
- Version 0.38b
+ Version 0.39a
- This also works with loadable SCSI as a module. Check configuration
- options QL_INT_ACTIVE_HIGH and QL_TURBO_PDMA for PCMCIA usage (which
- also requires an enabler).
+ Functions as standalone, loadable, and PCMCIA driver, the latter from
+ Dave Hind's PCMCIA package.
Redistributable under terms of the GNU Public License
*/
/*----------------------------------------------------------------*/
/* Configuration */
-/* Set this if you are using the PCMCIA adapter - it will automatically
- take care of several settings */
-#define QL_PCMCIA 0
-/* Set the following to 2 to use normal interrupt (active high/totempole-
+/* The following option is normally left alone. PCMCIA support needs to
+ change this to adapt to the different way the interrupt pin works.
+
+ Set the following to 2 to use normal interrupt (active high/totempole-
tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
drain */
#define QL_INT_ACTIVE_HIGH 2
#define QL_USE_IRQ 1
/* Set the following to max out the speed of the PIO PseudoDMA transfers,
- again, 0 tends to be slower, but more stable. THIS SHOULD BE ZERO FOR
- PCMCIA */
+ again, 0 tends to be slower, but more stable. */
#define QL_TURBO_PDMA 1
/* This will reset all devices when the driver is initialized (during bootup).
The other linux drivers don't do this, but the DOS drivers do, and after
- using DOS or some kind of crash or lockup this will bring things back */
+ using DOS or some kind of crash or lockup this will bring things back
+ without requiring a cold boot. It does take some time to recover from a
+ reset, so it is slower, and I have seen timeouts so that devices weren't
+ recognized when this was set. */
#define QL_RESET_AT_START 1
-/* This will set fast (10Mhz) synchronous timing, FASTCLK must also be 1*/
+/* This will set fast (10Mhz) synchronous timing, FASTCLK must also be 1 */
#define FASTSCSI 0
/* This will set a faster sync transfer rate */
cause the deassertion to be early by 1/2 clock. Bits 5&4 control
the assertion delay, also in 1/2 clocks (FASTCLK is ignored here). */
-/* Option Synchronization */
-
-#if QL_PCMCIA
+/* PCMCIA option adjustment */
+#ifdef PCMCIA
#undef QL_INT_ACTIVE_HIGH
-#undef QL_TURBO_PDMA
#define QL_INT_ACTIVE_HIGH 0
-#define QL_TURBO_PDMA 0
#endif
/*----------------------------------------------------------------*/
static Scsi_Cmnd *qlcmd; /* current command being processed */
/*----------------------------------------------------------------*/
-
+/* The qlogic card uses two register maps - These macros select which one */
#define REG0 ( outb( inb( qbase + 0xd ) & 0x7f , qbase + 0xd ), outb( 4 , qbase + 0xd ))
#define REG1 ( outb( inb( qbase + 0xd ) | 0x80 , qbase + 0xd ), outb( 0xb4 | QL_INT_ACTIVE_HIGH , qbase + 0xd ))
-/* following is watchdog timeout */
+/* following is watchdog timeout in microseconds */
#define WATCHDOG 5000000
/*----------------------------------------------------------------*/
if (!(inb(qbase + 4) & 0x80)) /* false alarm? */
return;
if (qlcmd == NULL) { /* no command to process? */
- while (inb(qbase + 5)); /* maybe also ql_zap() */
+ int i;
+ i = 16;
+ while (i-- && inb(qbase + 5)); /* maybe also ql_zap() */
return;
}
icmd = qlcmd;
/* Qlogic Cards only exist at 0x230 or 0x330 (the chip itself decodes the
address - I check 230 first since MIDI cards are typically at 330
- Note that this will not work for 2 Qlogic cards in 1 system. The
- easiest way to do that is to create 2 versions of this file, one for
- 230 and one for 330.
-
- Alternately, the Scsi_Host structure now stores the i/o port and can
- be used to set the port (go through and replace qbase with
- (struct Scsi_Cmnd *) cmd->host->io_port, or for efficiency, set a local
- copy of qbase. There will also need to be something similar within the
- IRQ handlers to sort out which board it came from and thus which port.
+
+ Theoretically, two Qlogic cards can coexist in the same system. This
+ should work by simply using this as a loadable module for the second
+ card, but I haven't tested this.
*/
for (qbase = 0x230; qbase < 0x430; qbase += 0x100) {
-#ifndef PCMCIA
if( check_region( qbase , 0x10 ) )
continue;
-#endif
REG1;
if ( ( (inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7 )
&& ( (inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7 ) )
while (inb(qbase + 5)); /* purge int */
while (i) /* find on bit */
i >>= 1, qlirq++; /* should check for exactly 1 on */
- if (qlirq >= 0 && !request_irq(qlirq, ql_ihandl, SA_INTERRUPT, "qlogic"))
+ if (qlirq >= 0 && !request_irq(qlirq, ql_ihandl, 0, "qlogic"))
host->can_queue = 1;
restore_flags( flags );
#endif
-#ifndef PCMCIA
request_region( qbase , 0x10 ,"qlogic");
-#endif
hreg = scsi_register( host , 0 ); /* no host data */
hreg->io_port = qbase;
hreg->n_io_port = 16;
+ hreg->dma_channel = -1;
if( qlirq != -1 )
hreg->irq = qlirq;
- sprintf(qinfo, "Qlogic Driver version 0.38b, chip %02X at %03X, IRQ %d, Opts:%d%d",
+ sprintf(qinfo, "Qlogic Driver version 0.39a, chip %02X at %03X, IRQ %d, Opts:%d%d",
qltyp, qbase, qlirq, QL_INT_ACTIVE_HIGH, QL_TURBO_PDMA );
host->name = qinfo;
{"CHINON","CD-ROM CDS-431","H42"}, /* Locks up if polled for lun != 0 */
{"CHINON","CD-ROM CDS-535","Q14"}, /* Lockup if polled for lun != 0 */
{"DENON","DRD-25X","V"}, /* A cdrom that locks up when probed at lun != 0 */
+ {"HITACHI","DK314C","CR21" }, /* responds to all lun */
{"IMS", "CDD521/10","2.06"}, /* Locks-up when LUN>0 polled. */
{"MAXTOR","XT-3280","PR02"}, /* Locks-up when LUN>0 polled. */
{"MAXTOR","XT-4380S","B3C"}, /* Locks-up when LUN>0 polled. */
up(SCpnt->request.sem);
}
-#ifdef NO_MULTI_LUN
-static int max_scsi_luns = 1;
-#else
+#ifdef CONFIG_SCSI_MULTI_LUN
static int max_scsi_luns = 8;
+#else
+static int max_scsi_luns = 1;
#endif
void scsi_luns_setup(char *str, int *ints) {
#define INTERMEDIATE_GOOD 0x08
#define INTERMEDIATE_C_GOOD 0x0a
#define RESERVATION_CONFLICT 0x0c
+#define QUEUE_FULL 0x1a
#define STATUS_MASK 0x1e
if(target >= sd_template.dev_max || !rscsi_disks[target].device)
return -ENXIO; /* No such device */
- /*
- * See if we are requesting a non-existent partition.
- */
- if(sd_sizes[MINOR(inode->i_rdev)] == 0)
- return -ENXIO;
-
/* Make sure that only one process can do a check_change_disk at one time.
This is also used to lock out further access when the partition table is being re-read. */
if(!rscsi_disks[target].device->access_count)
sd_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
};
+ /*
+ * See if we are requesting a non-existent partition. Do this
+ * after checking for disk change.
+ */
+ if(sd_sizes[MINOR(inode->i_rdev)] == 0)
+ return -ENXIO;
+
rscsi_disks[target].device->access_count++;
if (rscsi_disks[target].device->host->hostt->usage_count)
(*rscsi_disks[target].device->host->hostt->usage_count)++;
printk("do cmd\n");
#endif
scsi_do_cmd (SCpnt,(void *) cmnd,
- (void *) device->buff,device->header.pack_len-size-sizeof(struct sg_header),
+ (void *) device->buff,amt,
sg_command_done,device->timeout,SG_DEFAULT_RETRIES);
#ifdef DEBUG
printk("done cmd\n");
#define IOCTL_RETRIES 3
/* The CDROM is fairly slow, so we need a little extra time */
-#define IOCTL_TIMEOUT 200
+#define IOCTL_TIMEOUT 2000
extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
Copyright 1992, 1993, 1994, 1995 Kai Makisara
email Kai.Makisara@metla.fi
- Last modified: Thu Jan 19 23:28:05 1995 by makisara@kai.home
+ Last modified: Mon Jan 30 23:20:07 1995 by root@kai.home
*/
#include <linux/fs.h>
else
(STp->buffer)->writing = ((STp->buffer)->buffer_bytes /
STp->block_size) * STp->block_size;
- STp->dirty = 0;
+ STp->dirty = !((STp->buffer)->writing ==
+ (STp->buffer)->buffer_bytes);
if (STp->block_size == 0)
blks = (STp->buffer)->writing;
STp->drv_block = 0;
}
else if (cmd_in == MTFSR) {
- if (blkno >= undone)
- STp->drv_block = blkno - undone;
- else
- STp->drv_block = (-1);
+ if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+ (STp->mt_status)->mt_fileno++;
+ STp->drv_block = 0;
+ }
+ else {
+ if (blkno >= undone)
+ STp->drv_block = blkno - undone;
+ else
+ STp->drv_block = (-1);
+ }
}
else if (cmd_in == MTBSR) {
- if (blkno >= 0)
- STp->drv_block = blkno + undone;
- else
+ if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+ (STp->mt_status)->mt_fileno--;
STp->drv_block = (-1);
+ }
+ else {
+ if (blkno >= 0)
+ STp->drv_block = blkno + undone;
+ else
+ STp->drv_block = (-1);
+ }
}
- else if (cmd_in == MTEOM) {
+ else if (cmd_in == MTEOM || cmd_in == MTSEEK) {
(STp->mt_status)->mt_fileno = (-1);
STp->drv_block = (-1);
}
/*
* u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
*
+ * 28 Jan 1995 rev. 1.14 for linux 1.1.86
+ * Added module support.
+ * Log and do a retry when a disk drive returns a target status
+ * different from zero on a recovered error.
+ * Auto detects if U14F boards have an old firmware revision.
+ * Max number of scatter/gather lists set to 16 for all boards
+ * (most installation run fine using 33 sglists, while other
+ * has problems when using more then 16).
+ *
* 16 Jan 1995 rev. 1.13 for linux 1.1.81
* Display a message if check_region detects a port address
* already in use.
* The host->block flag is set for all the detected ISA boards.
*
* 30 Nov 1994 rev. 1.11 for linux 1.1.68
- * Redo i/o on target status CONDITION_GOOD for TYPE_DISK only.
+ * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only.
* Added optional support for using a single board at a time.
*
* 14 Nov 1994 rev. 1.10 for linux 1.1.63
*
* Here a sample configuration using two U14F boards:
*
- U14F0: PORT 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 33, Mbox 16, CmdLun 2, C1.
- U14F1: PORT 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 33, Mbox 16, CmdLun 2, C1.
+ U14F0: PORT 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 16, Mbox 16, CmdLun 2, C1.
+ U14F1: PORT 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 16, Mbox 16, CmdLun 2, C1.
*
* The boot controller must have its BIOS enabled, while other boards can
* have their BIOS disabled, or enabled to an higher address.
* when DISABLE_CLUSTERING is in effect, but unscattered requests could be
* larger than 16Kbyte.
*
- * The new firmware has fixed all the above problems and has been tested
- * with up to 33 scatter/gather lists.
+ * The new firmware has fixed all the above problems.
*
* In order to support multiple ISA boards in a reliable way,
* the driver sets host->block = TRUE for all ISA boards.
*/
+#if defined(MODULE)
+#include <linux/module.h>
+#endif
+
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#define HA_CMD_READ_BUFF 0x3
#define HA_CMD_WRITE_BUFF 0x4
-#if defined (HAVE_OLD_U14F_FIRMWARE)
-#define U14F_MAX_SGLIST 16
-#define U14F_CLUSTERING DISABLE_CLUSTERING
-#else
-#define U14F_MAX_SGLIST 33
-#define U14F_CLUSTERING ENABLE_CLUSTERING
-#endif
-
-#define U34F_MAX_SGLIST 33
-#define U34F_CLUSTERING ENABLE_CLUSTERING
-
#undef DEBUG_DETECT
#undef DEBUG_INTERRUPT
#undef DEBUG_STATISTICS
#define MAX_IRQ 16
#define MAX_BOARDS 4
#define MAX_MAILBOXES 16
-#define MAX_SGLIST 33
+#define MAX_SGLIST 16
#define MAX_CMD_PER_LUN 2
#define FALSE 0
#define IN_USE 1
#define LOCKED 2
#define IN_RESET 3
+#define IGNORE 4
#define NO_IRQ 0xff
-#define MAXLOOP 20000
+#define NO_DMA 0xff
+#define MAXLOOP 200000
#define REG_LCL_MASK 0
#define REG_LCL_INTR 1
#define REG_CONFIG2 7
#define REG_OGM 8
#define REG_ICM 12
-#define REG_REGION 0x0c
+#define REGION_SIZE 13
#define BSY_ASSERTED 0x01
-#define INTR_ASSERTED 0x01
+#define IRQ_ASSERTED 0x01
#define CMD_RESET 0xc0
#define CMD_OGM_INTR 0x01
#define CMD_CLR_INTR 0x01
unsigned int multicount; /* Total ... in second ihdlr loop */
int board_number; /* Number of this board */
char board_name[16]; /* Name of this board */
+ char board_id[256]; /* data from INQUIRY on this board */
int in_reset; /* True if board is doing a reset */
int target_time_out[MAX_TARGET]; /* N. of timeout errors on target */
int target_reset[MAX_TARGET]; /* If TRUE redo operation on target */
- unsigned char bios_drive_number: 1;
+ unsigned char subversion; /* Bus type, either ISA or ESA */
unsigned char heads;
unsigned char sectors;
- unsigned char subversion: 4; /* Bus type, either ISA or ESA */
/* slot != 0 for the U24F, slot == 0 for both the U14F and U34F */
unsigned char slot;
return FALSE;
}
+static int board_inquiry(unsigned int j) {
+ struct mscp *cpp;
+ unsigned int time, limit = 0;
+
+ cpp = &HD(j)->cp[0];
+ memset(cpp, 0, sizeof(struct mscp));
+ cpp->opcode = OP_HOST_ADAPTER;
+ cpp->xdir = DTD_IN;
+ cpp->data_address = (unsigned int) HD(j)->board_id;
+ cpp->data_len = sizeof(HD(j)->board_id);
+ cpp->scsi_cdbs_len = 6;
+ cpp->scsi_cdbs[0] = HA_CMD_INQUIRY;
+
+ if (wait_on_busy(sh[j]->io_port)) {
+ printk("%s: board_inquiry, adapter busy.\n", BN(j));
+ return TRUE;
+ }
+
+ HD(j)->cp_stat[0] = IGNORE;
+
+ /* Clear the interrupt indication */
+ outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
+
+ /* Store pointer in OGM address bytes */
+ outl((unsigned int)cpp, sh[j]->io_port + REG_OGM);
+
+ /* Issue OGM interrupt */
+ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
+
+ sti();
+ time = jiffies;
+ while (jiffies < (time + 100) && limit++ < 100000000) sti();
+ cli();
+
+ if (cpp->adapter_status || HD(j)->cp_stat[0] != FREE) {
+ HD(j)->cp_stat[0] = FREE;
+ printk("%s: board_inquiry, err 0x%x.\n", BN(j), cpp->adapter_status);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static inline int port_detect(ushort *port_base, unsigned int j,
Scsi_Host_Template * tpnt) {
unsigned char irq, dma_channel, subversion;
sprintf(name, "%s%d", driver_name, j);
- if(check_region(*port_base, REG_REGION)) {
- printk("%s: address 0x%03x already in use, detaching.\n",
+ if(check_region(*port_base, REGION_SIZE)) {
+ printk("%s: address 0x%03x in use, skipping probe.\n",
name, *port_base);
return FALSE;
}
sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
sh[j]->io_port = *port_base;
+ sh[j]->n_io_port = REGION_SIZE;
sh[j]->base = bios_segment_table[config_1.bios_segment];
sh[j]->irq = irq;
+ sh[j]->sg_tablesize = MAX_SGLIST;
sh[j]->this_id = config_2.ha_scsi_id;
sh[j]->can_queue = MAX_MAILBOXES;
sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
if (sh[j]->base == 0) outb(CMD_ENA_INTR, sh[j]->io_port + REG_SYS_MASK);
/* Register the I/O space that we use */
- request_region(sh[j]->io_port, REG_REGION, driver_name);
+ request_region(sh[j]->io_port, REGION_SIZE, driver_name);
memset(HD(j), 0, sizeof(struct hostdata));
HD(j)->heads = mapping_table[config_2.mapping_mode].heads;
HD(j)->sectors = mapping_table[config_2.mapping_mode].sectors;
- HD(j)->bios_drive_number = config_2.bios_drive_number;
HD(j)->subversion = subversion;
HD(j)->board_number = j;
irqlist[irq] = j;
if (HD(j)->subversion == ESA) {
- sh[j]->dma_channel = 0;
+ sh[j]->dma_channel = NO_DMA;
sh[j]->unchecked_isa_dma = FALSE;
- sh[j]->sg_tablesize = U34F_MAX_SGLIST;
- sh[j]->hostt->use_clustering = U34F_CLUSTERING;
+ sh[j]->hostt->use_clustering = ENABLE_CLUSTERING;
sprintf(BN(j), "U34F%d", j);
}
else {
- sh[j]->dma_channel = dma_channel;
+
+#if !defined(MODULE)
+ /* The module code does not checkin/checkout in the blocking list yet */
sh[j]->block = sh[j];
+#endif
+
+#if defined (HAVE_OLD_U14F_FIRMWARE)
+ sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
+#else
+ sh[j]->hostt->use_clustering = ENABLE_CLUSTERING;
+#endif
+
+ sh[j]->dma_channel = dma_channel;
sh[j]->unchecked_isa_dma = TRUE;
- sh[j]->sg_tablesize = U14F_MAX_SGLIST;
- sh[j]->hostt->use_clustering = U14F_CLUSTERING;
sprintf(BN(j), "U14F%d", j);
disable_dma(dma_channel);
clear_dma_ff(dma_channel);
enable_dma(dma_channel);
}
+ if (HD(j)->subversion == ISA && !board_inquiry(j)) {
+ HD(j)->board_id[40] = 0;
+
+ if (strcmp(&HD(j)->board_id[32], "06000600")) {
+ printk("%s: %s.\n", BN(j), &HD(j)->board_id[8]);
+ printk("%s: firmware %s is outdated, BIOS rev. should be 2.01.\n",
+ BN(j), &HD(j)->board_id[32]);
+ sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
+ }
+ }
+
printk("%s: PORT 0x%03x, BIOS 0x%05x, IRQ %u, DMA %u, SG %d, "\
"Mbox %d, CmdLun %d, C%d.\n", BN(j), sh[j]->io_port,
(int)sh[j]->base, sh[j]->irq,
static void u14_34f_interrupt_handler(int irq, struct pt_regs * regs) {
Scsi_Cmnd *SCpnt;
- unsigned int i, j, k, flags, status, loops, total_loops = 0;
+ unsigned int i, j, k, flags, status, tstatus, loops, total_loops = 0;
struct mscp *spp;
save_flags(flags);
loops = 0;
/* Loop until all interrupts for a board are serviced */
- while (inb(sh[j]->io_port + REG_SYS_INTR) & INTR_ASSERTED) {
+ while (inb(sh[j]->io_port + REG_SYS_INTR) & IRQ_ASSERTED) {
total_loops++;
loops++;
if (i >= sh[j]->can_queue)
panic("%s: ihdlr, invalid mscp address.\n", BN(j));
- if (HD(j)->cp_stat[i] == LOCKED) {
+ if (HD(j)->cp_stat[i] == IGNORE) {
+ HD(j)->cp_stat[i] = FREE;
+ continue;
+ }
+ else if (HD(j)->cp_stat[i] == LOCKED) {
HD(j)->cp_stat[i] = FREE;
printk("%s: ihdlr, mbox %d unlocked, count %d.\n",
BN(j), i, HD(j)->iocount);
" irq %d.\n", BN(j), i, SCpnt->pid,
*(unsigned int *)SCpnt->host_scribble, irq);
+ tstatus = status_byte(spp->target_status);
+
switch (spp->adapter_status) {
case ASOK: /* status OK */
- /* Fix a "READ CAPACITY failed" error on some disk drives */
- if (spp->target_status == INTERMEDIATE_GOOD
- && SCpnt->device->type != TYPE_TAPE)
+ /* Forces a reset if a disk drive keeps returning BUSY */
+ if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
status = DID_ERROR << 16;
/* If there was a bus reset, redo operation on each target */
- else if (spp->target_status == CONDITION_GOOD
- && SCpnt->device->type == TYPE_DISK
- && HD(j)->target_reset[SCpnt->target])
+ else if (tstatus != GOOD
+ && SCpnt->device->type == TYPE_DISK
+ && HD(j)->target_reset[SCpnt->target])
+ status = DID_BUS_BUSY << 16;
+
+ /* Works around a flaw in scsi.c */
+ else if (tstatus == CHECK_CONDITION
+ && SCpnt->device->type == TYPE_DISK
+ && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
status = DID_BUS_BUSY << 16;
+
+ else if (tstatus == CHECK_CONDITION
+ && (SCpnt->device->type == TYPE_DISK
+ || SCpnt->device->type == TYPE_ROM)
+ && (SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION)
+ status = DID_ERROR << 16;
+
else
status = DID_OK << 16;
- if (spp->target_status == 0)
+ if (tstatus == GOOD)
HD(j)->target_reset[SCpnt->target] = FALSE;
+ if (spp->target_status && (SCpnt->device->type == TYPE_DISK
+ || SCpnt->device->type == TYPE_ROM))
+ printk("%s: ihdlr, target %d:%d, pid %ld, target_status "\
+ "0x%x, sense key 0x%x.\n", BN(j),
+ SCpnt->target, SCpnt->lun, SCpnt->pid,
+ spp->target_status, SCpnt->sense_buffer[2]);
+
HD(j)->target_time_out[SCpnt->target] = 0;
break;
- case ASST: /* SCSI bus selection time out */
+ case ASST: /* Selection Time Out */
if (HD(j)->target_time_out[SCpnt->target] > 1)
status = DID_ERROR << 16;
restore_flags(flags);
return;
}
+
+#if defined(MODULE)
+Scsi_Host_Template driver_template = ULTRASTOR_14_34F;
+
+#include "scsi_module.c"
+#endif
int u14_34f_reset(Scsi_Cmnd *);
int u14_34f_biosparam(Disk *, int, int *);
-#define U14_34F_VERSION "1.13.00"
+#define U14_34F_VERSION "1.14.05"
#define ULTRASTOR_14_34F { \
NULL, \
0, /* sg_tablesize, reset by detect */ \
0, /* cmd_per_lun, reset by detect */ \
0, /* number of boards present */ \
- 0, /* unchecked isa dma, reset by detect */ \
+ 1, /* unchecked isa dma, reset by detect */ \
0, /* use_clustering, reset by detect */ \
}
#endif
.s.o:
$(AS) -o $*.o $<
-OBJS= hpfs_fs.o
+OBJS= hpfs_fs.o hpfs_caps.o
hpfs.o: $(OBJS)
- ln -f hpfs_fs.o hpfs.o
+ $(LD) -r -o hpfs.o $(OBJS)
dep:
$(CPP) -M *.c > .depend
--- /dev/null
+/* Capitalization rules for HPFS */
+
+/* In OS/2, HPFS filenames preserve upper and lower case letter distinctions
+ but filename matching ignores case. That is, creating a file "Foo"
+ actually creates a file named "Foo" which can be looked up as "Foo",
+ "foo", or "FOO", among other possibilities.
+
+ Also, HPFS is internationalized -- a table giving the uppercase
+ equivalent of every character is stored in the filesystem, so that
+ any national character set may be used. If several different
+ national character sets are in use, several tables are stored
+ in the filesystem.
+
+ It would be perfectly reasonable for Linux HPFS to act as a Unix
+ filesystem and match "Foo" only if asked for "Foo" exactly. But
+ the sort order of HPFS directories is case-insensitive, so Linux
+ still has to know the capitalization rules used by OS/2. Because
+ of this, it turns out to be more natural for us to be case-insensitive
+ than not.
+
+ Currently the standard character set used by Linux is Latin-1.
+ Work is underway to permit people to use UTF-8 instead, therefore
+ all code that depends on the character set is segregated here.
+
+ (It would be wonderful if Linux HPFS could be independent of what
+ character set is in use on the Linux side, but because of the
+ necessary case folding this is impossible.)
+
+ There is a map from Latin-1 into code page 850 for every printing
+ character in Latin-1. Most, maybe all, OS/2 installations have code
+ page 850 available, and surely all (on PC hardware) have 437 available.
+
+ It is not clear exactly how HPFS.IFS handles the situation when
+ multiple code pages are in use. Experiments show that
+
+ - tables on the disk give uppercasing rules for the installed code pages
+
+ - each directory entry is tagged with what code page was current
+ when that name was created
+
+ - doing just CHCP, without changing what's on the disk in any way,
+ can change what DIR reports, and what name a case-folded match
+ will match.
+
+ This means, I think, that HPFS.IFS operates in the current code
+ page, without regard to the uppercasing information recorded in
+ the tables on the disk. It does record the uppercasing rules
+ it used, perhaps for alien operating systems such as us, but it
+ does not appear to use them itself.
+
+ So: Linux, a Latin-1 system, will operate in code page 850. We
+ recode between 850 and Latin-1 when dealing with the names actually
+ on the disk. We don't use the uppercasing tables either.
+
+ In a hypothetical UTF-8 implementation, one reasonable way to
+ proceed that matches OS/2 (for least surprise) is: do case
+ translation in UTF-8, and recode to/from one of the code pages
+ available on the mounted filesystem. Reject as invalid any name
+ containing chars that can't be represented on disk by one of the
+ code pages OS/2 is using. Recoding from on-disk names to UTF-8
+ could use the code page tags, though this is not what OS/2 does. */
+
+static const unsigned char tb_cp850_to_latin1[128] =
+{
+ 199, 252, 233, 226, 228, 224, 229, 231,
+ 234, 235, 232, 239, 238, 236, 196, 197,
+ 201, 230, 198, 244, 246, 242, 251, 249,
+ 255, 214, 220, 248, 163, 216, 215, 159,
+ 225, 237, 243, 250, 241, 209, 170, 186,
+ 191, 174, 172, 189, 188, 161, 171, 187,
+ 155, 156, 157, 144, 151, 193, 194, 192,
+ 169, 135, 128, 131, 133, 162, 165, 147,
+ 148, 153, 152, 150, 145, 154, 227, 195,
+ 132, 130, 137, 136, 134, 129, 138, 164,
+ 240, 208, 202, 203, 200, 158, 205, 206,
+ 207, 149, 146, 141, 140, 166, 204, 139,
+ 211, 223, 212, 210, 245, 213, 181, 254,
+ 222, 218, 219, 217, 253, 221, 175, 180,
+ 173, 177, 143, 190, 182, 167, 247, 184,
+ 176, 168, 183, 185, 179, 178, 142, 160,
+};
+
+#if 0
+static const unsigned char tb_latin1_to_cp850[128] =
+{
+ 186, 205, 201, 187, 200, 188, 204, 185,
+ 203, 202, 206, 223, 220, 219, 254, 242,
+ 179, 196, 218, 191, 192, 217, 195, 180,
+ 194, 193, 197, 176, 177, 178, 213, 159,
+ 255, 173, 189, 156, 207, 190, 221, 245,
+ 249, 184, 166, 174, 170, 240, 169, 238,
+ 248, 241, 253, 252, 239, 230, 244, 250,
+ 247, 251, 167, 175, 172, 171, 243, 168,
+ 183, 181, 182, 199, 142, 143, 146, 128,
+ 212, 144, 210, 211, 222, 214, 215, 216,
+ 209, 165, 227, 224, 226, 229, 153, 158,
+ 157, 235, 233, 234, 154, 237, 232, 225,
+ 133, 160, 131, 198, 132, 134, 145, 135,
+ 138, 130, 136, 137, 141, 161, 140, 139,
+ 208, 164, 149, 162, 147, 228, 148, 246,
+ 155, 151, 163, 150, 129, 236, 231, 152,
+};
+#endif
+
+static inline unsigned latin1_upcase (unsigned c)
+{
+ if (c - (unsigned char) 'a' <= (unsigned char) 'z' - (unsigned char) 'a'
+ || (c - (unsigned char) '`' <= (unsigned char) '~' - (unsigned char) '`'
+ && c != (unsigned char) 'w'))
+ return c - (unsigned char) 'a' + (unsigned char) 'A';
+ else
+ return c;
+}
+
+static inline unsigned latin1_downcase (unsigned c)
+{
+ if (c - (unsigned char) 'A' <= (unsigned char) 'Z' - (unsigned char) 'A'
+ || (c - (unsigned char) '@' <= (unsigned char) '^' - (unsigned char) '@'
+ && c != (unsigned char) 'W'))
+ return c + (unsigned char) 'a' - (unsigned char) 'A';
+ else
+ return c;
+}
+
+#if 0
+static inline unsigned latin1_to_cp850 (unsigned c)
+{
+ if ((signed) c - 128 >= 0)
+ return tb_latin1_to_cp850[c - 128];
+ else
+ return c;
+}
+#endif
+
+static inline unsigned cp850_to_latin1 (unsigned c)
+{
+ if ((signed) c - 128 >= 0)
+ return tb_cp850_to_latin1[c - 128];
+ else
+ return c;
+}
+
+unsigned hpfs_char_to_upper_linux (unsigned c)
+{
+ return latin1_upcase (cp850_to_latin1 (c));
+}
+
+unsigned linux_char_to_upper_linux (unsigned c)
+{
+ return latin1_upcase (c);
+}
+
+unsigned hpfs_char_to_lower_linux (unsigned c)
+{
+ return latin1_downcase (cp850_to_latin1 (c));
+}
+
+unsigned hpfs_char_to_linux (unsigned c)
+{
+ return cp850_to_latin1 (c);
+}
--- /dev/null
+unsigned hpfs_char_to_linux (unsigned c);
+unsigned hpfs_char_to_lower_linux (unsigned c);
+unsigned hpfs_char_to_upper_linux (unsigned c);
+unsigned linux_char_to_upper_linux (unsigned c);
#include <asm/segment.h>
#include "hpfs.h"
+#include "hpfs_caps.h"
/*
* HPFS is a mixture of 512-byte blocks and 2048-byte blocks. The 2k blocks
if (n != 0)
do {
- unsigned c1 = *s1++;
- unsigned c2 = *s2++;
- if (c1 - 'a' < 26)
- c1 -= 040;
- if (c2 - 'a' < 26)
- c2 -= 040;
+ unsigned c1 = linux_char_to_upper_linux (*s1++);
+ unsigned c2 = hpfs_char_to_upper_linux (*s2++);
if ((t = c1 - c2) != 0)
return t;
} while (--n != 0);
put_fs_long(ino, &dirent->d_ino);
put_fs_word(namelen, &dirent->d_reclen);
- if (lowercase)
- for (n = namelen; n != 0;) {
- unsigned t = name[--n];
- if (t - 'A' < 26)
- t += 040;
- put_fs_byte(t, &dirent->d_name[n]);
- }
- else
- memcpy_tofs(dirent->d_name, name, namelen);
+ for (n = namelen; n != 0;) {
+ unsigned t = name[--n];
+ if (lowercase)
+ t = hpfs_char_to_lower_linux (t);
+ else
+ t = hpfs_char_to_linux (t);
+ put_fs_byte(t, &dirent->d_name[n]);
+ }
put_fs_byte(0, &dirent->d_name[namelen]);
}
return;
}
- /* files within /proc/net */
+#ifdef CONFIG_IP_ACCT
+ /* be careful: /proc/net/ip_acct_0 resets IP accounting */
+ if (ino == PROC_NET_IPACCT0) {
+ inode->i_mode = S_IFREG | S_IRUSR;
+ inode->i_op = &proc_net_inode_operations;
+ return;
+ }
+#endif
+#ifdef CONFIG_IP_FIREWALL
+ /* /proc/net/ip_forward_0 and /proc/net/ip_block_0 reset counters */
+ if ((ino == PROC_NET_IPFWFWD0) || (ino == PROC_NET_IPFWBLK0)) {
+ inode->i_mode = S_IFREG | S_IRUSR;
+ inode->i_op = &proc_net_inode_operations;
+ return;
+ }
+#endif
+
+ /* other files within /proc/net */
if ((ino >= PROC_NET_UNIX) && (ino < PROC_NET_LAST)) {
inode->i_mode = S_IFREG | S_IRUGO;
inode->i_op = &proc_net_inode_operations;
#if defined(CONFIG_WAVELAN)
extern int wavelan_get_info(char *, char **, off_t, int);
#endif /* defined(CONFIG_WAVELAN) */
+#ifdef CONFIG_IP_ACCT
extern int ip_acct_procinfo(char *, char **, off_t, int);
+extern int ip_acct0_procinfo(char *, char **, off_t, int);
+#endif /* CONFIG_IP_ACCT */
+#ifdef CONFIG_IP_FIREWALL
extern int ip_fw_blk_procinfo(char *, char **, off_t, int);
+extern int ip_fw_blk0_procinfo(char *, char **, off_t, int);
extern int ip_fw_fwd_procinfo(char *, char **, off_t, int);
+extern int ip_fw_fwd0_procinfo(char *, char **, off_t, int);
+#endif /* CONFIG_IP_FIREWALL */
extern int ip_msqhst_procinfo(char *, char **, off_t, int);
extern int ip_mc_procinfo(char *, char **, off_t, int);
#endif /* CONFIG_INET */
#endif
#ifdef CONFIG_IP_FIREWALL
{ PROC_NET_IPFWFWD, 10, "ip_forward"},
- { PROC_NET_IPBLFWD, 8, "ip_block"},
+ { PROC_NET_IPFWFWD0, 12, "ip_forward_0"},
+ { PROC_NET_IPFWBLK, 8, "ip_block"},
+ { PROC_NET_IPFWBLK0, 10, "ip_block_0"},
#endif
#ifdef CONFIG_IP_MASQUERADE
{ PROC_NET_IPMSQHST, 13, "ip_masquerade"},
#endif
#ifdef CONFIG_IP_ACCT
{ PROC_NET_IPACCT, 7, "ip_acct"},
+ { PROC_NET_IPACCT0, 9, "ip_acct_0"},
#endif
#if defined(CONFIG_WAVELAN)
{ PROC_NET_WAVELAN, 7, "wavelan" },
case PROC_NET_IPFWFWD:
length = ip_fw_fwd_procinfo(page, &start, file->f_pos,thistime);
break;
- case PROC_NET_IPBLFWD:
+ case PROC_NET_IPFWFWD0:
+ length = ip_fw_fwd0_procinfo(page, &start, file->f_pos,thistime);
+ break;
+ case PROC_NET_IPFWBLK:
length = ip_fw_blk_procinfo(page, &start, file->f_pos,thistime);
break;
+ case PROC_NET_IPFWBLK0:
+ length = ip_fw_blk0_procinfo(page, &start, file->f_pos,thistime);
+ break;
#endif
#ifdef CONFIG_IP_ACCT
case PROC_NET_IPACCT:
length = ip_acct_procinfo(page, &start, file->f_pos,thistime);
break;
+ case PROC_NET_IPACCT0:
+ length = ip_acct0_procinfo(page, &start, file->f_pos,thistime);
+ break;
#endif
#ifdef CONFIG_IP_MASQUERADE
case PROC_NET_IPMSQHST:
#ifndef __ALPHA_IO_H
#define __ALPHA_IO_H
-/*
- * Defines for the AlphaPC EISA IO and memory address space.
- */
+#include <linux/config.h>
#ifndef mb
#define mb() __asm__ __volatile__("mb": : :"memory")
#endif
/*
- * NOTE! Currently it never uses the HAE register, so these work only
- * for the low 25 bits of EISA addressing. That covers all of the IO
- * address space (16 bits), and most of the "normal" EISA memory space.
- * I'll fix it eventually, but I'll need to come up with a clean way
- * to handle races with interrupt services wanting to change HAE...
- */
-
-/*
- * NOTE 2! The memory operations do not set any memory barriers, as it's
- * not needed for cases like a frame buffer that is essentially memory-like.
- * You need to do them by hand if the operations depend on ordering.
- *
- * Similarly, the port IO operations do a "mb" only after a write operation:
- * if an mb is needed before (as in the case of doing memory mapped IO
- * first, and then a port IO operation to the same device), it needs to be
- * done by hand.
- *
- * After the above has bitten me 100 times, I'll give up and just do the
- * mb all the time, but right now I'm hoping this will work out. Avoiding
- * mb's may potentially be a noticeable speed improvement, but I can't
- * honestly say I've tested it.
- *
- * Handling interrupts that need to do mb's to synchronize to non-interrupts
- * is another fun race area. Don't do it (because if you do, I'll have to
- * do *everything* with interrupts disabled, ugh).
- */
-
-/*
- * Virtual -> physical identity mapping starts at this offset
- */
-#define IDENT_ADDR (0xfffffc0000000000UL)
-
-/*
- * EISA Interrupt Acknowledge address
- */
-#define EISA_INTA (IDENT_ADDR + 0x100000000UL)
-
-/*
- * FEPROM addresses
- */
-#define EISA_FEPROM0 (IDENT_ADDR + 0x180000000UL)
-#define EISA_FEPROM1 (IDENT_ADDR + 0x1A0000000UL)
-
-/*
- * VL82C106 base address
- */
-#define EISA_VL82C106 (IDENT_ADDR + 0x1C0000000UL)
-
-/*
- * EISA "Host Address Extension" address (bits 25-31 of the EISA address)
- */
-#define EISA_HAE (IDENT_ADDR + 0x1D0000000UL)
-
-/*
- * "SYSCTL" register address
- */
-#define EISA_SYSCTL (IDENT_ADDR + 0x1E0000000UL)
-
-/*
- * "spare" register address
- */
-#define EISA_SPARE (IDENT_ADDR + 0x1F0000000UL)
-
-/*
- * EISA memory address offset
- */
-#define EISA_MEM (IDENT_ADDR + 0x200000000UL)
-
-/*
- * EISA IO address offset
- */
-#define EISA_IO (IDENT_ADDR + 0x300000000UL)
-
-/*
- * IO functions
- *
- * The "local" functions are those that don't go out to the EISA bus,
- * but instead act on the VL82C106 chip directly.. This is mainly the
- * keyboard, RTC, printer and first two serial lines..
- *
- * The local stuff makes for some complications, but it seems to be
- * gone in the PCI version. I hope I can get DEC suckered^H^H^H^H^H^H^H^H
- * convinced that I need one of the newer machines.
- */
-extern inline unsigned int __local_inb(unsigned long addr)
-{
- long result = *(volatile int *) ((addr << 9) + EISA_VL82C106);
- return 0xffUL & result;
-}
-
-extern inline void __local_outb(unsigned char b, unsigned long addr)
-{
- *(volatile unsigned int *) ((addr << 9) + EISA_VL82C106) = b;
- mb();
-}
-
-extern inline unsigned int __inb(unsigned long addr)
-{
- long result = *(volatile int *) ((addr << 7) + EISA_IO + 0x00);
- result >>= (addr & 3) * 8;
- return 0xffUL & result;
-}
-
-extern inline void __outb(unsigned char b, unsigned long addr)
-{
- *(volatile unsigned int *) ((addr << 7) + EISA_IO + 0x00) = b * 0x01010101;
- mb();
-}
-
-/*
- * This is a stupid one: I'll make it a bitmap soon, promise..
- *
- * On the other hand: this allows gcc to optimize. Hmm. I'll
- * have to use the __constant_p() stuff here.
- *
- * The PCI version just returns zero all the time, I do believe..
+ * There are different version of the alpha motherboards: the
+ * "interesting" (read: slightly braindead) Jensen type hardware
+ * and the PCI version
*/
-extern inline int __is_local(unsigned long addr)
-{
- /* keyboard */
- if (addr == 0x60 || addr == 0x64)
- return 1;
-
- /* RTC */
- if (addr == 0x170 || addr == 0x171)
- return 1;
-
- /* motherboard COM2 */
- if (addr >= 0x2f8 && addr <= 0x2ff)
- return 1;
-
- /* motherboard LPT1 */
- if (addr >= 0x3bc && addr <= 0x3be)
- return 1;
-
- /* motherboard COM2 */
- if (addr >= 0x3f8 && addr <= 0x3ff)
- return 1;
-
- return 0;
-}
-
-extern inline unsigned int inb(unsigned long addr)
-{
- if (__is_local(addr))
- return __local_inb(addr);
- return __inb(addr);
-}
-
-extern inline void outb(unsigned char b, unsigned long addr)
-{
- if (__is_local(addr))
- __local_outb(b, addr);
- else
- __outb(b, addr);
-}
-
-extern inline unsigned int inw(unsigned long addr)
-{
- long result = *(volatile int *) ((addr << 7) + EISA_IO + 0x20);
- result >>= (addr & 3) * 8;
- return 0xffffUL & result;
-}
-
-extern inline unsigned int inl(unsigned long addr)
-{
- return *(volatile unsigned int *) ((addr << 7) + EISA_IO + 0x60);
-}
-
-extern inline void outw(unsigned short b, unsigned long addr)
-{
- *(volatile unsigned int *) ((addr << 7) + EISA_IO + 0x20) = b * 0x00010001;
- mb();
-}
-
-extern inline void outl(unsigned int b, unsigned long addr)
-{
- *(volatile unsigned int *) ((addr << 7) + EISA_IO + 0x60) = b;
- mb();
-}
-
-/*
- * Memory functions
- */
-extern inline unsigned long readb(unsigned long addr)
-{
- long result = *(volatile int *) ((addr << 7) + EISA_MEM + 0x00);
- result >>= (addr & 3) * 8;
- return 0xffUL & result;
-}
-
-extern inline unsigned long readw(unsigned long addr)
-{
- long result = *(volatile int *) ((addr << 7) + EISA_MEM + 0x20);
- result >>= (addr & 3) * 8;
- return 0xffffUL & result;
-}
-
-extern inline unsigned long readl(unsigned long addr)
-{
- return *(volatile unsigned int *) ((addr << 7) + EISA_MEM + 0x60);
-}
-
-extern inline void writeb(unsigned short b, unsigned long addr)
-{
- *(volatile unsigned int *) ((addr << 7) + EISA_MEM + 0x00) = b * 0x01010101;
-}
-
-extern inline void writew(unsigned short b, unsigned long addr)
-{
- *(volatile unsigned int *) ((addr << 7) + EISA_MEM + 0x20) = b * 0x00010001;
-}
-
-extern inline void writel(unsigned int b, unsigned long addr)
-{
- *(volatile unsigned int *) ((addr << 7) + EISA_MEM + 0x60) = b;
-}
-
-#define inb_p inb
-#define outb_p outb
+#ifdef CONFIG_PCI
+#include <asm/lca.h> /* get chip-specific definitions */
+#else
+#include <asm/jensen.h>
+#endif
#endif
--- /dev/null
+#ifndef __ALPHA_JENSEN_H
+#define __ALPHA_JENSEN_H
+
+/*
+ * Defines for the AlphaPC EISA IO and memory address space.
+ */
+
+/*
+ * NOTE! Currently it never uses the HAE register, so these work only
+ * for the low 25 bits of EISA addressing. That covers all of the IO
+ * address space (16 bits), and most of the "normal" EISA memory space.
+ * I'll fix it eventually, but I'll need to come up with a clean way
+ * to handle races with interrupt services wanting to change HAE...
+ */
+
+/*
+ * NOTE 2! The memory operations do not set any memory barriers, as it's
+ * not needed for cases like a frame buffer that is essentially memory-like.
+ * You need to do them by hand if the operations depend on ordering.
+ *
+ * Similarly, the port IO operations do a "mb" only after a write operation:
+ * if an mb is needed before (as in the case of doing memory mapped IO
+ * first, and then a port IO operation to the same device), it needs to be
+ * done by hand.
+ *
+ * After the above has bitten me 100 times, I'll give up and just do the
+ * mb all the time, but right now I'm hoping this will work out. Avoiding
+ * mb's may potentially be a noticeable speed improvement, but I can't
+ * honestly say I've tested it.
+ *
+ * Handling interrupts that need to do mb's to synchronize to non-interrupts
+ * is another fun race area. Don't do it (because if you do, I'll have to
+ * do *everything* with interrupts disabled, ugh).
+ */
+
+/*
+ * Virtual -> physical identity mapping starts at this offset
+ */
+#define IDENT_ADDR (0xfffffc0000000000UL)
+
+/*
+ * EISA Interrupt Acknowledge address
+ */
+#define EISA_INTA (IDENT_ADDR + 0x100000000UL)
+
+/*
+ * FEPROM addresses
+ */
+#define EISA_FEPROM0 (IDENT_ADDR + 0x180000000UL)
+#define EISA_FEPROM1 (IDENT_ADDR + 0x1A0000000UL)
+
+/*
+ * VL82C106 base address
+ */
+#define EISA_VL82C106 (IDENT_ADDR + 0x1C0000000UL)
+
+/*
+ * EISA "Host Address Extension" address (bits 25-31 of the EISA address)
+ */
+#define EISA_HAE (IDENT_ADDR + 0x1D0000000UL)
+
+/*
+ * "SYSCTL" register address
+ */
+#define EISA_SYSCTL (IDENT_ADDR + 0x1E0000000UL)
+
+/*
+ * "spare" register address
+ */
+#define EISA_SPARE (IDENT_ADDR + 0x1F0000000UL)
+
+/*
+ * EISA memory address offset
+ */
+#define EISA_MEM (IDENT_ADDR + 0x200000000UL)
+
+/*
+ * EISA IO address offset
+ */
+#define EISA_IO (IDENT_ADDR + 0x300000000UL)
+
+/*
+ * IO functions
+ *
+ * The "local" functions are those that don't go out to the EISA bus,
+ * but instead act on the VL82C106 chip directly.. This is mainly the
+ * keyboard, RTC, printer and first two serial lines..
+ *
+ * The local stuff makes for some complications, but it seems to be
+ * gone in the PCI version. I hope I can get DEC suckered^H^H^H^H^H^H^H^H
+ * convinced that I need one of the newer machines.
+ */
+extern inline unsigned int __local_inb(unsigned long addr)
+{
+ long result = *(volatile int *) ((addr << 9) + EISA_VL82C106);
+ return 0xffUL & result;
+}
+
+extern inline void __local_outb(unsigned char b, unsigned long addr)
+{
+ *(volatile unsigned int *) ((addr << 9) + EISA_VL82C106) = b;
+ mb();
+}
+
+extern inline unsigned int __inb(unsigned long addr)
+{
+ long result = *(volatile int *) ((addr << 7) + EISA_IO + 0x00);
+ result >>= (addr & 3) * 8;
+ return 0xffUL & result;
+}
+
+extern inline void __outb(unsigned char b, unsigned long addr)
+{
+ *(volatile unsigned int *) ((addr << 7) + EISA_IO + 0x00) = b * 0x01010101;
+ mb();
+}
+
+/*
+ * This is a stupid one: I'll make it a bitmap soon, promise..
+ *
+ * On the other hand: this allows gcc to optimize. Hmm. I'll
+ * have to use the __constant_p() stuff here.
+ */
+extern inline int __is_local(unsigned long addr)
+{
+ /* keyboard */
+ if (addr == 0x60 || addr == 0x64)
+ return 1;
+
+ /* RTC */
+ if (addr == 0x170 || addr == 0x171)
+ return 1;
+
+ /* motherboard COM2 */
+ if (addr >= 0x2f8 && addr <= 0x2ff)
+ return 1;
+
+ /* motherboard LPT1 */
+ if (addr >= 0x3bc && addr <= 0x3be)
+ return 1;
+
+ /* motherboard COM2 */
+ if (addr >= 0x3f8 && addr <= 0x3ff)
+ return 1;
+
+ return 0;
+}
+
+extern inline unsigned int inb(unsigned long addr)
+{
+ if (__is_local(addr))
+ return __local_inb(addr);
+ return __inb(addr);
+}
+
+extern inline void outb(unsigned char b, unsigned long addr)
+{
+ if (__is_local(addr))
+ __local_outb(b, addr);
+ else
+ __outb(b, addr);
+}
+
+extern inline unsigned int inw(unsigned long addr)
+{
+ long result = *(volatile int *) ((addr << 7) + EISA_IO + 0x20);
+ result >>= (addr & 3) * 8;
+ return 0xffffUL & result;
+}
+
+extern inline unsigned int inl(unsigned long addr)
+{
+ return *(volatile unsigned int *) ((addr << 7) + EISA_IO + 0x60);
+}
+
+extern inline void outw(unsigned short b, unsigned long addr)
+{
+ *(volatile unsigned int *) ((addr << 7) + EISA_IO + 0x20) = b * 0x00010001;
+ mb();
+}
+
+extern inline void outl(unsigned int b, unsigned long addr)
+{
+ *(volatile unsigned int *) ((addr << 7) + EISA_IO + 0x60) = b;
+ mb();
+}
+
+/*
+ * Memory functions
+ */
+extern inline unsigned long readb(unsigned long addr)
+{
+ long result = *(volatile int *) ((addr << 7) + EISA_MEM + 0x00);
+ result >>= (addr & 3) * 8;
+ return 0xffUL & result;
+}
+
+extern inline unsigned long readw(unsigned long addr)
+{
+ long result = *(volatile int *) ((addr << 7) + EISA_MEM + 0x20);
+ result >>= (addr & 3) * 8;
+ return 0xffffUL & result;
+}
+
+extern inline unsigned long readl(unsigned long addr)
+{
+ return *(volatile unsigned int *) ((addr << 7) + EISA_MEM + 0x60);
+}
+
+extern inline void writeb(unsigned short b, unsigned long addr)
+{
+ *(volatile unsigned int *) ((addr << 7) + EISA_MEM + 0x00) = b * 0x01010101;
+}
+
+extern inline void writew(unsigned short b, unsigned long addr)
+{
+ *(volatile unsigned int *) ((addr << 7) + EISA_MEM + 0x20) = b * 0x00010001;
+}
+
+extern inline void writel(unsigned int b, unsigned long addr)
+{
+ *(volatile unsigned int *) ((addr << 7) + EISA_MEM + 0x60) = b;
+}
+
+#define inb_p inb
+#define outb_p outb
+
+/*
+ * The Alpha Jensen hardware for some rather strange reason puts
+ * the RTC clock at 0x170 instead of 0x70. Probably due to some
+ * misguided idea about using 0x70 for NMI stuff.
+ *
+ * These defines will override the defaults when doing RTC queries
+ */
+#define RTC_PORT(x) (0x170+(x))
+#define RTC_ADDR(x) (x)
+#define RTC_ALWAYS_BCD 0
+
+#endif
--- /dev/null
+#ifndef __ALPHA_LCA__H
+#define __ALPHA_LCA__H
+
+/*
+ * Low Cost Alpha (LCA) definitions (these apply to 21066 and 21068,
+ * for example).
+ *
+ * This file is based on:
+ *
+ * DECchip 21066 and DECchip 21068 Alpha AXP Microprocessors
+ * Hardware Reference Manual; Digital Equipment Corp.; May 1994;
+ * Maynard, MA; Order Number: EC-N2681-71.
+ */
+
+/*
+ * NOTE! Currently, this never uses the HAE register, so it works only
+ * for the low 27 bits of the PCI sparse memory address space. Dense
+ * memory space doesn't require the HAE, but is restricted to aligned
+ * 32 and 64 bit accesses. Special Cycle and Interrupt Acknowledge
+ * cycles may also require the use of the HAE. The LCA limits I/O
+ * address space to the bottom 24 bits of address space, but this
+ * easily covers the 16 bit ISA I/O address space.
+ */
+
+/*
+ * NOTE 2! The memory operations do not set any memory barriers, as
+ * it's not needed for cases like a frame buffer that is essentially
+ * memory-like. You need to do them by hand if the operations depend
+ * on ordering.
+ *
+ * Similarly, the port I/O operations do a "mb" only after a write
+ * operation: if an mb is needed before (as in the case of doing
+ * memory mapped I/O first, and then a port I/O operation to the same
+ * device), it needs to be done by hand.
+ *
+ * After the above has bitten me 100 times, I'll give up and just do
+ * the mb all the time, but right now I'm hoping this will work out.
+ * Avoiding mb's may potentially be a noticeable speed improvement,
+ * but I can't honestly say I've tested it.
+ *
+ * Handling interrupts that need to do mb's to synchronize to
+ * non-interrupts is another fun race area. Don't do it (because if
+ * you do, I'll have to do *everything* with interrupts disabled,
+ * ugh).
+ */
+
+/*
+ * Virtual -> physical identity mapping starts at this offset.
+ */
+#define IDENT_ADDR (0xfffffc0000000000UL)
+
+/*
+ * I/O Controller registers:
+ */
+#define LCA_IOC_HAE (IDENT_ADDR + 0x180000000UL)
+#define LCA_IOC_CONF (IDENT_ADDR + 0x180000020UL)
+#define LCA_IOC_STAT0 (IDENT_ADDR + 0x180000040UL)
+#define LCA_IOC_STAT1 (IDENT_ADDR + 0x180000060UL)
+#define LCA_IOC_TBIA (IDENT_ADDR + 0x180000080UL)
+#define LCA_IOC_TB_ENA (IDENT_ADDR + 0x1800000a0UL)
+#define LCA_IOC_SFT_RST (IDENT_ADDR + 0x1800000c0UL)
+#define LCA_IOC_PAR_DIS (IDENT_ADDR + 0x1800000e0UL)
+#define LCA_IOC_W_BASE0 (IDENT_ADDR + 0x180000100UL)
+#define LCA_IOC_W_BASE1 (IDENT_ADDR + 0x180000120UL)
+#define LCA_IOC_W_MASK0 (IDENT_ADDR + 0x180000140UL)
+#define LCA_IOC_W_MASK1 (IDENT_ADDR + 0x180000160UL)
+#define LCA_IOC_T_BASE0 (IDENT_ADDR + 0x180000180UL)
+#define LCA_IOC_T_BASE1 (IDENT_ADDR + 0x1800001a0UL)
+#define LCA_IOC_TB_TAG0 (IDENT_ADDR + 0x188000000UL)
+#define LCA_IOC_TB_TAG1 (IDENT_ADDR + 0x188000020UL)
+#define LCA_IOC_TB_TAG2 (IDENT_ADDR + 0x188000040UL)
+#define LCA_IOC_TB_TAG3 (IDENT_ADDR + 0x188000060UL)
+#define LCA_IOC_TB_TAG4 (IDENT_ADDR + 0x188000070UL)
+#define LCA_IOC_TB_TAG5 (IDENT_ADDR + 0x1880000a0UL)
+#define LCA_IOC_TB_TAG6 (IDENT_ADDR + 0x1880000c0UL)
+#define LCA_IOC_TB_TAG7 (IDENT_ADDR + 0x1880000e0UL)
+
+/*
+ * Memory spaces:
+ */
+#define LCA_IACK_SC (IDENT_ADDR + 0x1a0000000UL)
+#define LCA_CONF (IDENT_ADDR + 0x1e0000000UL)
+#define LCA_IO (IDENT_ADDR + 0x1c0000000UL)
+#define LCA_SPARSE_MEM (IDENT_ADDR + 0x200000000UL)
+#define LCA_DENSE_MEM (IDENT_ADDR + 0x300000000UL)
+
+/*
+ * Bit definitions for I/O Controller status register 0:
+ */
+#define LCA_IOC_STAT0_CMD 0xf
+#define LCA_IOC_STAT0_ERR (1<<4)
+#define LCA_IOC_STAT0_LOST (1<<5)
+#define LCA_IOC_STAT0_THIT (1<<6)
+#define LCA_IOC_STAT0_TREF (1<<7)
+#define LCA_IOC_STAT0_CODE_SHIFT 8
+#define LCA_IOC_STAT0_CODE_MASK 0x7
+#define LCA_IOC_STAT0_P_NBR_SHIFT 13
+#define LCA_IOC_STAT0_P_NBR_MASK 0x7ffff
+
+/*
+ * I/O functions:
+ *
+ * Unlike Jensen, the Noname machines have no concept of local
+ * I/O---everything goes over the PCI bus.
+ *
+ * There is plenty room for optimization here. In particular,
+ * the Alpha's insb/insw/extb/extw should be useful in moving
+ * data to/from the right byte-lanes.
+ */
+
+extern inline unsigned int
+inb(unsigned long addr)
+{
+ long result = *(volatile int *) ((addr << 5) + LCA_IO + 0x00);
+ result >>= (addr & 3) * 8;
+ return 0xffUL & result;
+}
+
+extern inline unsigned int
+inw(unsigned long addr)
+{
+ long result = *(volatile int *) ((addr << 5) + LCA_IO + 0x08);
+ result >>= (addr & 3) * 8;
+ return 0xffffUL & result;
+}
+
+extern inline unsigned int
+inl(unsigned long addr)
+{
+ return *(volatile unsigned int *) ((addr << 5) + LCA_IO + 0x18);
+}
+
+extern inline void
+outb(unsigned char b, unsigned long addr)
+{
+ *(volatile unsigned int *) ((addr << 5) + LCA_IO + 0x00) = b * 0x01010101;
+ mb();
+}
+
+extern inline void
+outw(unsigned char b, unsigned long addr)
+{
+ *(volatile unsigned int *) ((addr << 5) + LCA_IO + 0x08) = b * 0x00010001;
+ mb();
+}
+
+extern inline void
+outl(unsigned char b, unsigned long addr)
+{
+ *(volatile unsigned int *) ((addr << 5) + LCA_IO + 0x18) = b;
+ mb();
+}
+
+
+/*
+ * Memory functions. 64-bit and 32-bit accesses are done through
+ * dense memory space, everything else through sparse space.
+ */
+
+extern inline unsigned long
+readb(unsigned long addr)
+{
+ long result = *(volatile int *) ((addr << 5) + LCA_SPARSE_MEM + 0x00);
+ result >>= (addr & 3) * 8;
+ return 0xffUL & result;
+}
+
+extern inline unsigned long
+readw(unsigned long addr)
+{
+ long result = *(volatile int *) ((addr << 5) + LCA_SPARSE_MEM + 0x08);
+ result >>= (addr & 3) * 8;
+ return 0xffffUL & result;
+}
+
+extern inline unsigned long
+readl(unsigned long addr)
+{
+ return *(volatile int *) (addr + LCA_DENSE_MEM);
+}
+
+extern inline void
+writeb(unsigned short b, unsigned long addr)
+{
+ *(volatile unsigned int *) ((addr << 5) + LCA_SPARSE_MEM + 0x00) =
+ b * 0x01010101;
+}
+
+extern inline void
+writew(unsigned short b, unsigned long addr)
+{
+ *(volatile unsigned int *) ((addr << 5) + LCA_SPARSE_MEM + 0x08) =
+ b * 0x00010001;
+}
+
+extern inline void
+writel(unsigned short b, unsigned long addr)
+{
+ *(volatile unsigned int *) (addr + LCA_DENSE_MEM) = b;
+}
+
+#define inb_local inb
+#define outb_local outb
+#define inb_p inb
+#define outb_p outb
+
+#endif
/*
* NOTE! The "accessed" bit isn't necessarily exact: it can be kept exactly
- * by software (use the KRE/URE/KWE/UWE bits appropritely), but I'll fake it.
+ * by software (use the KRE/URE/KWE/UWE bits appropriately), but I'll fake it.
* Under Linux/AXP, the "accessed" bit just means "read", and I'll just use
* the KRE/URE bits to watch for it. That way we don't need to overload the
* KWE/UWE bits with both handling dirty and accessed.
unsigned long r18;
};
+#ifdef __KERNEL__
#define user_mode(regs) ((regs)->ps & 8)
+extern void show_regs(struct pt_regs *);
+#endif
#endif
extern unsigned long rdusp(void);
#define halt() __asm__ __volatile__(".long 0");
-#define move_to_user_mode() halt()
-#define switch_to(x) halt()
+
+#define move_to_user_mode() printk("Null move_to_user_mode\n")
+#define switch_to(x) panic("switch_to() not yet done")
#ifndef mb
#define mb() __asm__ __volatile__("mb": : :"memory")
#ifdef __KERNEL__
#define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->cs))
+extern void show_regs(struct pt_regs *);
#endif
#endif
*/
extern struct desc_struct default_ldt;
+/*
+ * disable hlt during certain critical i/o operations
+ */
+#define HAVE_DISABLE_HLT
+void disable_hlt(void);
+void enable_hlt(void);
+
#endif
--- /dev/null
+/* clock.h: Definitions for the clock/timer chips on the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+/* Clock timer structures. The interrupt timer has two properties which
+ * are the counter (which is handled in do_timer in sched.c) and the limit.
+ * This limit is where the timer's counter 'wraps' around. Oddly enough,
+ * the sun4c timer when it hits the limit wraps back to 1 and not zero
+ * thus when calculating the value at which it will fire a microsecond you
+ * must adjust by one. Thanks SUN for designing such great hardware ;(
+ */
+
+/* Note that I am only going to use the timer that interrupts at
+ * Sparc IRQ 10. There is another one available that can fire at
+ * IRQ 14. If I can think of some creative uses for it this may
+ * change. It might make a nice kernel/user profiler etc.
+ */
+
+struct sparc_timer_info {
+ unsigned int cur_count10;
+ unsigned int timer_limit10;
+ unsigned int cur_count14;
+ unsigned int timer_limit14;
+};
+
+struct sparc_clock_info {
+ unsigned char hsec;
+ unsigned char hr;
+ unsigned char min;
+ unsigned char sec;
+ unsigned char mon;
+ unsigned char day;
+ unsigned char yr;
+ unsigned char wkday;
+ unsigned char ram_hsec;
+ unsigned char ram_hr;
+ unsigned char ram_min;
+ unsigned char ram_sec;
+ unsigned char ram_mon;
+ unsigned char ram_day;
+ unsigned char ram_year;
+ unsigned char ram_wkday;
+ unsigned char intr_reg;
+ unsigned char cmd_reg;
+ unsigned char foo[14];
+};
+
+/* YUCK YUCK YUCK, grrr... */
+#define TIMER_STRUCT ((struct sparc_timer_info *)((struct sparc_clock_info *) TIMER_VADDR))
+
/* cprefix.h: This file is included by assembly source which needs
* to know what the c-label prefixes are. The newer versions
* of cpp that come with gcc predefine such things to help
- * us out. The reason this stuff is needed is to make
+ * us out. The reason this stuff is neaded is to make
* solaris compiles of the kernel work.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
#define SUN4C_SEGSZ (1 << 18)
#define USRSTACK 0x0 /* no joke, this is temporary, trust me */
#define INT_ENABLE_REG_PHYSADR 0xf5000000
-#define INTS_ALL_ENAB 0x01
+#define INTS_ENAB 0x01
#define WRITE_PAUSE nop; nop; nop;
+#define PAGE_SIZE 4096
+
/* Here are some trap goodies */
#ifndef __SPARC_IO_H
#define __SPARC_IO_H
+#include <asm/page.h> /* IO address mapping routines need this */
+
/*
* Defines for io operations on the Sparc. Whether a memory access is going
* to i/o sparc is encoded in the pte. The type bits determine whether this
#define inb_p inb
#define outb_p outb
+extern inline void mapioaddr(unsigned long physaddr, unsigned long virt_addr)
+{
+ unsigned long page_entry;
+
+ page_entry = physaddr >> PAGE_SHIFT;
+ page_entry |= (PTE_V | PTE_ACC | PTE_W | PTE_P | PTE_IO); /* kernel io addr */
+
+ put_pte(page_entry, virt_addr);
+ return;
+}
+
#endif /* !(__SPARC_IO_H) */
#include <asm/asi.h> /* for get/set segmap/pte routines */
#include <asm/contregs.h> /* for switch_to_context */
-/* The current va context is global and known, so all that is needed to
- * do an invalidate is flush the VAC.
- */
-
-#define invalidate() flush_vac_context() /* how conveeeiiiiinnnient :> */
-
-
#define PAGE_SHIFT 12 /* This is the virtual page... */
-#define PGDIR_SHIFT 18 /* This is the virtual segment */
-#define PAGE_SIZE 4096
-#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
-#define PAGE_OFFSET 0
-#define MAP_NR(addr) (((addr)) >> PAGE_SHIFT)
-#define MAP_PAGE_RESERVED (1<<31)
+#ifndef __ASSEMBLY__
+#define PAGE_SIZE (1UL << PAGE_SHIFT)
+/* to mask away the intra-page address bits */
+#define PAGE_MASK (~(PAGE_SIZE-1))
+#ifdef __KERNEL__
-#define PAGE_PRESENT 0x001
-#define PAGE_RW 0x002
-#define PAGE_USER 0x004
-#define PAGE_ACCESSED 0x020
-#define PAGE_DIRTY 0x040
-#define PAGE_COW 0x200 /* implemented in software (one of the AVL bits) */
+#define CONFIG_STRICT_MM_TYPECHECKS
-#define PAGE_PRIVATE (PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_ACCESSED | PAGE_COW)
-#define PAGE_SHARED (PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_ACCESSED)
-#define PAGE_COPY (PAGE_PRESENT | PAGE_USER | PAGE_ACCESSED | PAGE_COW)
-#define PAGE_READONLY (PAGE_PRESENT | PAGE_USER | PAGE_ACCESSED)
-#define PAGE_EXECONLY (PAGE_PRESENT | PAGE_USER | PAGE_ACCESSED)
-#define PAGE_TABLE (PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_ACCESSED)
+#ifdef CONFIG_STRICT_MM_TYPECHECKS
+/*
+ * These are used to make use of C type-checking..
+ */
+typedef struct { unsigned long pte; } pte_t;
+typedef struct { unsigned long pmd; } pmd_t;
+typedef struct { unsigned long pgd; } pgd_t;
+typedef struct { unsigned long pgprot; } pgprot_t;
+
+#define pte_val(x) ((x).pte)
+#define pmd_val(x) ((x).pmd)
+#define pgd_val(x) ((x).pgd)
+#define pgprot_val(x) ((x).pgprot)
+
+#define __pte(x) ((pte_t) { (x) } )
+#define __pmd(x) ((pmd_t) { (x) } )
+#define __pgd(x) ((pgd_t) { (x) } )
+#define __pgprot(x) ((pgprot_t) { (x) } )
+
+#else
+/*
+ * .. while these make it easier on the compiler
+ */
+typedef unsigned long pte_t;
+typedef unsigned long pmd_t;
+typedef unsigned long pgd_t;
+typedef unsigned long pgprot_t;
-#define PAGE_CHG_MASK (PAGE_MASK | PAGE_ACCESSED | PAGE_DIRTY)
+#define pte_val(x) (x)
+#define pmd_val(x) (x)
+#define pgd_val(x) (x)
+#define pgprot_val(x) (x)
-#ifdef __KERNEL__
+#define __pte(x) (x)
+#define __pmd(x) (x)
+#define __pgd(x) (x)
+#define __pgprot(x) (x)
-/* number of bits that fit into a memory pointer */
-#define BITS_PER_PTR (8*sizeof(unsigned long)) /* better check this stuff */
+#endif
-/* to mask away the intra-page address bits */
-#define PAGE_MASK (~(PAGE_SIZE-1))
+/* The current va context is global and known, so all that is needed to
+ * do an invalidate is flush the VAC.
+ */
-/* to mask away the intra-page address bits */
-#define PGDIR_MASK (~(PGDIR_SIZE-1))
+#define invalidate() flush_vac_context() /* how conveeeiiiiinnnient :> */
/* to align the pointer to the (next) page boundary */
#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)
-#define PGDIR_ALIGN(addr) (((addr)+PGDIR_SIZE-1)&PGDIR_MASK)
-/* to align the pointer to a pointer address */
-#define PTR_MASK (~(sizeof(void*)-1))
+#define PAGE_OFFSET 0
+#define MAP_NR(addr) (((unsigned long)(addr)) >> PAGE_SHIFT)
+#define MAP_PAGE_RESERVED (1<<15)
-#define SIZEOF_PTR_LOG2 2
+#endif /* !(__ASSEMBLY__) */
/* The rest is kind of funky because on the sparc, the offsets into the mmu
* entries are encoded in magic alternate address space tables. I will
* Much thought must go into this code. (davem@caip.rutgers.edu)
*/
-#define PAGE_DIR_OFFSET(base, address) ((unsigned long *) 0)
-#define PAGE_PTR(address) ((unsigned long) 0)
-#define PTRS_PER_PAGE (64) /* 64 pte's per phys_seg */
-
/* Bitfields within a Sparc sun4c PTE (page table entry). */
#define PTE_V 0x80000000 /* valid bit */
#define PTE_RESV 0x00f80000 /* reserved bits */
#define PTE_PHYPG 0x0007ffff /* phys pg number, sun4c only uses 16bits */
-/* termed a 'page table' in the linux kernel, a segmap entry is obtained
- * with the following macro
- */
-
-#ifndef __ASSEMBLY__ /* for head.S */
-/*
- * BAD_PAGETABLE is used when we need a bogus page-table, while
- * BAD_PAGE is used for a bogus page.
- *
- * ZERO_PAGE is a global shared page that is always zero: used
- * for zero-mapped memory areas etc..
- */
-extern unsigned long __bad_page(void);
-extern unsigned long __bad_pagetable(void);
-extern unsigned long __zero_page(void);
-
-typedef unsigned int mem_map_t;
-
-#define BAD_PAGETABLE __bad_pagetable()
-#define BAD_PAGE __bad_page()
-#define ZERO_PAGE __zero_page()
-
extern __inline__ unsigned long get_segmap(unsigned long addr)
{
register unsigned long entry;
return ctx;
}
-/* to set the page-dir
- *
- * On the Sparc this is a nop for now. It will set the proper segmap
- * in the real implementation.
- */
-
-#define SET_PAGE_DIR(tsk,pgdir)
-
-
-#endif /* !(__ASSEMBLY__) */
+typedef unsigned short mem_map_t;
#endif /* __KERNEL__ */
--- /dev/null
+#ifndef _SPARC_PGTABLE_H
+#define _SPARC_PGTABLE_H
+
+/* asm-sparc/pgtable.h: Defines and functions used to work
+ * with Sparc page tables.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+/* PMD_SHIFT determines the size of the area a second-level page table can map */
+#define PMD_SHIFT 22
+#define PMD_SIZE (1UL << PMD_SHIFT)
+#define PMD_MASK (~(PMD_SIZE-1))
+
+/* PGDIR_SHIFT determines what a third-level page table entry can map */
+#define PGDIR_SHIFT 22
+#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK (~(PGDIR_SIZE-1))
+#define PGDIR_ALIGN(addr) (((addr)+PGDIR_SIZE-1)&PGDIR_MASK)
+
+/*
+ * Just following the i386 lead, because it works on the Sparc sun4c
+ * machines. Two-level, therefore there is no real PMD.
+ */
+
+#define PTRS_PER_PTE 1024
+#define PTRS_PER_PMD 1
+#define PTRS_PER_PGD 1024
+
+/* the no. of pointers that fit on a page: this will go away */
+#define PTRS_PER_PAGE (PAGE_SIZE/sizeof(void*))
+
+/* Just any arbitrary offset to the start of the vmalloc VM area: the
+ * current 8MB value just means that there will be a 8MB "hole" after the
+ * physical memory until the kernel virtual memory starts. That means that
+ * any out-of-bounds memory accesses will hopefully be caught.
+ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
+ * area for the same reason. ;)
+ */
+#define VMALLOC_OFFSET (8*1024*1024)
+#define VMALLOC_START ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
+#define VMALLOC_VMADDR(x) (TASK_SIZE + (unsigned long)(x))
+
+/*
+ * Sparc page table fields.
+ */
+
+#define _PAGE_VALID 0x80000000 /* valid page */
+#define _PAGE_WRITE 0x40000000 /* can be written to */
+#define _PAGE_PRIV 0x20000000 /* bit to signify privileged page */
+#define _PAGE_REF 0x02000000 /* Page had been accessed/referenced */
+#define _PAGE_DIRTY 0x01000000 /* Page has been modified, is dirty */
+#define _PAGE_COW 0x00800000 /* COW page, hardware ignores this bit (untested) */
+
+
+/* Sparc sun4c mmu has only a writable bit. Thus if a page is valid it can be
+ * read in a load, and executed as code automatically. Although, the memory fault
+ * hardware does make a distinction between date-read faults and insn-read faults
+ * which is determined by which trap happened plus magic sync/async fault register
+ * values which must be checked in the actual fault handler.
+ */
+
+/* We want the swapper not to swap out page tables, thus dirty and writable
+ * so that the kernel can change the entries as needed. Also valid for
+ * obvious reasons.
+ */
+#define _PAGE_TABLE (_PAGE_VALID | _PAGE_WRITE | _PAGE_DIRTY)
+#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_REF | _PAGE_DIRTY)
+
+#define PAGE_NONE __pgprot(_PAGE_VALID | _PAGE_REF)
+#define PAGE_SHARED __pgprot(_PAGE_VALID | _PAGE_WRITE | _PAGE_REF)
+#define PAGE_COPY __pgprot(_PAGE_VALID | _PAGE_REF | _PAGE_COW)
+#define PAGE_READONLY __pgprot(_PAGE_VALID | _PAGE_REF)
+#define PAGE_KERNEL __pgprot(_PAGE_VALID | _PAGE_PRIV)
+
+#define _PAGE_NORMAL(x) __pgprot(_PAGE_VALID | _PAGE_REF | (x))
+
+/* I define these like the i386 does because the check for text or data fault
+ * is done at trap time by the low level handler. Maybe I can set these bits
+ * then once determined. I leave them like this for now though.
+ */
+#define __P000 PAGE_NONE
+#define __P001 PAGE_READONLY
+#define __P010 PAGE_COPY
+#define __P011 PAGE_COPY
+#define __P100 PAGE_READONLY
+#define __P101 PAGE_READONLY
+#define __P110 PAGE_COPY
+#define __P111 PAGE_COPY
+
+#define __S000 PAGE_NONE
+#define __S001 PAGE_READONLY
+#define __S010 PAGE_SHARED
+#define __S011 PAGE_SHARED
+#define __S100 PAGE_READONLY
+#define __S101 PAGE_READONLY
+#define __S110 PAGE_SHARED
+#define __S111 PAGE_SHARED
+
+
+extern unsigned long pg0[1024];
+
+/*
+ * BAD_PAGETABLE is used when we need a bogus page-table, while
+ * BAD_PAGE is used for a bogus page.
+ *
+ * ZERO_PAGE is a global shared page that is always zero: used
+ * for zero-mapped memory areas etc..
+ */
+extern pte_t __bad_page(void);
+extern pte_t * __bad_pagetable(void);
+
+extern unsigned long __zero_page(void);
+
+
+#define BAD_PAGETABLE __bad_pagetable()
+#define BAD_PAGE __bad_page()
+#define ZERO_PAGE __zero_page()
+
+/* number of bits that fit into a memory pointer */
+#define BITS_PER_PTR (8*sizeof(unsigned long)) /* better check this stuff */
+
+/* to align the pointer to a pointer address */
+#define PTR_MASK (~(sizeof(void*)-1))
+
+
+#define SIZEOF_PTR_LOG2 2
+
+
+/* to set the page-dir
+ *
+ * On the Sparc the page segments hold 64 pte's which means 256k/segment.
+ * Therefore there is no global idea of 'the' page directory, although we
+ * make a virtual one in kernel memory so that we can keep the stats on
+ * all the pages since not all can be loaded at once in the mmu.
+ */
+
+#define SET_PAGE_DIR(tsk,pgdir)
+
+/* to find an entry in a page-table */
+#define PAGE_PTR(address) \
+((unsigned long)(address)>>(PAGE_SHIFT-SIZEOF_PTR_LOG2)&PTR_MASK&~PAGE_MASK)
+
+extern unsigned long high_memory;
+
+extern inline int pte_none(pte_t pte) { return !pte_val(pte); }
+extern inline int pte_present(pte_t pte) { return pte_val(pte) & _PAGE_VALID; }
+extern inline void pte_clear(pte_t *ptep) { pte_val(*ptep) = 0; }
+
+extern inline int pmd_none(pmd_t pmd) { return !pmd_val(pmd); }
+extern inline int pmd_bad(pmd_t pmd) { return (pmd_val(pmd) & ~PAGE_MASK) != _PAGE_TABLE || pmd_val(pmd) > high_memory; }
+extern inline int pmd_present(pmd_t pmd) { return pmd_val(pmd) & _PAGE_VALID; }
+extern inline void pmd_clear(pmd_t * pmdp) { pmd_val(*pmdp) = 0; }
+
+extern inline int pgd_none(pgd_t pgd) { return !pgd_val(pgd); }
+extern inline int pgd_bad(pgd_t pgd) { return (pgd_val(pgd) & ~PAGE_MASK) != _PAGE_TABLE || pgd_val(pgd) > high_memory; }
+extern inline int pgd_present(pgd_t pgd) { return pgd_val(pgd) & _PAGE_VALID; }
+extern inline void pgd_clear(pgd_t * pgdp) { pgd_val(*pgdp) = 0; }
+
+/*
+ * The following only work if pte_present() is true.
+ * Undefined behaviour if not..
+ */
+extern inline int pte_read(pte_t pte) { return pte_val(pte) & _PAGE_VALID; }
+extern inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_WRITE; }
+extern inline int pte_exec(pte_t pte) { return pte_val(pte) & _PAGE_VALID; }
+extern inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_REF; }
+extern inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_REF; }
+extern inline int pte_cow(pte_t pte) { return pte_val(pte) & _PAGE_COW; }
+
+extern inline pte_t pte_wrprotect(pte_t pte) { pte_val(pte) &= ~_PAGE_WRITE; return pte; }
+extern inline pte_t pte_rdprotect(pte_t pte) { pte_val(pte) &= ~_PAGE_VALID; return pte; }
+extern inline pte_t pte_exprotect(pte_t pte) { pte_val(pte) &= ~_PAGE_VALID; return pte; }
+extern inline pte_t pte_mkclean(pte_t pte) { pte_val(pte) &= ~_PAGE_DIRTY; return pte; }
+extern inline pte_t pte_mkold(pte_t pte) { pte_val(pte) &= ~_PAGE_REF; return pte; }
+extern inline pte_t pte_uncow(pte_t pte) { pte_val(pte) &= ~_PAGE_COW; return pte; }
+extern inline pte_t pte_mkwrite(pte_t pte) { pte_val(pte) |= _PAGE_WRITE; return pte; }
+extern inline pte_t pte_mkread(pte_t pte) { pte_val(pte) |= _PAGE_VALID; return pte; }
+extern inline pte_t pte_mkexec(pte_t pte) { pte_val(pte) |= _PAGE_VALID; return pte; }
+extern inline pte_t pte_mkdirty(pte_t pte) { pte_val(pte) |= _PAGE_DIRTY; return pte; }
+extern inline pte_t pte_mkyoung(pte_t pte) { pte_val(pte) |= _PAGE_REF; return pte; }
+extern inline pte_t pte_mkcow(pte_t pte) { pte_val(pte) |= _PAGE_COW; return pte; }
+
+/*
+ * Conversion functions: convert a page and protection to a page entry,
+ * and a page entry and page directory to the page they refer to.
+ */
+extern inline pte_t mk_pte(unsigned long page, pgprot_t pgprot)
+{ pte_t pte; pte_val(pte) = page | pgprot_val(pgprot); return pte; }
+
+extern inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
+{ pte_val(pte) = (pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot); return pte; }
+
+extern inline unsigned long pte_page(pte_t pte) { return pte_val(pte) & PAGE_MASK; }
+
+extern inline unsigned long pmd_page(pmd_t pmd) { return pmd_val(pmd) & PAGE_MASK; }
+
+extern inline unsigned long pgd_page(pgd_t pgd) { return pgd_val(pgd) & PAGE_MASK; }
+
+extern inline void pgd_set(pgd_t * pgdp, pte_t * ptep)
+{ pgd_val(*pgdp) = _PAGE_TABLE | (unsigned long) ptep; }
+
+/* to find an entry in a page-table-directory */
+#define PAGE_DIR_OFFSET(tsk,address) \
+((((unsigned long)(address)) >> 22) + (pgd_t *) (tsk)->tss.cr3)
+
+/* to find an entry in a page-table-directory */
+extern inline pgd_t * pgd_offset(struct task_struct * tsk, unsigned long address)
+{
+ return (pgd_t *) tsk->tss.cr3 + (address >> PGDIR_SHIFT);
+}
+
+/* Find an entry in the second-level page table.. */
+extern inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address)
+{
+ return (pmd_t *) dir;
+}
+
+/* Find an entry in the third-level page table.. */
+extern inline pte_t * pte_offset(pmd_t * dir, unsigned long address)
+{
+ return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1));
+}
+
+
+/*
+ * Allocate and free page tables. The xxx_kernel() versions are
+ * used to allocate a kernel page table - this turns on ASN bits
+ * if any, and marks the page tables reserved.
+ */
+extern inline void pte_free_kernel(pte_t * pte)
+{
+ mem_map[MAP_NR(pte)] = 1;
+ free_page((unsigned long) pte);
+}
+
+extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address)
+{
+ address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
+ if (pmd_none(*pmd)) {
+ pte_t * page = (pte_t *) get_free_page(GFP_KERNEL);
+ if (pmd_none(*pmd)) {
+ if (page) {
+ pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) page;
+ mem_map[MAP_NR(page)] = MAP_PAGE_RESERVED;
+ return page + address;
+ }
+ pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
+ return NULL;
+ }
+ free_page((unsigned long) page);
+ }
+ if (pmd_bad(*pmd)) {
+ printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
+ pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
+ return NULL;
+ }
+ return (pte_t *) pmd_page(*pmd) + address;
+}
+
+/*
+ * allocating and freeing a pmd is trivial: the 1-entry pmd is
+ * inside the pgd, so has no extra memory associated with it.
+ */
+extern inline void pmd_free_kernel(pmd_t * pmd)
+{
+}
+
+extern inline pmd_t * pmd_alloc_kernel(pgd_t * pgd, unsigned long address)
+{
+ return (pmd_t *) pgd;
+}
+
+extern inline void pte_free(pte_t * pte)
+{
+ free_page((unsigned long) pte);
+}
+
+extern inline pte_t * pte_alloc(pmd_t * pmd, unsigned long address)
+{
+ address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
+ if (pmd_none(*pmd)) {
+ pte_t * page = (pte_t *) get_free_page(GFP_KERNEL);
+ if (pmd_none(*pmd)) {
+ if (page) {
+ pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) page;
+ return page + address;
+ }
+ pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
+ return NULL;
+ }
+ free_page((unsigned long) page);
+ }
+ if (pmd_bad(*pmd)) {
+ printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
+ pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
+ return NULL;
+ }
+ return (pte_t *) pmd_page(*pmd) + address;
+}
+
+/*
+ * allocating and freeing a pmd is trivial: the 1-entry pmd is
+ * inside the pgd, so has no extra memory associated with it.
+ */
+extern inline void pmd_free(pmd_t * pmd)
+{
+}
+
+extern inline pmd_t * pmd_alloc(pgd_t * pgd, unsigned long address)
+{
+ return (pmd_t *) pgd;
+}
+
+extern pgd_t swapper_pg_dir[1024];
+
+#endif /* !(_SPARC_PGTABLE_H) */
unsigned long f_regs[64]; /* yuck yuck yuck */
};
-#define user_mode(regs) (~((regs)->ps&0x1)) /* if previous supervisor is 0, came from user */
+#ifdef __KERNEL__
+#define user_mode(regs) (0x0) /* if previous supervisor is 0, came from user */
+extern void show_regs(struct pt_regs *);
+#endif
#endif
*/
#include <asm/openprom.h>
+#include <asm/psr.h>
#define INIT_PCB 0x00011fe0
#define INIT_STACK 0x00013fe0
#define EMPTY_PGE 0x00001000
#define ZERO_PGE 0x00001000
+#define IRQ_ENA_ADR 0x2000 /* This is a bitmap of all activated IRQ's
+ * which is mapped in head.S during boot.
+ */
+
#ifndef __ASSEMBLY__
extern void wrent(void *, unsigned long);
*/
#define swpipl(__new_ipl) \
-({ unsigned long __old_ipl, psr; \
+({ unsigned long psr, retval; \
__asm__ __volatile__( \
- "rd %%psr, %0\n\t" : "=&r" (__old_ipl)); \
+ "rd %%psr, %0\n\t" : "=&r" (psr)); \
+retval = psr; \
+psr = (psr & ~(PSR_PIL)); \
+psr |= ((__new_ipl << 8) & PSR_PIL); \
__asm__ __volatile__( \
- "and %1, 15, %0\n\t" \
- "sll %0, 8, %0\n\t" \
- "or %0, %2, %0\n\t" \
"wr %0, 0x0, %%psr\n\t" \
- : "=&r" (psr) \
- : "r" (__new_ipl), "r" (__old_ipl)); \
-__old_ipl = ((__old_ipl>>8)&15); \
-__old_ipl; })
+ : : "r" (psr)); \
+retval = ((retval>>8)&15); \
+retval; })
#define cli() swpipl(15) /* 15 = no int's except nmi's */
#define sti() swpipl(0) /* same as alpha */
_set_gate(a,12,3,addr)
+extern inline unsigned int get_psr(void)
+{
+ unsigned int ret_val;
+ __asm__("rd %%psr, %0\n\t" :
+ "=r" (ret_val));
+ return ret_val;
+}
+
+extern inline void put_psr(unsigned int new_psr)
+{
+ __asm__("wr %0, 0x0, %%psr\n\t" : :
+ "r" (new_psr));
+}
+
/* Must this be atomic? */
extern inline void *xchg_u32(int * m, unsigned long val)
--- /dev/null
+#ifndef _SPARC_VADDRS_H
+#define _SPARC_VADDRS_H
+
+/* asm-sparc/vaddrs.h: Here will be define the virtual addresses at
+ * which important I/O addresses will be mapped.
+ * For instance the timer register virtual address
+ * is defined here.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#define TIMER_VADDR 0x3000 /* Next page after where the interrupt enable
+ * register gets mapped at boot.
+ */
+
+#endif /* !(_SPARC_VADDRS_H) */
-/* $Id: aztcd.h,v 0.80 1995/01/21 19:55:04 root Exp $
+/* $Id: aztcd.h,v 0.90 1995/02/02 18:14:28 root Exp $
* Definitions for a AztechCD268 CD-ROM interface
* Copyright (C) 1994, 1995 Werner Zimmermann
*
#define ACMD_GET_ERROR 0x40 /* get error code */
#define ACMD_GET_STATUS 0x41 /* get status */
#define ACMD_GET_Q_CHANNEL 0x50 /* read info from q channel */
-#define ACMD_EJECT 0x60 /* eject/open */
+#define ACMD_EJECT 0x60 /* eject/open tray */
+#define ACMD_CLOSE 0x61 /* close tray */
#define ACMD_PAUSE 0x80 /* pause */
#define ACMD_STOP 0x81 /* stop play */
#define ACMD_PLAY_AUDIO 0x90 /* play audio track */
+#define ACMD_SET_VOLUME 0x93 /* set audio level */
#define ACMD_GET_VERSION 0xA0 /* get firmware version */
#define ACMD_SET_MODE 0xA1 /* set drive mode */
-#define ACMD_SET_VOLUME 0xAE /* set audio level */
#define SET_TIMER(func, jifs) \
delay_timer.expires = jifs; \
#ifndef BIOS32_H
#define BIOS32_H
+extern int pcibios_present (void);
unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end);
+#define PCIBIOS_SUCCESSFUL 0x00
+#define PCIBIOS_FUNC_NOT_SUPPORTED 0x81
+#define PCIBIOS_BAD_VENDOR_ID 0x83
+#define PCIBIOS_DEVICE_NOT_FOUND 0x86
+#define PCIBIOS_BAD_REGISTER_NUMBER 0x87
+
extern int pcibios_find_class (unsigned long class_code, unsigned short index,
unsigned char *bus, unsigned char *device_fn);
extern int pcibios_find_device (unsigned short vendor, unsigned short device_id,
Elf32_Word sh_entsize;
} Elf32_Shdr;
+#define EI_MAG0 0 /* e_ident[] indexes */
+#define EI_MAG1 1
+#define EI_MAG2 2
+#define EI_MAG3 3
+#define EI_CLASS 4
+#define EI_DATA 5
+#define EI_VERSION 6
+#define EI_PAD 7
+
+#define ELFMAG0 0x7f /* EI_MAG */
+#define ELFMAG1 'E'
+#define ELFMAG2 'L'
+#define ELFMAG3 'F'
+#define ELFMAG "\177ELF"
+#define SELFMAG 4
+
+#define ELFCLASSNONE 0 /* EI_CLASS */
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+#define ELFCLASSNUM 3
+
#define ELF_START_MMAP 0x80000000
#endif /* _LINUX_ELF_H */
#define ARPHRD_RSRVD 260 /* Notional KISS type */
#define ARPHRD_ADAPT 264
#define ARPHRD_PPP 512
+#define ARPHRD_TUNNEL 768 /* IPIP tunnel */
/* ARP protocol opcodes. */
#define ARPOP_REQUEST 1 /* ARP request */
#define ETH_P_ARP 0x0806 /* Address Resolution packet */
#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */
#define ETH_P_X25 0x0805 /* CCITT X.25 */
+#define ETH_P_ATALK 0x809B /* Appletalk DDP */
#define ETH_P_IPX 0x8137 /* IPX over DIX */
#define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */
#define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */
IPPROTO_IP = 0, /* Dummy protocol for TCP */
IPPROTO_ICMP = 1, /* Internet Control Message Protocol */
IPPROTO_IGMP = 2, /* Internet Gateway Management Protocol */
+ IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */
IPPROTO_TCP = 6, /* Transmission Control Protocol */
IPPROTO_EGP = 8, /* Exterior Gateway Protocol */
IPPROTO_PUP = 12, /* PUP protocol */
* a raw socket for this. Instead we check rights in the calls.
*/
-#define IP_FW_BASE_CTL 64
+#define IP_FW_BASE_CTL 64
-#define IP_FW_ADD_BLK (IP_FW_BASE_CTL)
-#define IP_FW_ADD_FWD (IP_FW_BASE_CTL+1)
-#define IP_FW_CHK_BLK (IP_FW_BASE_CTL+2)
-#define IP_FW_CHK_FWD (IP_FW_BASE_CTL+3)
-#define IP_FW_DEL_BLK (IP_FW_BASE_CTL+4)
-#define IP_FW_DEL_FWD (IP_FW_BASE_CTL+5)
-#define IP_FW_FLUSH (IP_FW_BASE_CTL+6)
-#define IP_FW_POLICY (IP_FW_BASE_CTL+7)
+#define IP_FW_ADD_BLK (IP_FW_BASE_CTL)
+#define IP_FW_ADD_FWD (IP_FW_BASE_CTL+1)
+#define IP_FW_CHK_BLK (IP_FW_BASE_CTL+2)
+#define IP_FW_CHK_FWD (IP_FW_BASE_CTL+3)
+#define IP_FW_DEL_BLK (IP_FW_BASE_CTL+4)
+#define IP_FW_DEL_FWD (IP_FW_BASE_CTL+5)
+#define IP_FW_FLUSH_BLK (IP_FW_BASE_CTL+6)
+#define IP_FW_FLUSH_FWD (IP_FW_BASE_CTL+7)
+#define IP_FW_ZERO_BLK (IP_FW_BASE_CTL+8)
+#define IP_FW_ZERO_FWD (IP_FW_BASE_CTL+9)
+#define IP_FW_POLICY_BLK (IP_FW_BASE_CTL+10)
+#define IP_FW_POLICY_FWD (IP_FW_BASE_CTL+11)
-#define IP_ACCT_ADD (IP_FW_BASE_CTL+10)
-#define IP_ACCT_DEL (IP_FW_BASE_CTL+11)
-#define IP_ACCT_FLUSH (IP_FW_BASE_CTL+12)
-#define IP_ACCT_ZERO (IP_FW_BASE_CTL+13)
+#define IP_ACCT_ADD (IP_FW_BASE_CTL+16)
+#define IP_ACCT_DEL (IP_FW_BASE_CTL+17)
+#define IP_ACCT_FLUSH (IP_FW_BASE_CTL+18)
+#define IP_ACCT_ZERO (IP_FW_BASE_CTL+19)
/*
#ifdef CONFIG_IP_FIREWALL
extern struct ip_fw *ip_fw_blk_chain;
extern struct ip_fw *ip_fw_fwd_chain;
-extern int ip_fw_policy;
-extern int ip_fw_chk(struct iphdr *, struct ip_fw *);
+extern int ip_fw_blk_policy;
+extern int ip_fw_fwd_policy;
+extern int ip_fw_chk(struct iphdr *, struct ip_fw *, int);
extern int ip_fw_ctl(int, void *, int);
#endif
#ifdef CONFIG_IP_ACCT
extern struct ip_fw *ip_acct_chain;
-extern void ip_acct_cnt(struct iphdr *, struct ip_fw *, int);
+extern void ip_acct_cnt(struct iphdr *, struct ip_fw *);
extern int ip_acct_ctl(int, void *, int);
#endif
#endif /* KERNEL */
#define _MC146818RTC_H
#include <asm/io.h>
+#ifndef RTC_PORT
#define RTC_PORT(x) (0x70 + (x))
#define RTC_ADDR(x) (0x80 | (x))
#define RTC_ALWAYS_BCD 1
-
-/*
- * The Alpha Jensen hardware for some rather strange reason puts
- * the RTC clock at 0x170 instead of 0x70. Probably due to some
- * misguided idea about using 0x70 for NMI stuff.
- */
-#ifdef __alpha__
-#undef RTC_PORT
-#undef RTC_ADDR
-#undef RTC_ALWAYS_BCD
-#define RTC_PORT(x) (0x170+(x))
-#define RTC_ADDR(x) (x)
-#define RTC_ALWAYS_BCD 0
#endif
#define CMOS_READ(addr) ({ \
#include <asm/page.h>
+#ifdef __KERNEL__
+
#define VERIFY_READ 0
#define VERIFY_WRITE 1
};
/* end of planning stage */
-#ifdef __KERNEL__
-
/*
* Free area management
*/
unsigned long rmem_start; /* shmem "recv" start */
unsigned long mem_end; /* sahared mem end */
unsigned long mem_start; /* shared mem start */
- unsigned short base_addr; /* device I/O address */
+ unsigned long base_addr; /* device I/O address */
unsigned char irq; /* device IRQ number */
/* Low-level status flags. */
{0x0 ,0x0 ,0x0 } \
}
+#include <linux/bios32.h>
-/* PCI BIOS */
-
-extern int pcibios_present (void);
-
-#define PCIBIOS_SUCCESSFUL 0x00
-#define PCIBIOS_FUNC_NOT_SUPPORTED 0x81
-#define PCIBIOS_BAD_VENDOR_ID 0x83
-#define PCIBIOS_DEVICE_NOT_FOUND 0x86
-#define PCIBIOS_BAD_REGISTER_NUMBER 0x87
-
-/*
- * The PCIBIOS calls all bit-field the device_function variable such that
- * the bit fielding matches that of the bl register used in the actual
- * calls.
- */
-
-extern int pcibios_find_class (unsigned long class_code, unsigned short index,
- unsigned char *bus, unsigned char *device_fn);
-extern int pcibios_find_device (unsigned short vendor, unsigned short device_id,
- unsigned short index, unsigned char *bus, unsigned char *device_fn);
-extern int pcibios_read_config_byte (unsigned char bus,
- unsigned char device_fn, unsigned char where, unsigned char *value);
-extern int pcibios_read_config_word (unsigned char bus,
- unsigned char device_fn, unsigned char where, unsigned short *value);
-extern int pcibios_read_config_dword (unsigned char bus,
- unsigned char device_fn, unsigned char where, unsigned long *value);
-extern char *pcibios_strerror (int error);
-extern int pcibios_write_config_byte (unsigned char bus,
- unsigned char device_fn, unsigned char where, unsigned char value);
-extern int pcibios_write_config_word (unsigned char bus,
- unsigned char device_fn, unsigned char where, unsigned short value);
-extern pcibios_write_config_dword (unsigned char bus,
- unsigned char device_fn, unsigned char where, unsigned long value);
#endif /* ndef PCI_H */
#endif
#ifdef CONFIG_IP_FIREWALL
PROC_NET_IPFWFWD,
- PROC_NET_IPBLFWD,
+ PROC_NET_IPFWFWD0,
+ PROC_NET_IPFWBLK,
+ PROC_NET_IPFWBLK0,
#endif
#ifdef CONFIG_IP_ACCT
PROC_NET_IPACCT,
+ PROC_NET_IPACCT0,
#endif
#if defined(CONFIG_WAVELAN)
PROC_NET_WAVELAN,
struct tty_struct *tty; /* NULL if no tty */
/* ipc stuff */
struct sem_undo *semundo;
+ struct sem_queue *semsleeping;
/* ldt for this task - used by Wine. If NULL, default_ldt is used */
struct desc_struct *ldt;
/* tss for this task */
/* math */ 0, \
/* comm */ "swapper", \
/* fs info */ 0,NULL, \
-/* ipc */ NULL, \
+/* ipc */ NULL, NULL, \
/* ldt */ NULL, \
/* tss */ INIT_TSS, \
/* fs */ { INIT_FS }, \
#include <linux/ipc.h>
/* semop flags */
-#define SEM_UNDO 010000 /* undo the operation on exit */
+#define SEM_UNDO 0x1000 /* undo the operation on exit */
/* semctl Command Definitions. */
#define GETPID 11 /* get sempid */
/* One semid data structure for each set of semaphores in the system. */
struct semid_ds {
- struct ipc_perm sem_perm; /* permissions .. see ipc.h */
- time_t sem_otime; /* last semop time */
- time_t sem_ctime; /* last change time */
- struct sem *sem_base; /* ptr to first semaphore in array */
- struct wait_queue *eventn;
- struct wait_queue *eventz;
- struct sem_undo *undo; /* undo requests on this array */
- ushort sem_nsems; /* no. of semaphores in array */
+ struct ipc_perm sem_perm; /* permissions .. see ipc.h */
+ time_t sem_otime; /* last semop time */
+ time_t sem_ctime; /* last change time */
+ struct sem *sem_base; /* ptr to first semaphore in array */
+ struct sem_queue *sem_pending; /* pending operations to be processed */
+ struct sem_queue **sem_pending_last; /* last pending operation */
+ struct sem_undo *undo; /* undo requests on this array */
+ ushort sem_nsems; /* no. of semaphores in array */
};
/* semop system calls takes an array of these. */
};
struct seminfo {
- int semmap;
- int semmni;
- int semmns;
- int semmnu;
- int semmsl;
- int semopm;
- int semume;
- int semusz;
- int semvmx;
- int semaem;
+ int semmap;
+ int semmni;
+ int semmns;
+ int semmnu;
+ int semmsl;
+ int semopm;
+ int semume;
+ int semusz;
+ int semvmx;
+ int semaem;
};
#define SEMMNI 128 /* ? max # of semaphore identifiers */
#define SEMMNU SEMMNS /* num of undo structures system wide */
#define SEMAEM (SEMVMX >> 1) /* adjust on exit max value */
#define SEMMAP SEMMNS /* # of entries in semaphore map */
-#define SEMUSZ 20 /* sizeof struct sem_undo */
+#define SEMUSZ 20 /* sizeof struct sem_undo */
#ifdef __KERNEL__
/* One semaphore structure for each semaphore in the system. */
struct sem {
+ short semval; /* current value */
short sempid; /* pid of last operation */
- ushort semval; /* current value */
- ushort semncnt; /* num procs awaiting increase in semval */
- ushort semzcnt; /* num procs awaiting semval = 0 */
};
/* ipcs ctl cmds */
-#define SEM_STAT 18
+#define SEM_STAT 18
#define SEM_INFO 19
-/* per process undo requests */
-/* this gets linked into the task_struct */
+/* One queue for each semaphore set in the system. */
+struct sem_queue {
+ struct sem_queue * next; /* next entry in the queue */
+ struct sem_queue ** prev; /* previous entry in the queue, *(q->prev) == q */
+ struct wait_queue * sleeper; /* sleeping process */
+ struct sem_undo * undo; /* undo structure */
+ int pid; /* process id of requesting process */
+ int status; /* completion status of operation */
+ struct semid_ds * sma; /* semaphore array for operations */
+ struct sembuf * sops; /* array of pending operations */
+ int nsops; /* number of operations */
+};
+
+/* Each task has a list of undo requests. They are executed automatically
+ * when the process exits.
+ */
struct sem_undo {
- struct sem_undo *proc_next;
- struct sem_undo *id_next;
- int semid;
- short semadj; /* semval adjusted by exit */
- ushort sem_num; /* semaphore index in array semid */
-};
+ struct sem_undo * proc_next; /* next entry on this process */
+ struct sem_undo * id_next; /* next entry on this semaphore set */
+ int semid; /* semaphore set identifier */
+ short * semadj; /* array of adjustments, one per semaphore */
+};
#endif /* __KERNEL__ */
#define SOL_IP 0
#define SOL_IPX 256
#define SOL_AX25 257
+#define SOL_ATALK 258
#define SOL_TCP 6
#define SOL_UDP 17
#define NFDBITS __NFDBITS
#ifdef __KERNEL__
+void do_gettimeofday(struct timeval *tv);
#include <asm/bitops.h>
#include <linux/string.h>
#define FD_SETSIZE __FD_SETSIZE
/*
* linux/ipc/sem.c
- * Copyright (C) 1992 Krishna Balasubramanian
+ * Copyright (C) 1992 Krishna Balasubramanian
+ * Copyright (C) 1995 Eric Schenk, Bruno Haible
+ *
+ * IMPLEMENTATION NOTES ON CODE REWRITE (Eric Schenk, January 1995):
+ * This code underwent a massive rewrite in order to solve some problems
+ * with the original code. In particular the original code failed to
+ * wake up processes that were waiting for semval to go to 0 if the
+ * value went to 0 and was then incremented rapidly enough. In solving
+ * this problem I have also modified the implementation so that it
+ * processes pending operations in a FIFO manner, thus give a guarentee
+ * that processes waiting for a lock on the semaphore won't starve
+ * unless another locking process fails to unlock.
+ * In addition the following two changes in behavior have been introduced:
+ * - The original implementation of semop returned the value
+ * last semaphore element examined on success. This does not
+ * match the manual page specifications, and effectively
+ * allows the user to read the semaphore even if they do not
+ * have read permissions. The implementation now returns 0
+ * on success as stated in the manual page.
+ * - There is some confusion over whether the set of undo adjustments
+ * to be peformed at exit should be done in an atomic manner.
+ * That is, if we are attempting to decrement the semval should we queue
+ * up and wait until we can do so legally?
+ * The original implementation attempted to do this.
+ * The current implementation does not do so. This is because I don't
+ * think it is the right thing (TM) to do, and because I couldn't
+ * see a clean way to get the old behavior with the new design.
+ * The POSIX standard and SVID should be consulted to determine
+ * what behavior is mandated.
*/
#include <linux/errno.h>
static void freeary (int id);
static struct semid_ds *semary[SEMMNI];
-static int used_sems = 0, used_semids = 0;
+static int used_sems = 0, used_semids = 0;
static struct wait_queue *sem_lock = NULL;
static int max_semid = 0;
void sem_init (void)
{
int i;
-
+
sem_lock = NULL;
used_sems = used_semids = max_semid = sem_seq = 0;
for (i = 0; i < SEMMNI; i++)
{
int id;
struct semid_ds *sma;
-
+
for (id = 0; id <= max_semid; id++) {
- while ((sma = semary[id]) == IPC_NOID)
+ while ((sma = semary[id]) == IPC_NOID)
interruptible_sleep_on (&sem_lock);
if (sma == IPC_UNUSED)
continue;
return -EINVAL;
if (used_sems + nsems > SEMMNS)
return -ENOSPC;
- for (id = 0; id < SEMMNI; id++)
+ for (id = 0; id < SEMMNI; id++)
if (semary[id] == IPC_UNUSED) {
semary[id] = (struct semid_ds *) IPC_NOID;
goto found;
ipcp->cuid = ipcp->uid = current->euid;
ipcp->gid = ipcp->cgid = current->egid;
sma->sem_perm.seq = sem_seq;
- sma->eventn = sma->eventz = NULL;
+ /* sma->sem_pending = NULL; */
+ sma->sem_pending_last = &sma->sem_pending;
+ /* sma->undo = NULL; */
sma->sem_nsems = nsems;
sma->sem_ctime = CURRENT_TIME;
- if (id > max_semid)
+ if (id > max_semid)
max_semid = id;
used_semids++;
semary[id] = sma;
{
int id;
struct semid_ds *sma;
-
- if (nsems < 0 || nsems > SEMMSL)
+
+ if (nsems < 0 || nsems > SEMMSL)
return -EINVAL;
- if (key == IPC_PRIVATE)
+ if (key == IPC_PRIVATE)
return newary(key, nsems, semflg);
if ((id = findkey (key)) == -1) { /* key not used */
if (!(semflg & IPC_CREAT))
if (ipcperms(&sma->sem_perm, semflg))
return -EACCES;
return (unsigned int) sma->sem_perm.seq * SEMMNI + id;
-}
+}
+
+/* Manage the doubly linked list sma->sem_pending as a FIFO:
+ * insert new queue elements at the tail sma->sem_pending_last.
+ */
+static inline void insert_into_queue (struct semid_ds * sma, struct sem_queue * q)
+{
+ *(q->prev = sma->sem_pending_last) = q;
+ *(sma->sem_pending_last = &q->next) = NULL;
+}
+static inline void remove_from_queue (struct semid_ds * sma, struct sem_queue * q)
+{
+ *(q->prev) = q->next;
+ if (q->next)
+ q->next->prev = q->prev;
+ else /* sma->sem_pending_last == &q->next */
+ sma->sem_pending_last = q->prev;
+ q->prev = NULL; /* mark as removed */
+}
+
+/* Determine whether a sequence of semaphore operations would succeed
+ * all at once. Return 0 if yes, 1 if need to sleep, else return error code.
+ */
+static int try_semop (struct semid_ds * sma, struct sembuf * sops, int nsops)
+{
+ int result = 0;
+ int i = 0;
+
+ while (i < nsops) {
+ struct sembuf * sop = &sops[i];
+ struct sem * curr = &sma->sem_base[sop->sem_num];
+ if (sop->sem_op + curr->semval > SEMVMX) {
+ result = -ERANGE;
+ break;
+ }
+ if (!sop->sem_op && curr->semval) {
+ if (sop->sem_flg & IPC_NOWAIT)
+ result = -EAGAIN;
+ else
+ result = 1;
+ break;
+ }
+ i++;
+ curr->semval += sop->sem_op;
+ if (curr->semval < 0) {
+ if (sop->sem_flg & IPC_NOWAIT)
+ result = -EAGAIN;
+ else
+ result = 1;
+ break;
+ }
+ }
+ while (--i >= 0) {
+ struct sembuf * sop = &sops[i];
+ struct sem * curr = &sma->sem_base[sop->sem_num];
+ curr->semval -= sop->sem_op;
+ }
+ return result;
+}
+
+/* Actually perform a sequence of semaphore operations. Atomically. */
+/* This assumes that try_semop() already returned 0. */
+static int do_semop (struct semid_ds * sma, struct sembuf * sops, int nsops,
+ struct sem_undo * un, int pid)
+{
+ int i;
+
+ for (i = 0; i < nsops; i++) {
+ struct sembuf * sop = &sops[i];
+ struct sem * curr = &sma->sem_base[sop->sem_num];
+ if (sop->sem_op + curr->semval > SEMVMX) {
+ printk("do_semop: race\n");
+ break;
+ }
+ if (!sop->sem_op) {
+ if (curr->semval) {
+ printk("do_semop: race\n");
+ break;
+ }
+ } else {
+ curr->semval += sop->sem_op;
+ if (curr->semval < 0) {
+ printk("do_semop: race\n");
+ break;
+ }
+ if (sop->sem_flg & SEM_UNDO)
+ un->semadj[sop->sem_num] -= sop->sem_op;
+ }
+ curr->sempid = pid;
+ }
+ sma->sem_otime = CURRENT_TIME;
+
+ /* Previous implementation returned the last semaphore's semval.
+ * This is wrong because we may not have checked read permission,
+ * only write permission.
+ */
+ return 0;
+}
+
+/* Go through the pending queue for the indicated semaphore
+ * looking for tasks that can be completed. Keep cycling through
+ * the queue until a pass is made in which no process is woken up.
+ */
+static void update_queue (struct semid_ds * sma)
+{
+ int wokeup, error;
+ struct sem_queue * q;
+
+ do {
+ wokeup = 0;
+ for (q = sma->sem_pending; q; q = q->next) {
+ error = try_semop(sma, q->sops, q->nsops);
+ /* Does q->sleeper still need to sleep? */
+ if (error > 0)
+ continue;
+ /* Perform the operations the sleeper was waiting for */
+ if (!error)
+ error = do_semop(sma, q->sops, q->nsops, q->undo, q->pid);
+ q->status = error;
+ /* Remove it from the queue */
+ remove_from_queue(sma,q);
+ /* Wake it up */
+ wake_up_interruptible(&q->sleeper); /* doesn't sleep! */
+ wokeup++;
+ }
+ } while (wokeup);
+}
+
+/* The following counts are associated to each semaphore:
+ * semncnt number of tasks waiting on semval being nonzero
+ * semzcnt number of tasks waiting on semval being zero
+ * This model assumes that a task waits on exactly one semaphore.
+ * Since semaphore operations are to be performed atomically, tasks actually
+ * wait on a whole sequence of semaphores simultaneously.
+ * The counts we return here are a rough approximation, but still
+ * warrant that semncnt+semzcnt>0 if the task is on the pending queue.
+ */
+static int count_semncnt (struct semid_ds * sma, ushort semnum)
+{
+ int semncnt;
+ struct sem_queue * q;
+
+ semncnt = 0;
+ for (q = sma->sem_pending; q; q = q->next) {
+ struct sembuf * sops = q->sops;
+ int nsops = q->nsops;
+ int i;
+ for (i = 0; i < nsops; i++)
+ if (sops[i].sem_num == semnum
+ && (sops[i].sem_op < 0)
+ && !(sops[i].sem_flg & IPC_NOWAIT))
+ semncnt++;
+ }
+ return semncnt;
+}
+static int count_semzcnt (struct semid_ds * sma, ushort semnum)
+{
+ int semzcnt;
+ struct sem_queue * q;
+
+ semzcnt = 0;
+ for (q = sma->sem_pending; q; q = q->next) {
+ struct sembuf * sops = q->sops;
+ int nsops = q->nsops;
+ int i;
+ for (i = 0; i < nsops; i++)
+ if (sops[i].sem_num == semnum
+ && (sops[i].sem_op == 0)
+ && !(sops[i].sem_flg & IPC_NOWAIT))
+ semzcnt++;
+ }
+ return semzcnt;
+}
+/* Free a semaphore set. */
static void freeary (int id)
{
struct semid_ds *sma = semary[id];
struct sem_undo *un;
+ struct sem_queue *q;
+ /* Invalidate this semaphore set */
sma->sem_perm.seq++;
sem_seq = (sem_seq+1) % ((unsigned)(1<<31)/SEMMNI); /* increment, but avoid overflow */
used_sems -= sma->sem_nsems;
while (max_semid && (semary[--max_semid] == IPC_UNUSED));
semary[id] = (struct semid_ds *) IPC_UNUSED;
used_semids--;
+
+ /* Invalidate the existing undo structures for this semaphore set.
+ * (They will be freed without any further action in sem_exit().)
+ */
for (un = sma->undo; un; un = un->id_next)
- un->semadj = 0;
- while (sma->eventz || sma->eventn) {
- if (sma->eventz)
- wake_up (&sma->eventz);
- if (sma->eventn)
- wake_up (&sma->eventn);
- schedule();
+ un->semid = -1;
+
+ /* Wake up all pending processes and let them fail with EIDRM. */
+ for (q = sma->sem_pending; q; q = q->next) {
+ q->status = -EIDRM;
+ q->prev = NULL;
+ wake_up_interruptible(&q->sleeper); /* doesn't sleep! */
}
+
kfree(sma);
- return;
}
int sys_semctl (int semid, int semnum, int cmd, union semun arg)
return -EINVAL;
switch (cmd) {
- case IPC_INFO:
- case SEM_INFO:
+ case IPC_INFO:
+ case SEM_INFO:
{
struct seminfo seminfo, *tmp = arg.__buf;
seminfo.semmni = SEMMNI;
seminfo.semmsl = SEMMSL;
seminfo.semopm = SEMOPM;
seminfo.semvmx = SEMVMX;
- seminfo.semmnu = SEMMNU;
- seminfo.semmap = SEMMAP;
+ seminfo.semmnu = SEMMNU;
+ seminfo.semmap = SEMMAP;
seminfo.semume = SEMUME;
seminfo.semusz = SEMUSZ;
seminfo.semaem = SEMAEM;
if (ipcperms (ipcp, S_IRUGO))
return -EACCES;
switch (cmd) {
- case GETVAL : return curr->semval;
+ case GETVAL : return curr->semval;
case GETPID : return curr->sempid;
- case GETNCNT: return curr->semncnt;
- case GETZCNT: return curr->semzcnt;
+ case GETNCNT: return count_semncnt(sma,semnum);
+ case GETZCNT: return count_semzcnt(sma,semnum);
case GETALL:
array = arg.array;
i = verify_area (VERIFY_WRITE, array, nsems*sizeof(ushort));
break;
case SETVAL:
val = arg.val;
- if (val > SEMVMX || val < 0)
+ if (val > SEMVMX || val < 0)
return -ERANGE;
break;
case IPC_RMID:
- if (suser() || current->euid == ipcp->cuid ||
- current->euid == ipcp->uid) {
- freeary (id);
+ if (suser() || current->euid == ipcp->cuid || current->euid == ipcp->uid) {
+ freeary (id);
return 0;
}
return -EPERM;
memcpy_fromfs (&tbuf, buf, sizeof (*buf));
break;
}
-
+
if (semary[id] == IPC_UNUSED || semary[id] == IPC_NOID)
return -EIDRM;
if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
return -EIDRM;
-
+
switch (cmd) {
case GETALL:
if (ipcperms (ipcp, S_IRUGO))
if (ipcperms (ipcp, S_IWUGO))
return -EACCES;
for (un = sma->undo; un; un = un->id_next)
- if (semnum == un->sem_num)
- un->semadj = 0;
- sma->sem_ctime = CURRENT_TIME;
+ un->semadj[semnum] = 0;
curr->semval = val;
- if (sma->eventn)
- wake_up (&sma->eventn);
- if (sma->eventz)
- wake_up (&sma->eventz);
+ sma->sem_ctime = CURRENT_TIME;
+ /* maybe some queued-up processes were waiting for this */
+ update_queue(sma);
break;
case IPC_SET:
- if (suser() || current->euid == ipcp->cuid ||
- current->euid == ipcp->uid) {
+ if (suser() || current->euid == ipcp->cuid || current->euid == ipcp->uid) {
ipcp->uid = tbuf.sem_perm.uid;
ipcp->gid = tbuf.sem_perm.gid;
ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
case SETALL:
if (ipcperms (ipcp, S_IWUGO))
return -EACCES;
- for (i = 0; i < nsems; i++)
+ for (i = 0; i < nsems; i++)
sma->sem_base[i].semval = sem_io[i];
for (un = sma->undo; un; un = un->id_next)
- un->semadj = 0;
- if (sma->eventn)
- wake_up (&sma->eventn);
- if (sma->eventz)
- wake_up (&sma->eventz);
+ for (i = 0; i < nsems; i++)
+ un->semadj[i] = 0;
sma->sem_ctime = CURRENT_TIME;
+ /* maybe some queued-up processes were waiting for this */
+ update_queue(sma);
break;
default:
return -EINVAL;
int sys_semop (int semid, struct sembuf *tsops, unsigned nsops)
{
- int i, id;
+ int i, id, size, error;
struct semid_ds *sma;
- struct sem *curr = NULL;
struct sembuf sops[SEMOPM], *sop;
struct sem_undo *un;
- int undos = 0, alter = 0, semncnt = 0, semzcnt = 0;
-
+ int undos = 0, alter = 0;
+
if (nsops < 1 || semid < 0)
return -EINVAL;
if (nsops > SEMOPM)
return -E2BIG;
- if (!tsops)
+ if (!tsops)
return -EFAULT;
if ((i = verify_area (VERIFY_READ, tsops, nsops * sizeof(*tsops))))
return i;
- memcpy_fromfs (sops, tsops, nsops * sizeof(*tsops));
+ memcpy_fromfs (sops, tsops, nsops * sizeof(*tsops));
id = (unsigned int) semid % SEMMNI;
if ((sma = semary[id]) == IPC_UNUSED || sma == IPC_NOID)
return -EINVAL;
- for (i = 0; i < nsops; i++) {
+ if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
+ return -EIDRM;
+ for (i = 0; i < nsops; i++) {
sop = &sops[i];
- if (sop->sem_num > sma->sem_nsems)
+ if (sop->sem_num >= sma->sem_nsems)
return -EFBIG;
if (sop->sem_flg & SEM_UNDO)
undos++;
- if (sop->sem_op) {
+ if (sop->sem_op)
alter++;
- if (sop->sem_op > 0)
- semncnt ++;
- }
}
if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
return -EACCES;
- /*
- * ensure every sop with undo gets an undo structure
- */
+ error = try_semop(sma, sops, nsops);
+ if (error < 0)
+ return error;
if (undos) {
- for (i = 0; i < nsops; i++) {
- if (!(sops[i].sem_flg & SEM_UNDO))
- continue;
- for (un = current->semundo; un; un = un->proc_next)
- if ((un->semid == semid) &&
- (un->sem_num == sops[i].sem_num))
- break;
- if (un)
- continue;
- un = (struct sem_undo *)
- kmalloc (sizeof(*un), GFP_ATOMIC);
+ /* Make sure we have an undo structure
+ * for this process and this semaphore set.
+ */
+ for (un = current->semundo; un; un = un->proc_next)
+ if (un->semid == semid)
+ break;
+ if (!un) {
+ size = sizeof(struct sem_undo) + sizeof(short)*sma->sem_nsems;
+ un = (struct sem_undo *) kmalloc(size, GFP_ATOMIC);
if (!un)
- return -ENOMEM; /* freed on exit */
+ return -ENOMEM;
+ memset(un, 0, size);
+ un->semadj = (short *) &un[1];
un->semid = semid;
- un->semadj = 0;
- un->sem_num = sops[i].sem_num;
un->proc_next = current->semundo;
current->semundo = un;
un->id_next = sma->undo;
sma->undo = un;
}
- }
-
- slept:
- if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
- return -EIDRM;
- for (i = 0; i < nsops; i++) {
- sop = &sops[i];
- curr = &sma->sem_base[sop->sem_num];
- if (sop->sem_op + curr->semval > SEMVMX)
- return -ERANGE;
- if (!sop->sem_op && curr->semval) {
- if (sop->sem_flg & IPC_NOWAIT)
- return -EAGAIN;
- if (current->signal & ~current->blocked)
- return -EINTR;
- curr->semzcnt++;
- interruptible_sleep_on (&sma->eventz);
- curr->semzcnt--;
- goto slept;
- }
- if ((sop->sem_op + curr->semval < 0) ) {
- if (sop->sem_flg & IPC_NOWAIT)
- return -EAGAIN;
- if (current->signal & ~current->blocked)
- return -EINTR;
- curr->semncnt++;
- interruptible_sleep_on (&sma->eventn);
- curr->semncnt--;
- goto slept;
- }
- }
-
- for (i = 0; i < nsops; i++) {
- sop = &sops[i];
- curr = &sma->sem_base[sop->sem_num];
- curr->sempid = current->pid;
- if (!(curr->semval += sop->sem_op))
- semzcnt++;
- if (!(sop->sem_flg & SEM_UNDO))
- continue;
- for (un = current->semundo; un; un = un->proc_next)
- if ((un->semid == semid) &&
- (un->sem_num == sop->sem_num))
- break;
- if (!un) {
- printk ("semop : no undo for op %d\n", i);
- continue;
+ } else
+ un = NULL;
+ if (error == 0) {
+ /* the operations go through immediately */
+ error = do_semop(sma, sops, nsops, un, current->pid);
+ /* maybe some queued-up processes were waiting for this */
+ update_queue(sma);
+ return error;
+ } else {
+ /* We need to sleep on this operation, so we put the current
+ * task into the pending queue and go to sleep.
+ */
+ struct sem_queue queue;
+
+ queue.sma = sma;
+ queue.sops = sops;
+ queue.nsops = nsops;
+ queue.undo = un;
+ queue.pid = current->pid;
+ queue.status = 0;
+ insert_into_queue(sma,&queue);
+ queue.sleeper = NULL;
+ current->semsleeping = &queue;
+ interruptible_sleep_on(&queue.sleeper);
+ current->semsleeping = NULL;
+ /* When we wake up, either the operation is finished,
+ * or some kind of error happened.
+ */
+ if (!queue.prev) {
+ /* operation is finished, update_queue() removed us */
+ return queue.status;
+ } else {
+ remove_from_queue(sma,&queue);
+ return -EINTR;
}
- un->semadj -= sop->sem_op;
}
- sma->sem_otime = CURRENT_TIME;
- if (semncnt && sma->eventn)
- wake_up(&sma->eventn);
- if (semzcnt && sma->eventz)
- wake_up(&sma->eventz);
- return curr->semval;
}
/*
* add semadj values to semaphores, free undo structures.
* undo structures are not freed when semaphore arrays are destroyed
* so some of them may be out of date.
+ * IMPLEMENTATION NOTE: There is some confusion over whether the
+ * set of adjustments that needs to be done should be done in an atomic
+ * manner or not. That is, if we are attempting to decrement the semval
+ * should we queue up and wait until we can do so legally?
+ * The original implementation attempted to do this (queue and wait).
+ * The current implementation does not do so. The POSIX standard
+ * and SVID should be consulted to determine what behavior is mandated.
*/
void sem_exit (void)
{
+ struct sem_queue *q;
struct sem_undo *u, *un = NULL, **up, **unp;
struct semid_ds *sma;
- struct sem *sem = NULL;
-
+ int nsems, i;
+
+ /* If the current process was sleeping for a semaphore,
+ * remove it from the queue.
+ */
+ if ((q = current->semsleeping)) {
+ if (q->prev)
+ remove_from_queue(q->sma,q);
+ current->semsleeping = NULL;
+ }
+
for (up = ¤t->semundo; (u = *up); *up = u->proc_next, kfree(u)) {
+ if (u->semid == -1)
+ continue;
sma = semary[(unsigned int) u->semid % SEMMNI];
- if (sma == IPC_UNUSED || sma == IPC_NOID)
+ if (sma == IPC_UNUSED || sma == IPC_NOID)
continue;
if (sma->sem_perm.seq != (unsigned int) u->semid / SEMMNI)
continue;
+ /* remove u from the sma->undo list */
for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {
- if (u == un)
+ if (u == un)
goto found;
}
printk ("sem_exit undo list error id=%d\n", u->semid);
break;
found:
*unp = un->id_next;
- if (!un->semadj)
- continue;
- while (1) {
- if (sma->sem_perm.seq != (unsigned int) un->semid / SEMMNI)
- break;
- sem = &sma->sem_base[un->sem_num];
- if (sem->semval + un->semadj >= 0) {
- sem->semval += un->semadj;
- sem->sempid = current->pid;
- sma->sem_otime = CURRENT_TIME;
- if (un->semadj > 0 && sma->eventn)
- wake_up (&sma->eventn);
- if (!sem->semval && sma->eventz)
- wake_up (&sma->eventz);
- break;
- }
- if (current->signal & ~current->blocked)
- break;
- sem->semncnt++;
- interruptible_sleep_on (&sma->eventn);
- sem->semncnt--;
+ /* perform adjustments registered in u */
+ nsems = sma->sem_nsems;
+ for (i = 0; i < nsems; i++) {
+ struct sem * sem = &sma->sem_base[i];
+ sem->semval += u->semadj[i];
+ if (sem->semval < 0)
+ sem->semval = 0; /* shouldn't happen */
+ sem->sempid = current->pid;
}
+ sma->sem_otime = CURRENT_TIME;
+ /* maybe some queued-up processes were waiting for this */
+ update_queue(sma);
}
current->semundo = NULL;
- return;
}
intr_count = 0;
}
fake_volatile:
- if (current->semundo)
- sem_exit();
+ sem_exit();
exit_mmap(current);
free_page_tables(current);
exit_files();
#ifdef CONFIG_INET
#include <linux/net.h>
#include <linux/netdevice.h>
+#include <linux/ip.h>
+#include "../net/inet/protocol.h"
#endif
#ifdef CONFIG_PCI
#include <linux/pci.h>
/* dma handling */
X(request_dma),
X(free_dma),
+#ifdef HAVE_DISABLE_HLT
+ X(disable_hlt),
+ X(enable_hlt),
+#endif
/* IO port handling */
X(check_region),
/* Miscellaneous access points */
X(si_meminfo),
-
+#ifdef CONFIG_NET
/* socket layer registration */
X(sock_register),
X(sock_unregister),
+ /* Internet layer registration */
+#ifdef CONFIG_INET
+ X(inet_add_protocol),
+ X(inet_del_protocol),
+#endif
+ /* Device callback registration */
+ X(register_netdevice_notifier),
+ X(unregister_netdevice_notifier),
+#endif
#ifdef CONFIG_FTAPE
/* The next labels are needed for ftape driver. */
X(dev_ioctl),
X(dev_queue_xmit),
X(dev_base),
+ X(dev_close),
+ X(n_tty_ioctl),
+ X(tty_register_ldisc),
+ X(kill_fasync),
+ X(tty_hung_up_p),
#endif
#ifdef CONFIG_SCSI
/* Supports loadable scsi drivers */
X(scsi_malloc),
X(scsi_register),
X(scsi_unregister),
+ X(scsicam_bios_param),
#endif
/* Added to make file system as module */
X(set_writetime),
bh_base[IMMEDIATE_BH].routine = immediate_bh;
if (request_irq(TIMER_IRQ, do_timer, 0, "timer") != 0)
panic("Could not allocate timer IRQ!");
+ enable_bh(TIMER_BH);
+ enable_bh(TQUEUE_BH);
+ enable_bh(IMMEDIATE_BH);
}
unsigned long intr_count = 0;
unsigned long bh_active = 0;
-unsigned long bh_mask = ~0UL;
+unsigned long bh_mask = 0;
struct bh_struct bh_base[32];
save_flags(flags);
cli();
-#if defined (__i386__) || defined (__mips__)
*tv = xtime;
+#if defined (__i386__) || defined (__mips__)
tv->tv_usec += do_gettimeoffset();
if (tv->tv_usec >= 1000000) {
tv->tv_usec -= 1000000;
tv->tv_sec++;
}
- sti();
-#else /* !defined (__i386__) && !defined (__mips__) */
- cli();
- *tv = xtime;
- sti();
#endif /* !defined (__i386__) && !defined (__mips__) */
restore_flags(flags);
}
* sizes[order].firstfree used to be NULL, otherwise we wouldn't be
* here, but you never know....
*/
- page->next = sizes[order].firstfree;
- if (dma_flag)
+ if (dma_flag) {
+ page->next = sizes[order].dmafree;
sizes[order].dmafree = page;
- else
+ } else {
+ page->next = sizes[order].firstfree;
sizes[order].firstfree = page;
+ }
restore_flags(flags);
}
*/
#ifdef CONFIG_IP_FIREWALL
- if(!ip_fw_chk(skb->h.iph, ip_fw_fwd_chain))
+ if(!ip_fw_chk(skb->h.iph, ip_fw_fwd_chain, ip_fw_fwd_policy))
{
return;
}
* Count mapping we shortcut
*/
- ip_acct_cnt(iph,ip_acct_chain,1);
+ ip_acct_cnt(iph,ip_acct_chain);
#endif
/*
#ifdef CONFIG_IP_FIREWALL
- if(!LOOPBACK(iph->daddr) && !ip_fw_chk(iph,ip_fw_blk_chain))
+ if(!LOOPBACK(iph->daddr) && !ip_fw_chk(iph,ip_fw_blk_chain,
+ ip_fw_blk_policy))
{
kfree_skb(skb, FREE_WRITE);
return 0;
*/
#ifdef CONFIG_IP_ACCT
- ip_acct_cnt(iph,ip_acct_chain,1);
+ ip_acct_cnt(iph,ip_acct_chain);
#endif
/*
ip_statistics.IpOutRequests++;
#ifdef CONFIG_IP_ACCT
- ip_acct_cnt(iph,ip_acct_chain,1);
+ ip_acct_cnt(iph,ip_acct_chain);
#endif
#ifdef CONFIG_IP_MULTICAST
case IP_FW_DEL_FWD:
case IP_FW_CHK_BLK:
case IP_FW_CHK_FWD:
- case IP_FW_FLUSH:
- case IP_FW_POLICY:
+ case IP_FW_FLUSH_BLK:
+ case IP_FW_FLUSH_FWD:
+ case IP_FW_ZERO_BLK:
+ case IP_FW_ZERO_FWD:
+ case IP_FW_POLICY_BLK:
+ case IP_FW_POLICY_FWD:
if(!suser())
return -EPERM;
if(optlen>sizeof(tmp_fw) || optlen<1)
#ifdef CONFIG_IP_FIREWALL
struct ip_fw *ip_fw_fwd_chain;
struct ip_fw *ip_fw_blk_chain;
-static int ip_fw_policy=1;
+int ip_fw_blk_policy=1;
+int ip_fw_fwd_policy=1;
#endif
#ifdef CONFIG_IP_ACCT
struct ip_fw *ip_acct_chain;
#endif
+#define IP_INFO_BLK 0
+#define IP_INFO_FWD 1
+#define IP_INFO_ACCT 2
+
extern inline void print_ip(unsigned long xaddr)
{
#ifdef CONFIG_IP_FIREWALL
-int ip_fw_chk(struct iphdr *ip, struct ip_fw *chain)
+int ip_fw_chk(struct iphdr *ip, struct ip_fw *chain, int policy)
{
unsigned long src, dst;
char got_proto=0;
unsigned short *portptr=(unsigned short *)&(((u_int *)ip)[ip->ihl]);
if (!chain)
- return(1); /* If no chain , always say Ok to packet */
+ return(policy); /* If no chain, use your policy. */
src = ip->saddr;
dst = ip->daddr;
#ifdef DEBUG_CONFIG_IP_FIREWALL
printf("universal frwl match\n");
#endif
+ f->p_cnt++;
+ f->b_cnt+=ntohs(ip->tot_len);
#ifdef CONFIG_IP_FIREWALL_VERBOSE
if (!(f->flags & IP_FW_F_ACCEPT))
goto bad_packet;
port_match(&f->ports[f->n_src_p],f->n_dst_p,dst_port,
f->flags&IP_FW_F_DRNG)))
{
+ /* We've got a match! */
+ f->p_cnt++;
+ f->b_cnt+=ntohs(ip->tot_len);
#ifdef CONFIG_IP_FIREWALL_VERBOSE
if (!(f->flags & IP_FW_F_ACCEPT))
goto bad_packet;
*/
#ifdef CONFIG_IP_FIREWALL_VERBOSE
- if (!(ip_fw_policy))
+ if (!(policy))
goto bad_packet;
return 1;
#else
- return(ip_fw_policy);
+ return(policy);
#endif
#ifdef CONFIG_IP_FIREWALL_VERBOSE
#ifdef CONFIG_IP_ACCT
-void ip_acct_cnt(struct iphdr *ip,struct ip_fw *chain,int nh_conv)
+void ip_acct_cnt(struct iphdr *ip,struct ip_fw *chain)
{
unsigned long src, dst;
char got_proto=0,rev=0;
f->p_cnt++; /* Rise packet count */
/*
- * Rise byte count, if need to convert from host to network byte order,do it.
+ * Rise byte count, convert from host to network byte order.
*/
- if (nh_conv)
- f->b_cnt+=ntohs(ip->tot_len);
- else
- f->b_cnt+=ip->tot_len;
+ f->b_cnt+=ntohs(ip->tot_len);
}
else
{
{
f->p_cnt++; /* Rise packet count */
/*
- * Rise byte count, if need to convert from host to network byte order,do it.
+ * Rise byte count, convert from host to network byte order.
*/
- if (nh_conv)
- f->b_cnt+=ntohs(ip->tot_len);
- else
- f->b_cnt+=ip->tot_len;
+ f->b_cnt+=ntohs(ip->tot_len);
} /* Ports match */
} /* Proto matches */
} /* ALL/Specific */
#ifdef CONFIG_IP_FIREWALL
int ip_fw_ctl(int stage, void *m, int len)
{
- if ( stage == IP_FW_FLUSH )
+ if ( stage == IP_FW_FLUSH_BLK )
{
free_fw_chain(&ip_fw_blk_chain);
+ return(0);
+ }
+
+ if ( stage == IP_FW_FLUSH_FWD )
+ {
free_fw_chain(&ip_fw_fwd_chain);
return(0);
}
- if ( stage == IP_FW_POLICY )
+ if ( stage == IP_FW_ZERO_BLK )
+ {
+ zero_fw_chain(ip_fw_blk_chain);
+ return(0);
+ }
+
+ if ( stage == IP_FW_ZERO_FWD )
+ {
+ zero_fw_chain(ip_fw_fwd_chain);
+ return(0);
+ }
+
+ if ( stage == IP_FW_POLICY_BLK || stage == IP_FW_POLICY_FWD )
{
int *tmp_policy_ptr;
tmp_policy_ptr=(int *)m;
if ((*tmp_policy_ptr)!=1 && (*tmp_policy_ptr)!=0)
return (EINVAL);
- ip_fw_policy=*tmp_policy_ptr;
+ if ( stage == IP_FW_POLICY_BLK )
+ ip_fw_blk_policy=*tmp_policy_ptr;
+ else
+ ip_fw_fwd_policy=*tmp_policy_ptr;
return 0;
}
- if ( stage == IP_FW_CHK_BLK
- || stage == IP_FW_CHK_FWD )
+ if ( stage == IP_FW_CHK_BLK || stage == IP_FW_CHK_FWD )
{
struct iphdr *ip;
if ( ip_fw_chk(ip,
stage == IP_FW_CHK_BLK ?
- ip_fw_blk_chain : ip_fw_fwd_chain )
+ ip_fw_blk_chain : ip_fw_fwd_chain,
+ stage == IP_FW_CHK_BLK ?
+ ip_fw_blk_policy : ip_fw_fwd_policy )
)
return(0);
else
#if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT)
-static int ip_chain_procinfo(struct ip_fw *chain, char *buffer, char **start, off_t offset, int length)
+static int ip_chain_procinfo(int stage, char *buffer, char **start,
+ off_t offset, int length, int reset)
{
off_t pos=0, begin=0;
struct ip_fw *i;
unsigned long flags;
- int len=0;
-
+ int len;
- len=sprintf(buffer,"Firewall Rules\n");
+
+ switch(stage)
+ {
+#ifdef CONFIG_IP_FIREWALL
+ case IP_INFO_BLK:
+ i = ip_fw_blk_chain;
+ len=sprintf(buffer, "IP firewall block rules, policy = %d\n",
+ ip_fw_blk_policy);
+ break;
+ case IP_INFO_FWD:
+ i = ip_fw_fwd_chain;
+ len=sprintf(buffer, "IP firewall forward rules, policy = %d\n",
+ ip_fw_fwd_policy);
+ break;
+#endif
+#ifdef CONFIG_IP_ACCT
+ case IP_INFO_ACCT:
+ i = ip_acct_chain;
+ len=sprintf(buffer,"IP accounting rules\n");
+ break;
+#endif
+ default:
+ /* this should never be reached, but safety first... */
+ i = NULL;
+ len=0;
+ break;
+ }
+
save_flags(flags);
cli();
- i=chain;
-
while(i!=NULL)
{
len+=sprintf(buffer+len,"%08lX/%08lX->%08lX/%08lX %X ",
len=0;
begin=pos;
}
+ else if(reset)
+ {
+ /* This needs to be done at this specific place! */
+ i->p_cnt=0L;
+ i->b_cnt=0L;
+ }
if(pos>offset+length)
break;
i=i->next;
int ip_acct_procinfo(char *buffer, char **start, off_t offset, int length)
{
- return ip_chain_procinfo(ip_acct_chain, buffer,start,offset,length);
+ return ip_chain_procinfo(IP_INFO_ACCT, buffer,start,offset,length,0);
+}
+
+int ip_acct0_procinfo(char *buffer, char **start, off_t offset, int length)
+{
+ return ip_chain_procinfo(IP_INFO_ACCT, buffer,start,offset,length,1);
}
#endif
int ip_fw_blk_procinfo(char *buffer, char **start, off_t offset, int length)
{
- return ip_chain_procinfo(ip_fw_blk_chain, buffer,start,offset,length);
+ return ip_chain_procinfo(IP_INFO_BLK, buffer,start,offset,length,0);
+}
+
+int ip_fw_blk0_procinfo(char *buffer, char **start, off_t offset, int length)
+{
+ return ip_chain_procinfo(IP_INFO_BLK, buffer,start,offset,length,1);
}
int ip_fw_fwd_procinfo(char *buffer, char **start, off_t offset, int length)
{
- return ip_chain_procinfo(ip_fw_fwd_chain, buffer,start,offset,length);
+ return ip_chain_procinfo(IP_INFO_FWD, buffer,start,offset,length,0);
+}
+
+int ip_fw_fwd0_procinfo(char *buffer, char **start, off_t offset, int length)
+{
+ return ip_chain_procinfo(IP_INFO_FWD, buffer,start,offset,length,1);
}
#endif
memcpy(&tip,rarp_ptr,4);
/*
- * Process entry
+ * Process entry. Use tha for table lookup according to RFC903.
*/
cli();
for (entry = rarp_tables; entry != NULL; entry = entry->next)
- if (!memcmp(entry->ha, sha, rarp->ar_hln))
- break;
+ if (!memcmp(entry->ha, tha, rarp->ar_hln))
+ break;
if (entry != NULL)
{
* Alan Cox : Split IP from generic code
* Alan Cox : New kfree_skbmem()
* Alan Cox : Make SO_DEBUG superuser only.
+ * Alan Cox : Allow anyone to clear SO_DEBUG
+ * (compatibility fix)
*
* To Fix:
*
return(-ENOPROTOOPT);
case SO_DEBUG:
- if(!suser())
+ if(val && !suser())
return(-EPERM);
sk->debug=val?1:0;
return 0;
/*
* Default sequence number picking algorithm.
+ * As close as possible to RFC 793, which
+ * suggests using a 250kHz clock.
+ * Further reading shows this assumes 2MB/s networks.
+ * For 10MB/s ethernet, a 1MHz clock is appropriate.
+ * That's funny, Linux has one built in! Use it!
*/
-extern inline long tcp_init_seq(void)
+extern inline unsigned long tcp_init_seq(void)
{
- return jiffies * SEQ_TICK - seq_offset;
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ return tv.tv_usec+tv.tv_sec*1000000;
}
/*
sk->inuse = 1;
sk->daddr = usin->sin_addr.s_addr;
- sk->write_seq = jiffies * SEQ_TICK - seq_offset;
+ sk->write_seq = tcp_init_seq();
sk->window_seq = sk->write_seq;
sk->rcv_ack_seq = sk->write_seq -1;
sk->err = 0;
*/
bh_base[NET_BH].routine= net_bh;
+ enable_bh(NET_BH);
#endif
}