diff -u --recursive --new-file v1.1.82/linux/CREDITS linux/CREDITS --- v1.1.82/linux/CREDITS Fri Jan 13 16:57:03 1995 +++ linux/CREDITS Wed Jan 18 09:40:45 1995 @@ -32,6 +32,14 @@ S: University of Calgary S: Calgary, Alberta, Canada +N: Ralf Baechle +E: ralf@waldorf-gmbh.de +D: Linux/MIPS port +D: Linux/68k hacker +S: Hauptstrasse 19 +S: 79837 St. Blasien +S: Germany + N: Krishna Balasubramanian E: balasub@cis.ohio-state.edu D: Wrote SYS V IPC (part of standard kernel since 0.99.10) @@ -629,6 +637,13 @@ S: FIN-00330 Helsingfors S: Finland +N: Michael Neuffer +E: neuffer@goofy.zdv.uni-mainz.de +D: developer and maintainer of the eata_dma SCSI driver +S: Zum Schiersteiner Grund 2 +S: 55127 Mainz +S: Germany + N: David C. Niemi E: niemidc@clark.net E: niemidc@slma.com @@ -921,6 +936,15 @@ S: 17 Canterbury Square #101 S: Alexandria, Virginia 22304 S: USA + +N: Niibe Yutaka +E: gniibe@mri.co.jp +D: PLIP driver +D: Asynchronous socket I/O in the NET code +S: Mitsubishi Research Institute, Inc. +S: ARCO Tower 1-8-1 Shimomeguro Meguro-ku +S: Tokyo 153 +S: Japan N: Orest Zborowski E: orestz@eskimo.com diff -u --recursive --new-file v1.1.82/linux/Makefile linux/Makefile --- v1.1.82/linux/Makefile Mon Jan 16 14:18:12 1995 +++ linux/Makefile Wed Jan 18 23:33:09 1995 @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 1 -SUBLEVEL = 82 +SUBLEVEL = 83 ARCH = i386 @@ -165,6 +165,7 @@ echo \#define LINUX_COMPILE_DOMAIN \"`domainname`\"; \ fi >> include/linux/version.h @echo \#define LINUX_COMPILER \"`$(HOSTCC) -v 2>&1 | tail -1`\" >> include/linux/version.h + @echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)` >> include/linux/version.h init/version.o: init/version.c include/linux/version.h $(CC) $(CFLAGS) -DUTS_MACHINE='"$(ARCH)"' -c -o init/version.o init/version.c diff -u --recursive --new-file v1.1.82/linux/arch/alpha/boot/Makefile linux/arch/alpha/boot/Makefile --- v1.1.82/linux/arch/alpha/boot/Makefile Mon Jan 16 14:18:12 1995 +++ linux/arch/alpha/boot/Makefile Wed Jan 18 22:52:36 1995 @@ -21,8 +21,8 @@ OBJECTS = head.o main.o -all: - @echo Be careful.. This works for me, not for you +all: tools/lxboot tools/bootlx vmlinux + @echo run mkfloppy on machine with floppy drive msb: tools/lxboot tools/bootlx vmlinux ( cat tools/lxboot tools/bootlx vmlinux ) > /dev/rz0a diff -u --recursive --new-file v1.1.82/linux/arch/alpha/boot/main.c linux/arch/alpha/boot/main.c --- v1.1.82/linux/arch/alpha/boot/main.c Mon Jan 16 14:18:12 1995 +++ linux/arch/alpha/boot/main.c Wed Jan 18 22:52:36 1995 @@ -75,13 +75,13 @@ #define new_vptb (0xfffffffe00000000UL) void pal_init(void) { - unsigned long i, rev; - unsigned long *L1; + unsigned long i, rev, sum; + unsigned long *L1, *l; struct percpu_struct * percpu; struct pcb_struct * pcb_pa; /* Find the level 1 page table and duplicate it in high memory */ - L1 = (unsigned long *) 0x200802000UL; + L1 = (unsigned long *) 0x200802000UL; /* (1<<33 | 1<<23 | 1<<13) */ L1[1023] = L1[1]; percpu = (struct percpu_struct *) (hwrpb.processor_offset + (unsigned long) &hwrpb), @@ -113,7 +113,15 @@ halt(); } rev = percpu->pal_revision = percpu->palcode_avail[2]; + hwrpb.vptb = new_vptb; + + /* update checksum: */ + sum = 0; + for (l = (unsigned long *) &hwrpb; l < (unsigned long *) &hwrpb.chksum; ++l) + sum += *l; + hwrpb.chksum = sum; + printk("Ok (rev %lx)\n", rev); /* remove the old virtual page-table mapping */ L1[1] = 0; diff -u --recursive --new-file v1.1.82/linux/arch/alpha/config.in linux/arch/alpha/config.in --- v1.1.82/linux/arch/alpha/config.in Mon Jan 16 14:18:12 1995 +++ linux/arch/alpha/config.in Wed Jan 18 22:52:36 1995 @@ -9,6 +9,7 @@ bool 'Normal harddisk support' CONFIG_BLK_DEV_HD n bool 'XT harddisk support' CONFIG_BLK_DEV_XD n bool 'Networking support' CONFIG_NET n +bool 'PCI alpha motherboard' CONFIG_PCI n bool 'System V IPC' CONFIG_SYSVIPC n bool 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF y diff -u --recursive --new-file v1.1.82/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S --- v1.1.82/linux/arch/alpha/kernel/entry.S Mon Jan 16 14:18:12 1995 +++ linux/arch/alpha/kernel/entry.S Wed Jan 18 22:52:36 1995 @@ -87,6 +87,42 @@ rti .end entMM +.align 5 +.globl entArith +.ent entArith +entArith: + SAVE_ALL + bis $30,$30,$19 + lda $27,do_entArith + jsr $26,($27),do_entArith + RESTORE_ALL + rti +.end entArith + +.align 5 +.globl entIF +.ent entIF +entIF: + SAVE_ALL + bis $30,$30,$19 + lda $27,do_entIF + jsr $26,($27),do_entIF + RESTORE_ALL + rti +.end entIF + +.align 5 +.globl entUna +.ent entUna +entUna: + SAVE_ALL + bis $30,$30,$19 + lda $27,do_entUna + jsr $26,($27),do_entUna + RESTORE_ALL + rti +.end entUna + .align 5 .globl sys_call_table sys_call_table: diff -u --recursive --new-file v1.1.82/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c --- v1.1.82/linux/arch/alpha/kernel/irq.c Mon Jan 16 14:18:12 1995 +++ linux/arch/alpha/kernel/irq.c Wed Jan 18 22:52:36 1995 @@ -10,6 +10,7 @@ * should be easier. */ +#include #include #include #include @@ -139,62 +140,125 @@ halt(); } -static void handle_irq(int irq, struct pt_regs * regs) +static void handle_nmi(struct pt_regs * regs) +{ + printk("Whee.. NMI received. Probable hardware error\n"); + printk("61=%02x, 461=%02x\n", inb(0x61), inb(0x461)); +} + +static void unexpected_irq(int irq, struct pt_regs * regs) +{ + int i; + + printk("IO device interrupt, irq = %d\n", irq); + printk("PC = %016lx PS=%04lx\n", regs->pc, regs->ps); + printk("Expecting: "); + for (i = 0; i < 16; i++) + if (irq_action[i].handler) + printk("[%s:%d] ", irq_action[i].name, i); + printk("\n"); + printk("64=%02x, 60=%02x, 3fa=%02x 2fa=%02x\n", + inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa)); + outb(0x0c, 0x3fc); + outb(0x0c, 0x2fc); + outb(0,0x61); + outb(0,0x461); +} + +static inline void handle_irq(int irq, struct pt_regs * regs) { struct irqaction * action = irq + irq_action; kstat.interrupts[irq]++; - if (action->handler) - action->handler(irq, regs); + if (!action->handler) { + unexpected_irq(irq, regs); + return; + } + action->handler(irq, regs); +} + +static void local_device_interrupt(unsigned long vector, struct pt_regs * regs) +{ + switch (vector) { + /* com1: map to irq 4 */ + case 0x900: + handle_irq(4, regs); + return; + + /* com2: map to irq 3 */ + case 0x920: + handle_irq(3, regs); + return; + + /* keyboard: map to irq 1 */ + case 0x980: + handle_irq(1, regs); + return; + + /* mouse: map to irq 12 */ + case 0x990: + handle_irq(12, regs); + return; + default: + printk("Unknown local interrupt %lx\n", vector); + } } /* - * I don't have any good documentation on the EISA hardware interrupt - * stuff: I don't know the mapping between the interrupt vector and the - * EISA interrupt number. - * - * It *seems* to be 0x8X0 for EISA interrupt X, and 0x9X0 for the - * local motherboard interrupts.. + * The vector is 0x8X0 for EISA interrupt X, and 0x9X0 for the local + * motherboard interrupts.. This is for the Jensen. * - * 0x660 - NMI? + * 0x660 - NMI * - * 0x800 - ??? I've gotten this, but EISA irq0 shouldn't happen - * as the timer is not on the EISA bus - * - * 0x860 - ??? floppy disk (EISA irq6) - * - * 0x900 - ??? I get this at autoprobing when the EISA serial - * lines com3/com4 don't exist. It keeps coming after - * that.. + * 0x800 - IRQ0 interval timer (not used, as we use the RTC timer) + * 0x810 - IRQ1 line printer (duh..) + * 0x860 - IRQ6 floppy disk + * 0x8E0 - IRQ14 SCSI controller * + * 0x900 - COM1 + * 0x920 - COM2 * 0x980 - keyboard * 0x990 - mouse * - * We'll see.. + * The PCI version is more sane: it doesn't have the local interrupts at + * all, and has only normal PCI interrupts from devices. Happily it's easy + * enough to do a sane mapping from the Jensen.. Note that this means + * that we may have to do a hardware "ack" to a different interrupt than + * we report to the rest of the world.. */ static void device_interrupt(unsigned long vector, struct pt_regs * regs) { - int i; - static int nr = 0; + int irq, ack; - if (vector == 0x980 && irq_action[1].handler) { - handle_irq(1, regs); + if (vector == 0x660) { + handle_nmi(regs); return; } - if (nr > 3) + ack = irq = (vector - 0x800) >> 4; +#ifndef CONFIG_PCI + if (vector >= 0x900) { + local_device_interrupt(vector, regs); return; - nr++; - printk("IO device interrupt, vector = %lx\n", vector); - printk("PC = %016lx PS=%04lx\n", regs->pc, regs->ps); - printk("Expecting: "); - for (i = 0; i < 16; i++) - if (irq_action[i].handler) - printk("[%s:%d] ", irq_action[i].name, i); - printk("\n"); - printk("64=%02x, 60=%02x, 3fa=%02x 2fa=%02x\n", - inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa)); - printk("61=%02x, 461=%02x\n", inb(0x61), inb(0x461)); + } + /* irq1 is supposed to be the keyboard, silly Jensen */ + if (irq == 1) + irq = 7; +#endif + printk("%d%d", irq, ack); + if (!irq) + printk("."); + else + handle_irq(irq, regs); + + /* ACK the interrupt making it the lowest priority */ + /* First the slave .. */ + if (ack > 7) { + outb(0xC0 | (ack - 8), 0xa0); + ack = 2; + } + /* .. then the master */ + outb(0xC0 | ack, 0x20); } static void machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs) diff -u --recursive --new-file v1.1.82/linux/arch/alpha/kernel/setup.c linux/arch/alpha/kernel/setup.c --- v1.1.82/linux/arch/alpha/kernel/setup.c Mon Jan 16 14:18:12 1995 +++ linux/arch/alpha/kernel/setup.c Wed Jan 18 22:52:36 1995 @@ -65,12 +65,12 @@ cluster = memdesc->cluster; for (i = memdesc->numclusters ; i > 0; i--, cluster++) { unsigned long tmp; - if (cluster->usage & 1) - continue; tmp = (cluster->start_pfn + cluster->numpages) << PAGE_SHIFT; if (tmp > high) high = tmp; } + /* round it up to an even number of pages.. */ + high = (high + PAGE_SIZE) & (PAGE_MASK*2); return PAGE_OFFSET + high; } diff -u --recursive --new-file v1.1.82/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c --- v1.1.82/linux/arch/alpha/kernel/traps.c Mon Jan 16 14:18:13 1995 +++ linux/arch/alpha/kernel/traps.c Wed Jan 18 22:52:36 1995 @@ -15,12 +15,33 @@ unsigned long i; printk("%s %ld\n", str, err); - for (i = 0 ; i++ ; i < 500000000) + printk("PC = %016lx PS = %04lx\n", regs->pc, regs->ps); + for (i = 0 ; i < 5000000000 ; i++) /* pause */; halt(); } +asmlinkage void do_entArith(unsigned long summary, unsigned long write_mask, unsigned long a2, struct pt_regs * regs) +{ + printk("Arithmetic trap: %02lx %016lx\n", summary, write_mask); + die_if_kernel("Arithmetic fault", regs, 0); +} + +asmlinkage void do_entIF(unsigned long type, unsigned long a1, unsigned long a2, struct pt_regs * regs) +{ + die_if_kernel("Instruction fault", regs, type); +} + +asmlinkage void do_entUna(unsigned long va, unsigned long opcode, unsigned long reg, struct pt_regs * regs) +{ + printk("Unaligned trap: %016lx %ld %ld\n", va, opcode, reg); + die_if_kernel("Unaligned", regs, 0); +} + extern asmlinkage void entMM(void); +extern asmlinkage void entIF(void); +extern asmlinkage void entArith(void); +extern asmlinkage void entUna(void); void trap_init(void) { @@ -30,5 +51,9 @@ "___tmp:\tldgp %0,0(%0)" : "=r" (gptr)); wrkgp(gptr); + + wrent(entArith, 1); wrent(entMM, 2); + wrent(entIF, 3); + wrent(entUna, 4); } diff -u --recursive --new-file v1.1.82/linux/arch/alpha/mm/fault.c linux/arch/alpha/mm/fault.c --- v1.1.82/linux/arch/alpha/mm/fault.c Mon Jan 16 14:18:13 1995 +++ linux/arch/alpha/mm/fault.c Wed Jan 18 09:31:40 1995 @@ -44,12 +44,9 @@ if (mmcsr == 1) goto bad_area; - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - goto bad_area; - if (vma->vm_end > address) - break; - } + vma = find_vma(current, address); + if (!vma) + goto bad_area; if (vma->vm_start <= address) goto good_area; if (!(vma->vm_flags & VM_GROWSDOWN)) diff -u --recursive --new-file v1.1.82/linux/arch/alpha/mm/init.c linux/arch/alpha/mm/init.c --- v1.1.82/linux/arch/alpha/mm/init.c Mon Jan 16 14:18:13 1995 +++ linux/arch/alpha/mm/init.c Tue Jan 17 00:57:01 1995 @@ -91,7 +91,7 @@ * 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 cah change the L1 page table by just using VL1p below. + * means that we can change the L1 page table by just using VL1p below. */ #define VL1p ((unsigned long *) 0xffffffffffffe000) unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) diff -u --recursive --new-file v1.1.82/linux/arch/i386/config.in linux/arch/i386/config.in --- v1.1.82/linux/arch/i386/config.in Mon Jan 16 14:18:13 1995 +++ linux/arch/i386/config.in Wed Jan 18 21:00:26 1995 @@ -187,6 +187,7 @@ fi fi fi +bool 'Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support' CONFIG_AZTCD n comment 'Filesystems' diff -u --recursive --new-file v1.1.82/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c --- v1.1.82/linux/arch/i386/kernel/bios32.c Mon Jan 16 14:18:13 1995 +++ linux/arch/i386/kernel/bios32.c Tue Jan 17 00:57:01 1995 @@ -550,17 +550,17 @@ for (i=0;ics = USER_CS; + regs->ds = regs->es = regs->ss = regs->fs = regs->gs = USER_DS; regs->eip = eip; regs->esp = esp; } diff -u --recursive --new-file v1.1.82/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c --- v1.1.82/linux/arch/i386/kernel/ptrace.c Fri Jan 13 10:12:22 1995 +++ linux/arch/i386/kernel/ptrace.c Wed Jan 18 09:31:40 1995 @@ -152,17 +152,14 @@ } } -static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long addr) +static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr) { struct vm_area_struct * vma; addr &= PAGE_MASK; - for (vma = tsk->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - return NULL; - if (vma->vm_end > addr) - break; - } + vma = find_vma(tsk,addr); + if (!vma) + return NULL; if (vma->vm_start <= addr) return vma; if (!(vma->vm_flags & VM_GROWSDOWN)) @@ -181,7 +178,7 @@ static int read_long(struct task_struct * tsk, unsigned long addr, unsigned long * result) { - struct vm_area_struct * vma = find_vma(tsk, addr); + struct vm_area_struct * vma = find_extend_vma(tsk, addr); if (!vma) return -EIO; @@ -223,7 +220,7 @@ static int write_long(struct task_struct * tsk, unsigned long addr, unsigned long data) { - struct vm_area_struct * vma = find_vma(tsk, addr); + struct vm_area_struct * vma = find_extend_vma(tsk, addr); if (!vma) return -EIO; diff -u --recursive --new-file v1.1.82/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c --- v1.1.82/linux/arch/i386/mm/fault.c Mon Jan 16 14:18:13 1995 +++ linux/arch/i386/mm/fault.c Wed Jan 18 09:31:40 1995 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -33,12 +34,9 @@ /* get the address */ __asm__("movl %%cr2,%0":"=r" (address)); - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - goto bad_area; - if (vma->vm_end > address) - break; - } + vma = find_vma(current, address); + if (!vma) + goto bad_area; if (vma->vm_start <= address) goto good_area; if (!(vma->vm_flags & VM_GROWSDOWN)) diff -u --recursive --new-file v1.1.82/linux/arch/mips/boot/Makefile linux/arch/mips/boot/Makefile --- v1.1.82/linux/arch/mips/boot/Makefile Mon Jan 16 14:18:14 1995 +++ linux/arch/mips/boot/Makefile Tue Jan 17 00:57:01 1995 @@ -10,7 +10,7 @@ # # -# Fake compessed boot +# Fake compressed boot # zImage: $(CONFIGURE) $(TOPDIR)/vmlinux ln -fs $(TOPDIR)/vmlinux zImage diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/Makefile linux/arch/mips/kernel/Makefile --- v1.1.82/linux/arch/mips/kernel/Makefile Mon Jan 16 14:18:14 1995 +++ linux/arch/mips/kernel/Makefile Wed Jan 18 08:54:12 1995 @@ -19,7 +19,7 @@ $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o OBJS = process.o signal.o entry.o traps.o irq.o ptrace.o cache.o resume.o \ - ioport.o bootinfo.o + ioport.o setup.o bios32.o all: kernel.o head.o diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/bios32.c linux/arch/mips/kernel/bios32.c --- v1.1.82/linux/arch/mips/kernel/bios32.c Thu Jan 1 02:00:00 1970 +++ linux/arch/mips/kernel/bios32.c Wed Jan 18 08:54:12 1995 @@ -0,0 +1,7 @@ +/* + * bios 32 replacement + */ +unsigned long bios32_init(unsigned long memory_start, unsigned long memory_end) +{ + return memory_start; +} diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/bootinfo.c linux/arch/mips/kernel/bootinfo.c --- v1.1.82/linux/arch/mips/kernel/bootinfo.c Mon Jan 16 14:18:14 1995 +++ linux/arch/mips/kernel/bootinfo.c Thu Jan 1 02:00:00 1970 @@ -1,14 +0,0 @@ -/* - * arch/mips/kernel/bootinfo.c - * - * Copyright (C) 1995 Ralf Baechle - * - * Kernel data passed by the loader - */ -#include - -/* - * Initialise this structure so that it will be placed in the - * .data section of the object file - */ -struct bootinfo boot_info = BOOT_INFO; diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/entry.S linux/arch/mips/kernel/entry.S --- v1.1.82/linux/arch/mips/kernel/entry.S Mon Jan 16 14:18:14 1995 +++ linux/arch/mips/kernel/entry.S Wed Jan 18 08:54:12 1995 @@ -41,6 +41,10 @@ .set noreorder .align 4 handle_bottom_half: + /* + * If your assembler breaks on the next line it's + * time to update! + */ lui s0,%hi(_intr_count) lw s1,%lo(_intr_count)(s0) mfc0 s3,CP0_STATUS # Enable IRQs @@ -286,7 +290,7 @@ .align 5 spurious_interrupt: /* - * Nothing happend... (whistle) + * Nothing happened... (whistle) */ lui t1,%hi(_spurious_count) lw t0,%lo(_spurious_count)(t1) diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/head.S linux/arch/mips/kernel/head.S --- v1.1.82/linux/arch/mips/kernel/head.S Mon Jan 16 14:18:14 1995 +++ linux/arch/mips/kernel/head.S Wed Jan 18 08:54:13 1995 @@ -306,7 +306,7 @@ mtc0 zero,CP0_INDEX la t0,TLB_ROOT dmtc0 t0,CP0_ENTRYHI - li t0,_swapper_pg_dir + la t0,_swapper_pg_dir srl t0,t0,6 ori t0,t0,MODE_ALIAS # uncachable, dirty, valid dmtc0 t0,CP0_ENTRYLO0 @@ -315,8 +315,9 @@ /* * Make page zero unaccessible to catch zero references */ + la t0,_pg0 li t0,KERNELBASE - addiu t0,_pg0 + addu t0,t1 sw zero,(t0) /* * Load the context register with a value that allows @@ -437,7 +438,7 @@ .data /* - * Instead of Intel's strage and unportable segment descriptor magic + * Instead of Intel's strange and unportable segment descriptor magic * we difference user and kernel space by their address. * Kernel space (== physical memory) is mapped at KSEG[01], * User space is mapped at 0x0. @@ -446,7 +447,7 @@ _segment_fs: .word KERNEL_DS /* - * Inital mapping tables for supported Mips boards. + * Initial mapping tables for supported Mips boards. * First item is always the number of wired TLB entries, * following by EntryHi/EntryLo pairs and page mask. * Since everything must be quad-aligned (8) we insert @@ -501,7 +502,7 @@ * Initial mapping for ACER PICA-61 boards. * FIXME: These are rather preliminary since many drivers, * such as serial, parallel, scsi and ethernet need some - * changes to distuingish between "local" (built-in) and + * changes to distinguish between "local" (built-in) and * "optional" (ISA/PCI) I/O hardware. * Local video ram is mapped to the same location as the * bios maps it to. Console driver has been changed diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/ioport.c linux/arch/mips/kernel/ioport.c --- v1.1.82/linux/arch/mips/kernel/ioport.c Mon Jan 16 14:18:14 1995 +++ linux/arch/mips/kernel/ioport.c Wed Jan 18 08:54:13 1995 @@ -15,10 +15,6 @@ struct resource_entry_t *next; } resource_entry_t; -static resource_entry_t iolist = { 0, 0, "", NULL }; - -static resource_entry_t iotable[IOTABLE_SIZE]; - /* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */ static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value) { diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/irq.c linux/arch/mips/kernel/irq.c --- v1.1.82/linux/arch/mips/kernel/irq.c Mon Jan 16 14:18:14 1995 +++ linux/arch/mips/kernel/irq.c Wed Jan 18 08:54:13 1995 @@ -1,5 +1,5 @@ /* - * linux/kernel/irq.c + * linux/arch/mips/kernel/irq.c * * Copyright (C) 1992 Linus Torvalds * @@ -12,14 +12,7 @@ /* * IRQ's are in fact implemented a bit like signal handlers for the kernel. - * The same sigaction struct is used, and with similar semantics (ie there - * is a SA_INTERRUPT flag etc). Naturally it's not a 1:1 relation, but there - * are similarities. - * - * sa_handler(int irq_NR) is the default function called (0 if no). - * sa_mask is horribly ugly (I won't even mention it) - * sa_flags contains various info: SA_INTERRUPT etc - * sa_restorer is the unused + * Naturally it's not a 1:1 relation, but there are similarities. */ #include @@ -28,6 +21,7 @@ #include #include #include +#include #include #include @@ -90,7 +84,14 @@ /* * Initial irq handlers. */ -static struct sigaction irq_sigaction[16] = { +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 }, @@ -104,15 +105,15 @@ int get_irq_list(char *buf) { int i, len = 0; - struct sigaction * sa = irq_sigaction; + struct irqaction * action = irq_action; - for (i = 0 ; i < 16 ; i++, sa++) { - if (!sa->sa_handler) + for (i = 0 ; i < 16 ; i++, action++) { + if (!action->handler) continue; len += sprintf(buf+len, "%2d: %8d %c %s\n", i, kstat.interrupts[i], - (sa->sa_flags & SA_INTERRUPT) ? '+' : ' ', - (char *) sa->sa_mask); + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); } return len; } @@ -126,10 +127,10 @@ */ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) { - struct sigaction * sa = irq + irq_sigaction; + struct irqaction * action = irq + irq_action; kstat.interrupts[irq]++; - sa->sa_handler((int) regs); + action->handler(irq, regs); } /* @@ -139,38 +140,38 @@ */ asmlinkage void do_fast_IRQ(int irq) { - struct sigaction * sa = irq + irq_sigaction; + struct irqaction * action = irq + irq_action; kstat.interrupts[irq]++; - sa->sa_handler(irq); + action->handler(irq, NULL); } #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) +int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *), + unsigned long irqflags, const char * devname) { - struct sigaction * sa; + struct irqaction * action; unsigned long flags; if (irq > 15) return -EINVAL; - sa = irq + irq_sigaction; - if (sa->sa_handler) + action = irq + irq_action; + if (action->handler) return -EBUSY; - if (!new_sa->sa_handler) + if (!handler) return -EINVAL; save_flags(flags); cli(); - *sa = *new_sa; - /* - * FIXME: Does the SA_INTERRUPT flag make any sense on the MIPS??? - */ - if (!(sa->sa_flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */ - if (sa->sa_flags & SA_INTERRUPT) + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */ + /* + * FIXME: Does the SA_INTERRUPT flag make any sense on MIPS??? + */ + if (action->flags & SA_INTERRUPT) set_intr_gate(irq,fast_interrupt); else set_intr_gate(irq,interrupt); @@ -188,28 +189,16 @@ return 0; } -int request_irq(unsigned int irq, void (*handler)(int), - unsigned long flags, const char * devname) -{ - struct sigaction sa; - - sa.sa_handler = handler; - sa.sa_flags = flags; - sa.sa_mask = (unsigned long) devname; - sa.sa_restorer = NULL; - return irqaction(irq,&sa); -} - void free_irq(unsigned int irq) { - struct sigaction * sa = irq + irq_sigaction; + struct irqaction * action = irq + irq_action; unsigned long flags; if (irq > 15) { printk("Trying to free IRQ%d\n",irq); return; } - if (!sa->sa_handler) { + if (!action->handler) { printk("Trying to free free IRQ%d\n",irq); return; } @@ -223,26 +212,14 @@ outb(cache_A1,0xA1); } set_intr_gate(irq,bad_interrupt); - sa->sa_handler = NULL; - sa->sa_flags = 0; - sa->sa_mask = 0; - sa->sa_restorer = NULL; + action->handler = NULL; + action->flags = 0; + action->mask = 0; + action->name = NULL; restore_flags(flags); } -#if 0 -/* - * handle fpu errors - */ -static void math_error_irq(int cpl) -{ - if (!hard_math) - return; - handle_fpe(); -} -#endif - -static void no_action(int cpl) { } +static void no_action(int cpl, struct pt_regs * regs) { } unsigned int probe_irq_on (void) { @@ -300,6 +277,10 @@ { int i; + /* set the clock to 100 Hz */ + outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ + outb_p(LATCH & 0xff , 0x40); /* LSB */ + outb(LATCH >> 8 , 0x40); /* MSB */ for (i = 0; i < 16 ; i++) set_intr_gate(i, bad_interrupt); if (request_irq(2, no_action, SA_INTERRUPT, "cascade")) diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/process.c linux/arch/mips/kernel/process.c --- v1.1.82/linux/arch/mips/kernel/process.c Mon Jan 16 14:18:14 1995 +++ linux/arch/mips/kernel/process.c Wed Jan 18 08:54:13 1995 @@ -27,17 +27,6 @@ #include #include -/* - * Tell us the machine setup.. - */ -#pragma char wp_works_ok = 0; /* set if paging hardware honours WP */ -char wait_available; /* set if the "wait" instruction available */ - -/* - * Bus types .. - */ -int EISA_bus = 0; - asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call"); /* @@ -115,7 +104,7 @@ /* * New tasks loose permission to use the fpu. This accelerates context - * switching for non fp programms, which true for the most programms. + * switching for non fp programs, which true for the most programs. */ p->tss.cp0_status = regs->cp0_status & ~(ST0_CU1|ST0_CU0|ST0_KSU|ST0_ERL|ST0_EXL); diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/ptrace.c linux/arch/mips/kernel/ptrace.c --- v1.1.82/linux/arch/mips/kernel/ptrace.c Mon Jan 16 14:18:14 1995 +++ linux/arch/mips/kernel/ptrace.c Wed Jan 18 09:31:40 1995 @@ -152,17 +152,14 @@ } } -static struct vm_area_struct * find_vma(struct task_struct * tsk, unsigned long addr) +static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr) { struct vm_area_struct * vma; addr &= PAGE_MASK; - for (vma = tsk->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - return NULL; - if (vma->vm_end > addr) - break; - } + vma = find_vma(tsk, addr); + if (!vma) + return NULL; if (vma->vm_start <= addr) return vma; if (!(vma->vm_flags & VM_GROWSDOWN)) @@ -181,7 +178,7 @@ static int read_long(struct task_struct * tsk, unsigned long addr, unsigned long * result) { - struct vm_area_struct * vma = find_vma(tsk, addr); + struct vm_area_struct * vma = find_extend_vma(tsk, addr); if (!vma) return -EIO; @@ -223,7 +220,7 @@ static int write_long(struct task_struct * tsk, unsigned long addr, unsigned long data) { - struct vm_area_struct * vma = find_vma(tsk, addr); + struct vm_area_struct * vma = find_extend_vma(tsk, addr); if (!vma) return -EIO; diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/setup.c linux/arch/mips/kernel/setup.c --- v1.1.82/linux/arch/mips/kernel/setup.c Thu Jan 1 02:00:00 1970 +++ linux/arch/mips/kernel/setup.c Wed Jan 18 08:54:13 1995 @@ -0,0 +1,114 @@ +/* + * linux/arch/mips/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Tell us the machine setup.. + */ +char wait_available; /* set if the "wait" instruction available */ + +/* + * Bus types .. + */ +int EISA_bus = 0; + +/* + * Setup options + */ +struct drive_info_struct drive_info; +struct screen_info screen_info; + +unsigned char aux_device_present; +extern int ramdisk_size; +extern int root_mountflags; +extern int end; + +extern char empty_zero_page[PAGE_SIZE]; + +/* + * Initialise this structure so that it will be placed in the + * .data section of the object file + */ +struct bootinfo boot_info = BOOT_INFO; + +/* + * This is set up by the setup-routine at boot-time + */ +#define PARAM empty_zero_page +#define EXT_MEM (boot_info.memupper) +#define DRIVE_INFO (boot_info.drive_info) +#define SCREEN_INFO (screen_info) +#define MOUNT_ROOT_RDONLY (boot_info.mount_root_rdonly) +#define RAMDISK_SIZE (boot_info.ramdisk_size) +#if 0 +#define ORIG_ROOT_DEV (*(unsigned short *) (PARAM+0x1FC)) +#define AUX_DEVICE_INFO (*(unsigned char *) (PARAM+0x1FF)) +#endif +#define COMMAND_LINE (boot_info.command_line) + +static char command_line[CL_SIZE] = { 0, }; + +void setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p) +{ + unsigned long memory_start, memory_end; + char c = ' ', *to = command_line, *from = COMMAND_LINE; + int len = 0; + +#if 0 + ROOT_DEV = ORIG_ROOT_DEV; +#endif + drive_info = DRIVE_INFO; + screen_info = SCREEN_INFO; +#if 0 + aux_device_present = AUX_DEVICE_INFO; +#endif + memory_end = EXT_MEM; + memory_end &= PAGE_MASK; + ramdisk_size = RAMDISK_SIZE; + if (MOUNT_ROOT_RDONLY) + root_mountflags |= MS_RDONLY; + memory_start = (unsigned long) &end - KERNELBASE; + + for (;;) { + if (c == ' ' && *(unsigned long *)from == *(unsigned long *)"mem=") { + memory_end = simple_strtoul(from+4, &from, 0); + if ( *from == 'K' || *from == 'k' ) { + memory_end = memory_end << 10; + from++; + } else if ( *from == 'M' || *from == 'm' ) { + memory_end = memory_end << 20; + from++; + } + } + c = *(from++); + if (!c) + break; + if (CL_SIZE <= ++len) + break; + *(to++) = c; + } + *to = '\0'; + *cmdline_p = command_line; + *memory_start_p = memory_start; + *memory_end_p = memory_end; +} diff -u --recursive --new-file v1.1.82/linux/arch/mips/kernel/traps.c linux/arch/mips/kernel/traps.c --- v1.1.82/linux/arch/mips/kernel/traps.c Mon Jan 16 14:18:14 1995 +++ linux/arch/mips/kernel/traps.c Tue Jan 17 00:57:01 1995 @@ -244,7 +244,7 @@ case 0x44000000: case 0xc4000000: case 0xe4000000: - printk("CP1 instruction - enabeling cp1.\n"); + printk("CP1 instruction - enabling cp1.\n"); regs->cp0_status |= ST0_CU1; /* * No need to handle branch delay slots @@ -252,7 +252,7 @@ break; default: /* - * This wasn't a cp1 instruction and therfore illegal. + * This wasn't a cp1 instruction and therefore illegal. * Default is to kill the process. */ send_sig(SIGILL, current, 1); @@ -289,7 +289,7 @@ void do_reserved(struct pt_regs *regs) { /* - * Game over - no way to handle this if it ever occours. + * Game over - no way to handle this if it ever occurs. * Most probably caused by a new unknown cpu type or a * after another deadly hard/software error. */ diff -u --recursive --new-file v1.1.82/linux/arch/mips/mm/Makefile linux/arch/mips/mm/Makefile --- v1.1.82/linux/arch/mips/mm/Makefile Mon Jan 16 14:18:14 1995 +++ linux/arch/mips/mm/Makefile Wed Jan 18 08:54:13 1995 @@ -14,7 +14,7 @@ .c.s: $(CC) $(CFLAGS) -S $< -OBJS = fault.o +OBJS = fault.o init.o mm.o: $(OBJS) $(LD) -r -o mm.o $(OBJS) diff -u --recursive --new-file v1.1.82/linux/arch/mips/mm/fault.c linux/arch/mips/mm/fault.c --- v1.1.82/linux/arch/mips/mm/fault.c Mon Jan 16 14:18:14 1995 +++ linux/arch/mips/mm/fault.c Wed Jan 18 09:31:40 1995 @@ -1,5 +1,5 @@ /* - * arch/mips/mm/memory.c + * arch/mips/mm/fault.c * * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds * Ported to MIPS by Ralf Baechle @@ -20,12 +20,7 @@ #include #include -extern unsigned long pg0[1024]; /* page table for 0-4MB for everybody */ - -extern void scsi_mem_init(unsigned long); -extern void sound_mem_init(void); -extern void die_if_kernel(char *,struct pt_regs *,long); -extern void show_net_buffers(void); +extern void die_if_kernel(char *, struct pt_regs *, long); /* * This routine handles page faults. It determines the address, @@ -42,12 +37,9 @@ __asm__("dmfc0\t%0,$8" : "=r" (address)); - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - goto bad_area; - if (vma->vm_end > address) - break; - } + vma = find_vma(current, address); + if (!vma) + goto bad_area; if (vma->vm_start <= address) goto good_area; if (!(vma->vm_flags & VM_GROWSDOWN)) @@ -116,228 +108,4 @@ } die_if_kernel("Oops", regs, error_code); do_exit(SIGKILL); -} - -/* - * BAD_PAGE is the page that is used for page faults when linux - * is out-of-memory. Older versions of linux just did a - * do_exit(), but using this instead means there is less risk - * for a process dying in kernel mode, possibly leaving a inode - * unused etc.. - * - * BAD_PAGETABLE is the accompanying page-table: it is initialized - * to point to BAD_PAGE entries. - * - * ZERO_PAGE is a special page that is used for zero-initialized - * data and COW. - */ -unsigned long __bad_pagetable(void) -{ - extern char empty_bad_page_table[PAGE_SIZE]; - unsigned long dummy; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - "1:\tsw\t%2,(%0)\n\t" - "subu\t%1,%1,1\n\t" - "bne\t$0,%1,1b\n\t" - "addiu\t%0,%0,1\n\t" - ".set\treorder" - :"=r" (dummy), - "=r" (dummy) - :"r" (BAD_PAGE + PAGE_TABLE), - "0" ((long) empty_bad_page_table), - "1" (PTRS_PER_PAGE)); - - return (unsigned long) empty_bad_page_table; -} - -unsigned long __bad_page(void) -{ - extern char empty_bad_page[PAGE_SIZE]; - unsigned long dummy; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - "1:\tsw\t$0,(%0)\n\t" - "subu\t%1,%1,1\n\t" - "bne\t$0,%1,1b\n\t" - "addiu\t%0,%0,1\n\t" - ".set\treorder" - :"=r" (dummy), - "=r" (dummy) - :"0" ((long) empty_bad_page), - "1" (PTRS_PER_PAGE)); - - return (unsigned long) empty_bad_page; -} - -unsigned long __zero_page(void) -{ - extern char empty_zero_page[PAGE_SIZE]; - unsigned long dummy; - - __asm__ __volatile__( - ".set\tnoreorder\n\t" - "1:\tsw\t$0,(%0)\n\t" - "subu\t%1,%1,1\n\t" - "bne\t$0,%1,1b\n\t" - "addiu\t%0,%0,1\n\t" - ".set\treorder" - :"=r" (dummy), - "=r" (dummy) - :"0" ((long) empty_zero_page), - "1" (PTRS_PER_PAGE)); - - return (unsigned long) empty_zero_page; -} - -void show_mem(void) -{ - int i,free = 0,total = 0,reserved = 0; - int shared = 0; - - printk("Mem-info:\n"); - show_free_areas(); - printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); - i = high_memory >> PAGE_SHIFT; - while (i-- > 0) { - total++; - if (mem_map[i] & MAP_PAGE_RESERVED) - reserved++; - else if (!mem_map[i]) - free++; - else - shared += mem_map[i]-1; - } - printk("%d pages of RAM\n",total); - printk("%d free pages\n",free); - printk("%d reserved pages\n",reserved); - printk("%d pages shared\n",shared); - show_buffers(); -#ifdef CONFIG_NET - show_net_buffers(); -#endif -} - -extern unsigned long free_area_init(unsigned long, unsigned long); - -/* - * paging_init() sets up the page tables - note that the first 4MB are - * already mapped by head.S. - * - * This routines also unmaps the page at virtual kernel address 0, so - * that we can trap those pesky NULL-reference errors in the kernel. - */ -unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) -{ - unsigned long * pg_dir; - unsigned long * pg_table; - unsigned long tmp; - unsigned long address; - - start_mem = PAGE_ALIGN(start_mem); - address = 0; - pg_dir = swapper_pg_dir; - while (address < end_mem) { - tmp = *pg_dir; - tmp &= PAGE_MASK; - if (!tmp) { - tmp = start_mem; - start_mem += PAGE_SIZE; - } - /* - * also map it in at 0x00000000 for init - */ - *pg_dir = tmp | PAGE_TABLE; - pg_dir++; - pg_table = (unsigned long *) (tmp & PAGE_MASK); - for (tmp = 0 ; tmp < PTRS_PER_PAGE ; tmp++,pg_table++) { - if (address < end_mem) - *pg_table = address | PAGE_SHARED; - else - *pg_table = 0; - address += PAGE_SIZE; - } - } -#if KERNELBASE == KSEG0 - cacheflush(); -#endif - invalidate(); - return free_area_init(start_mem, end_mem); -} - -void mem_init(unsigned long start_mem, unsigned long end_mem) -{ - int codepages = 0; - int reservedpages = 0; - int datapages = 0; - unsigned long tmp; - extern int etext; - - end_mem &= PAGE_MASK; - high_memory = end_mem; - - /* mark usable pages in the mem_map[] */ - start_mem = PAGE_ALIGN(start_mem); - - while (start_mem < high_memory) { - mem_map[MAP_NR(start_mem)] = 0; - start_mem += PAGE_SIZE; - } -#ifdef CONFIG_SCSI - scsi_mem_init(high_memory); -#endif -#ifdef CONFIG_SOUND - sound_mem_init(); -#endif - for (tmp = 0 ; tmp < high_memory ; tmp += PAGE_SIZE) { - if (mem_map[MAP_NR(tmp)]) { - /* - * We don't have any reserved pages on the - * MIPS systems supported until now - */ - if (0) - reservedpages++; - else if (tmp < ((unsigned long) &etext - KERNELBASE)) - codepages++; - else - datapages++; - continue; - } - mem_map[MAP_NR(tmp)] = 1; - free_page(tmp); - } - tmp = nr_free_pages << PAGE_SHIFT; - printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n", - tmp >> 10, - high_memory >> 10, - codepages << (PAGE_SHIFT-10), - reservedpages << (PAGE_SHIFT-10), - datapages << (PAGE_SHIFT-10)); - - invalidate(); - return; -} - -void si_meminfo(struct sysinfo *val) -{ - int i; - - i = high_memory >> PAGE_SHIFT; - val->totalram = 0; - val->sharedram = 0; - val->freeram = nr_free_pages << PAGE_SHIFT; - val->bufferram = buffermem; - while (i-- > 0) { - if (mem_map[i] & MAP_PAGE_RESERVED) - continue; - val->totalram++; - if (!mem_map[i]) - continue; - val->sharedram += mem_map[i]-1; - } - val->totalram <<= PAGE_SHIFT; - val->sharedram <<= PAGE_SHIFT; - return; } diff -u --recursive --new-file v1.1.82/linux/arch/mips/mm/init.c linux/arch/mips/mm/init.c --- v1.1.82/linux/arch/mips/mm/init.c Thu Jan 1 02:00:00 1970 +++ linux/arch/mips/mm/init.c Wed Jan 18 08:54:13 1995 @@ -0,0 +1,252 @@ +/* + * arch/mips/mm/init.c + * + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * Ported to MIPS by Ralf Baechle + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +extern unsigned long pg0[1024]; /* page table for 0-4MB for everybody */ + +extern void scsi_mem_init(unsigned long); +extern void sound_mem_init(void); +extern void die_if_kernel(char *,struct pt_regs *,long); +extern void show_net_buffers(void); + +/* + * BAD_PAGE is the page that is used for page faults when linux + * is out-of-memory. Older versions of linux just did a + * do_exit(), but using this instead means there is less risk + * for a process dying in kernel mode, possibly leaving a inode + * unused etc.. + * + * BAD_PAGETABLE is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ +unsigned long __bad_pagetable(void) +{ + extern char empty_bad_page_table[PAGE_SIZE]; + unsigned long dummy; + + __asm__ __volatile__( + ".set\tnoreorder\n\t" + "1:\tsw\t%2,(%0)\n\t" + "subu\t%1,%1,1\n\t" + "bne\t$0,%1,1b\n\t" + "addiu\t%0,%0,1\n\t" + ".set\treorder" + :"=r" (dummy), + "=r" (dummy) + :"r" (BAD_PAGE + PAGE_TABLE), + "0" ((long) empty_bad_page_table), + "1" (PTRS_PER_PAGE)); + + return (unsigned long) empty_bad_page_table; +} + +unsigned long __bad_page(void) +{ + extern char empty_bad_page[PAGE_SIZE]; + unsigned long dummy; + + __asm__ __volatile__( + ".set\tnoreorder\n\t" + "1:\tsw\t$0,(%0)\n\t" + "subu\t%1,%1,1\n\t" + "bne\t$0,%1,1b\n\t" + "addiu\t%0,%0,1\n\t" + ".set\treorder" + :"=r" (dummy), + "=r" (dummy) + :"0" ((long) empty_bad_page), + "1" (PTRS_PER_PAGE)); + + return (unsigned long) empty_bad_page; +} + +unsigned long __zero_page(void) +{ + extern char empty_zero_page[PAGE_SIZE]; + unsigned long dummy; + + __asm__ __volatile__( + ".set\tnoreorder\n\t" + "1:\tsw\t$0,(%0)\n\t" + "subu\t%1,%1,1\n\t" + "bne\t$0,%1,1b\n\t" + "addiu\t%0,%0,1\n\t" + ".set\treorder" + :"=r" (dummy), + "=r" (dummy) + :"0" ((long) empty_zero_page), + "1" (PTRS_PER_PAGE)); + + return (unsigned long) empty_zero_page; +} + +void show_mem(void) +{ + int i,free = 0,total = 0,reserved = 0; + int shared = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = high_memory >> PAGE_SHIFT; + while (i-- > 0) { + total++; + if (mem_map[i] & MAP_PAGE_RESERVED) + reserved++; + else if (!mem_map[i]) + free++; + else + shared += mem_map[i]-1; + } + printk("%d pages of RAM\n",total); + printk("%d free pages\n",free); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + show_buffers(); +#ifdef CONFIG_NET + show_net_buffers(); +#endif +} + +extern unsigned long free_area_init(unsigned long, unsigned long); + +/* + * paging_init() sets up the page tables - note that the first 4MB are + * already mapped by head.S. + * + * This routines also unmaps the page at virtual kernel address 0, so + * that we can trap those pesky NULL-reference errors in the kernel. + */ +unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) +{ + unsigned long * pg_dir; + unsigned long * pg_table; + unsigned long tmp; + unsigned long address; + + start_mem = PAGE_ALIGN(start_mem); + address = 0; + pg_dir = swapper_pg_dir; + while (address < end_mem) { + tmp = *pg_dir; + tmp &= PAGE_MASK; + if (!tmp) { + tmp = start_mem; + start_mem += PAGE_SIZE; + } + /* + * also map it in at 0x00000000 for init + */ + *pg_dir = tmp | PAGE_TABLE; + pg_dir++; + pg_table = (unsigned long *) (tmp & PAGE_MASK); + for (tmp = 0 ; tmp < PTRS_PER_PAGE ; tmp++,pg_table++) { + if (address < end_mem) + *pg_table = address | PAGE_SHARED; + else + *pg_table = 0; + address += PAGE_SIZE; + } + } +#if KERNELBASE == KSEG0 + cacheflush(); +#endif + invalidate(); + return free_area_init(start_mem, end_mem); +} + +void mem_init(unsigned long start_mem, unsigned long end_mem) +{ + int codepages = 0; + int reservedpages = 0; + int datapages = 0; + unsigned long tmp; + extern int etext; + + end_mem &= PAGE_MASK; + high_memory = end_mem; + + /* mark usable pages in the mem_map[] */ + start_mem = PAGE_ALIGN(start_mem); + + while (start_mem < high_memory) { + mem_map[MAP_NR(start_mem)] = 0; + start_mem += PAGE_SIZE; + } +#ifdef CONFIG_SCSI + scsi_mem_init(high_memory); +#endif +#ifdef CONFIG_SOUND + sound_mem_init(); +#endif + for (tmp = 0 ; tmp < high_memory ; tmp += PAGE_SIZE) { + if (mem_map[MAP_NR(tmp)]) { + /* + * We don't have any reserved pages on the + * MIPS systems supported until now + */ + if (0) + reservedpages++; + else if (tmp < ((unsigned long) &etext - KERNELBASE)) + codepages++; + else + datapages++; + continue; + } + mem_map[MAP_NR(tmp)] = 1; + free_page(tmp); + } + tmp = nr_free_pages << PAGE_SHIFT; + printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n", + tmp >> 10, + high_memory >> 10, + codepages << (PAGE_SHIFT-10), + reservedpages << (PAGE_SHIFT-10), + datapages << (PAGE_SHIFT-10)); + + invalidate(); + return; +} + +void si_meminfo(struct sysinfo *val) +{ + int i; + + i = high_memory >> PAGE_SHIFT; + val->totalram = 0; + val->sharedram = 0; + val->freeram = nr_free_pages << PAGE_SHIFT; + val->bufferram = buffermem; + while (i-- > 0) { + if (mem_map[i] & MAP_PAGE_RESERVED) + continue; + val->totalram++; + if (!mem_map[i]) + continue; + val->sharedram += mem_map[i]-1; + } + val->totalram <<= PAGE_SHIFT; + val->sharedram <<= PAGE_SHIFT; + return; +} diff -u --recursive --new-file v1.1.82/linux/arch/sparc/kernel/ioport.c linux/arch/sparc/kernel/ioport.c --- v1.1.82/linux/arch/sparc/kernel/ioport.c Mon Jan 16 14:18:15 1995 +++ linux/arch/sparc/kernel/ioport.c Tue Jan 17 00:57:01 1995 @@ -1,6 +1,6 @@ /* 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 - * throught the compiler. :-) + * through the compiler. :-) * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) */ diff -u --recursive --new-file v1.1.82/linux/drivers/block/Makefile linux/drivers/block/Makefile --- v1.1.82/linux/drivers/block/Makefile Mon Jan 9 07:22:00 1995 +++ linux/drivers/block/Makefile Wed Jan 18 21:00:26 1995 @@ -34,6 +34,11 @@ SRCS := $(SRCS) mcd.c endif +ifdef CONFIG_AZTCD +OBJS := $(OBJS) aztcd.o +SRCS := $(SRCS) aztcd.c +endif + ifdef CONFIG_SBPCD OBJS := $(OBJS) sbpcd.o SRCS := $(SRCS) sbpcd.c diff -u --recursive --new-file v1.1.82/linux/drivers/block/README.aztcd linux/drivers/block/README.aztcd --- v1.1.82/linux/drivers/block/README.aztcd Thu Jan 1 02:00:00 1970 +++ linux/drivers/block/README.aztcd Wed Jan 18 21:00:26 1995 @@ -0,0 +1,688 @@ + Readme-File README.aztcd + for Aztech CD-ROM CDA268-01A, ORCHID CD-3110, OKANO/WEARNES CDD110 + CD-ROM Driver + BETA-Version 0.6 and newer + (for other drives see 6.-8.) + +NOTE: THIS DRIVER WILL WORK WITH THE CD-ROM DRIVES LISTED, WHICH HAVE + A PROPRIETARY INTERFACE (implemented on a sound card or on a + ISA-AT-bus card). + IT WILL DEFINITELY NOT WORK WITH CD-ROM DRIVES WITH *IDE*-INTERFACE, + such as the Aztech CDA269-031SE !!! IF YOU'RE USING A CD-ROM DRIVE + WITH IDE-INTERFACE, SOMETIMES ALSO CALLED ATAPI-COMPATIBLE, PLEASE + USE THE ide-cd.c DRIVER, WRITTEN BY MARK LORD AND SCOTT SNYDER !!! +---------------------------------------------------------------------------- +Contents of this file: + 1. NOTE + 2. INSTALLATION + 3. CONFIGURING YOUR KERNEL + 4. RECOMPILING YOUR KERNEL + 5. KNOWN PROBLEMS, FUTURE DEVELOPMENTS + 6. BUG REPORTS + 7. OTHER DRIVES + 8. IF YOU DON'T SUCCEED ... DEBUGGING + 9. TECHNICAL HISTORY OF THE DRIVER + 10. ACKNOWLEDGEMENTS + 11. PROGRAMMING ADD ONS: CDPLAY.C + APPENDIX: Source code of cdplay.c +---------------------------------------------------------------------------- + +1. NOTE +This software has now been tested in some different machines with AZTECH +CDA268-01A, ORCHID CDS-3110 and ORCHID/WEARNES CDD110 and has proven to be +pretty stable with kernel versions 1.0.0 to 1.1.75. Nevertheless I'd only +assign BETA status to the software, as the number of known users is still +small. So when in 'day to day' use or when used with different firmware +versions of the CD-ROM drives, there still may be some problems. So I'm +quite sure I'll see some bug reports (see chapter BUG REPORTS) and you are +invited in helping us to increase the number of drives, which are supported, +and in debugging this software (see chapters OTHER DRIVES and DEBUGGING). + +Please read the README-files carefully and always keep a backup copy of your +old kernel, in order to reboot if something goes wrong! + + +2. INSTALLATION +If you received this software as a standalone package AZTECH.CDROM.Vxx.tgz +(xx=version number) and not included in the standard Linux kernel, read the +file AZTECH.CDROM.Vxx.README included in that package to install it. The +standalone package's home is 'ftp.gwdg.de : pub/linux/cdrom/drivers/aztech'. +The driver consists of a header file 'aztcd.h', which normally should reside +in /usr/include/linux and the source code 'aztcd.c', which normally resides in +/usr/src/linux/drivers/block. It uses /dev/aztcd0, which must be a valid block +device with major number 29 and reside in directory /dev. To mount a CD-ROM, +your kernel needs to have the ISO9660-filesysytem support included. + + +3. CONFIGURING YOUR KERNEL +If your kernel is already configured for using the AZTECH driver you will +see the following message while Linux boots: + Aztech CD-ROM Init: DriverVersion= BaseAddress= + Aztech CD-ROM Init: FirmwareVersion=>> + Aztech CD-ROM Init: detected + Aztech CD-ROM Init: End +If the message looks different and you are sure to have a supported drive, +it may have a different base address. The Aztech driver does have an 'auto- +detection feature', which looks for the CD-ROM drive at the base address +specified in aztcd.h at compile time. This address can be overwritten by boot +parameter aztcd=....You should reboot and start Linux with boot parameter +aztcd=, e.g. aztcd=0x320. If you do not know the base address, +start your PC with DOS and look at the boot message of your CD-ROM's DOS +driver. + +If the message looks correct, as user 'root' you should be able to mount the +drive by + mount -t iso9660 -r /dev/aztcd0 /mnt +and use it as any other filesystem. (If this does not work, check if + /dev/aztcd0 and /mnt do exist and create them, if necessary by doing + mknod /dev/aztcd0 b 29 0 + mkdir /mnt + +If you still get a different message while Linux boots or when you get the +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. + + +4. RECOMPILING YOUR KERNEL +If your kernel is not yet configured for the AZTECH driver and the ISO9660- +filesystem, you have to recompile your kernel: + +- Edit aztcd.h to set the I/O-adress to your I/O-Base adress (AZT_BASE_ADDR), + the driver does not use interrupts or DMA, so if you are using an AZTECH + CD268, an ORCHID CD-3110 or ORCHID/WEARNES CDD110 that's the only item you + have to set up. + Users of other drives should read chapter OTHER DRIVES of this file. + You also can configure that address by LILO boot parameter aztcd=... +- Build a new kernel, configure it for 'Aztech/Orchid/Okano/Wearnes support' + and include the ISO9660-filesystem. +- Activate the new kernel, normally this is done by running lilo (don't for- + get to configure it before and to keep a copy of your old kernel in case + something goes wrong!). +- Reboot +- You now should see during boot some messages like + Aztech CD-ROM Init: DriverVersion= BaseAddress= + Aztech CD-ROM Init: FirmwareVersion= + Aztech CD-ROM Init: detected + Aztech CD-ROM Init: End +- If the message looks correct, as user 'root' you should be able to mount + the drive by + mount -t iso9660 -r /dev/aztcd0 /mnt + and use it as any other filesystem. (If this does not work, check if + /dev/aztcd0 and /mnt do exist and create them, if necessary by doing + mknod /dev/aztcd0 b 29 0 + mkdir /mnt +- If this still does not help, see chapters OTHER DRIVES and DEBUGGING. + + +5. KNOWN PROBLEMS, FUTURE DEVELOPMENTS +The driver does not support applications such as photo CD, multi session CD +etc.. I do not plan to include the support for that in the driver, because I +do not use such applications. If you are interested in that stuff and would +like to extend the drivers capability on your own, please contact me, I'll +support you as far as I can. + +The drive status recognition does not work correctly in all cases. Changing +a disk or having the door open, when a drive is already mounted, is detected +by the Aztech driver itself, but nevertheless causes multiple read attempts +by the different layers of the ISO9660-filesystem driver, which finally timeout, +so you have to wait quite a little... But isn't it bad style to change a disk +in a mounted drive, anyhow ?! + +The driver uses busy wait in most cases for the drive handshake (macros +STEN_LOW and DTEN_LOW). I tested with a 486/DX2 at 66MHz and a Pentium at +60MHz. Whenever you use a much faster machine you are likely to get timeout +messages. In that case edit aztcd.h and increase the timeout value AZT_TIMEOUT. + +For some 'slow' drive commands I implemented waiting with a timer waitqueue +(macro STEN_LOW_WAIT). If you get this timeout message, you may also edit +aztcd.h and increase the timeout value AZT_STATUS_DELAY. The waitqueue has +shown to be a little critical. If you get kernel panic messages, edit aztcd.c +and substitute STEN_LOW_WAIT by STEN_LOW. Busy waiting with STEN_LOW is more +stable, but also causes CPU overhead. + + +6. BUG REPORTS +Please send detailed bug reports and bug fixes via EMail to + + zimmerma@rz.fht-esslingen.de + +I also would like to get positive feedback. Please inform me, if you have +positive test results. Please include a description of your CD-ROM drive +type and interface card, the exact firmware message during Linux bootup, +the version number of the AZTECH-CDROM-driver and the Linux kernel version. +Also a description of your system's other hardware could be of interest, +especially microprocessor type, clock frequency, other interface cards such +as soundcards, ethernet adapter, game cards etc.. + +I will try to collect the reports and make the necessary modifications from +time to time. I may also come back to you directly with some bug fixes and +ask you to do further testing and debugging. + +Editors of CD-ROMs are invited to send a 'cooperation' copy of their +CD-ROMs to the volunteers, who provided the CD-ROM support for Linux. My +snail mail address for such 'stuff' is + Prof. Dr. W. Zimmermann + Fachhochschule fuer Technik Esslingen + Fachbereich NT + Flandernstrasse 101 + D-73732 Esslingen + Germany + + +7. OTHER DRIVES +The following drives ORCHID CDS3110, OKANO CDD110 and WEARNES nearly look +the same as AZTECH CDA268-01A, especially they seem to use the same command +codes. So it should be simple to make the AZTECH driver work with these drives. + +Unfortuntately I do not have any of these drives available, so I can't test +it. I've got reports, that it works with ORCHID CDS3110 and Game-Wave32 +sound cards and also with WEARNES CDD110 in some different combinations. In +some installations, it seems necessary to initialize the drive with the DOS +driver before (especially if combined with a sound card) and then do a warm +boot (CTRL-ALT-RESET) or start Linux from DOS, e.g. with 'loadlin'. + +Please give me positive or negative feedback, if you have tried it. Especially +you should tell me, how the version string reads during initialization. If you +don't succeed, read chapter DEBUGGING. Thanks in advance! + +Sorry for the inconvenience, but it is difficult to develop for hardware, +which you don't have available for testing. So if you like, please help us. + + +8. DEBUGGING : IF YOU DON'T SUCCEED, TRY THE FOLLOWING +-reread the complete README file +-make sure, that your drive is hardware configured for + transfer mode: polled + IRQ: not used + DMA: not used + Base Address: something like 300, 320 ... + You can check this, when you start the DOS driver, which came with your + drive. By appropiately configuring the drive and the DOS driver you can + check, whether your drive does operate in this mode correctly under DOS. If + it does not operate under DOS, it won't under Linux. + Make sure the Base Address is configured correctly in aztcd.h, also make + sure, that /dev/aztcd0 exists with the correct major number (compare it with + the entry in file /usr/include/linux/major.h for the Aztech drive). +-insert a CD-ROM and close the tray +-cold boot your PC (i.e. via the power on switch or the reset button) +-if you start Linux via DOS, e.g. using loadlin, make sure, that the DOS + driver for the CD-ROM drive is not loaded (comment out the calling lines + in DOS' config.sys!) +-look for the aztcd: init message during Linux init and note them exactly +-log in as root and do a mount -t iso9660 /dev/aztcd0 /mnt +-if you don't succeed in the first time, try several times. Try also to open + and close the tray, then mount again. Please note carefully all commands + you typed in and the aztcd-messages, which you get. +-if you get an 'Aztech CD-ROM init: aborted' message, read the remarks about + the version string below. + +If this does not help, do the same with the following differences +-start DOS before; make now sure, that the DOS driver for the CD-ROM is + loaded under DOS (i.e. uncomment it again in config.sys) +-warm boot your PC (i.e. via CTRL-ALT-DEL) + if you have it, you can also start via loadlin (try both). + ... + Again note all commands and the aztcd-messages. + +If you see STEN_LOW or STEN_LOW_WAIT error messages, increase the timeout +values. + +If this still does not help, +-look in aztcd.c for the lines #if 0 + #define AZT_TEST1 + ... + #endif + and substitute '#if 0' by '#if 1'. +-recompile your kernel and repeat the above two procedures. You will now get + a bundle of debugging messages from the driver. Again note your commands + and the appropriate messages. If you have syslogd running, these messages + may also be found in syslogd's kernel log file. Nevertheless in some + installations syslogd does not yet run, when init() is called, thus look for + the aztcd-messages during init, before the login-prompt appears. + Then look in aztcd.c, to find out, what happened. The normal calling sequence + is: aztcd_init() during Linux bootup procedure init() + after doing a 'mount -t iso9660 /dev/aztcd0 /mnt' the normal calling sequence is + aztcd_open() -> Status 2c after cold reboot with CDROM or audio CD inserted + -> Status 8 after warm reboot with CDROM inserted + -> Status 2e after cold reboot with no disk, closed tray + -> Status 6e after cold reboot, mount with door open + aztUpdateToc() + aztGetDiskInfo() + aztGetQChannelInfo() repeated several times + aztGetToc() + aztGetQChannelInfo() repeated several times + a list of track informations + do_aztcd_request() } + azt_transfer() } repeated several times + azt_poll } + Check, if there is a difference in the calling sequence or the status flags! + + There are a lot of other messages, eg. the ACMD-command code (defined in + aztcd.h), status info from the getAztStatus-command and the state sequence of + the finite state machine in azt_poll(). The most important are the status + messages, look how they are defined and try to understand, if they make + sense in the context where they appear. With a CD-ROM inserted the status + should always be 8, except in aztcd_open(). Try to open the tray, insert a + audio disk, insert no disk or reinsert the CD-ROM and check, if the status + bits change accordingly. The status bits are the most likely point, where + the drive manufacturers may implement changes. + +If you still don't succeed, a good point to start is to look in aztcd.c in +function aztcd_init, where the drive should be detected during init. Do the +following: +-reboot the system with boot parameter 'aztcd=,0x79'. With + parameter 0x79 most of the drive version detection is bypassed. After that + you should see the complete version string including leading and trailing + blanks during init. + Now adapt the statement + if ((result[1]=='A')&&(result[2]=='Z' ...) + in aztcd_init() to exactly match the first 3 or 4 letters you have seen. +-Another point is the 'smart' card detection feature in aztcd_init(). Normally + the CD-ROM drive is ready, when aztcd_init is trying to read the version + string and a time consuming ACMD_SOFT_RESET command can be avoided. This is + detected by looking, if AFL_OP_OK can be read correctly. If the CD-ROM drive + hangs in some unknown state, e.g. because of an error before a warm start or + because you first operated under DOS, even the version string may be correct, + but the following commands will not. Then change the code in such a way, + that the ACMD_SOFT_RESET is issued in any case, by substituting the + if-statement 'if ( ...=AFL_OP_OK)' by 'if (1)'. + +If you succeed, please mail may the exact version string of your drive and +the code modifications, you have made together with a short explanation. +If you don't succeed, you may mail me the output of the debugging messages. +But remember, they are only useful, if they are exact and complete and you +describe in detail your hardware setup and what you did (cold/warm reboot, +with/without DOS, DOS-driver started/not started, which Linux-commands etc.) + + +9. TECHNICAL HISTORY OF THE DRIVER +The AZTECH-Driver is a rework of the Mitsumi-Driver. Four major items had to +be reworked: + +a) The Mitsumi drive does issue complete status informations acknowleding +each command, the Aztech drive does only signal that the command was +processed. So whenever the complete status information is needed, an extra +ACMD_GET_STATUS command is issued. The handshake procedure for the drive +can be found in the functions aztSendCmd(), sendAztCmd() and getAztStatus(). + +b) The Aztech Drive does not have a ACMD_GET_DISK_INFO command, so the +necessary info about the number of tracks (firstTrack, lastTrack), disk +length etc. has to be read from the TOC in the lead in track (see function +aztGetDiskInfo()). + +c) Whenever data is read from the drive, the Mitsumi drive is started with a +command to read an indefinite (0xffffff) number of sectors. When the appropriate +number of sectors is read, the drive is stopped by a ACDM_STOP command. This +does not work with the Aztech drive. I did not find a way to stop it. The +stop and pause commands do only work in AUDIO mode but not in DATA mode. +Therefore I had to modify the 'finite state machine' in function azt_poll to +only read a certain number of sectors and then start a new read on demand. As I +have not completely understood, how the buffer/caching scheme of the Mitsumi +driver was implemented, I am not sure, if I have covered all cases correctly, +whenever you get timeout messages, the bug is most likely to be in that +function azt_poll() around switch(cmd) .... case ACD_S_DATA. + +d) I did not get information about changing drive mode. So I doubt, that the +code around function azt_poll() case AZT_S_MODE does work. In my test I have +not been able to switch to reading in raw mode. For reading raw mode, Aztech +uses a different command than for cooked mode, which I only have implemen- +ted in the ioctl-section but not in the section which is used by the ISO9660- + +The driver was developped on an AST PC with Intel 486/DX2, 8MB RAM, 340MB IDE +hard disk and on an AST PC with Intel Pentium 60MHz, 16MB RAM, 520MB IDE +running Linux kernel version 1.0.9 from the LST 1.8 Distribution. The kernel +was compiled with gcc.2.5.8. My CD-ROM drive is an Aztech CDA268-01A. My +drive says, that it has Firmware Version AZT26801A1.3. It came with a ISA-bus +interface card and works with polled I/O without DMA and without interrupts. +The code for all other drives was 'remote' tested and debugged by a number of +volunteers on the Internet. + +Points, where I feel that possible problems might be and all points where I +did not completely understand the drive's behaviour or trust my own code are +marked with /*???*/ in the source code. There are also some parts in the +Mitsumi driver, where I did not completely understand their code. + + +10. ACKNOWLEDGEMENTS +Without the help of P.Bush, Aztech, who delivered technical information +about the Aztech Drive and without the help of E.Moenkeberg, GWDG, who did a +great job in analyzing the command structure of various CD-ROM drives, this +work would not have been possible. E.Moenkeberg was also a great help in +making the software 'kernel ready' and in answering many of the CDROM-related +questions in the newsgroups. He really is *the* Linux CD-ROM guru. Thanks +also to Ruediger Helsch, Unifix, and to all the guys on the Internet, who +collected valuable technical information about CDROMs. + +Joe Nardone (nardone@clark.net) was a patient tester even for my first +trial, which was more than slow, and made suggestions for code improvement. +Especially the 'finite state machine' azt_poll() was rewritten by Joe to get +clean C code and avoid the ugly 'gotos', which I copied from mcd.c. + +Robby Schirmer (schirmer@fmi.uni-passau.de) tested the audio stuff (ioctls) +and suggested a lot of patches for them. + +Joseph Piskor and Peter Nugent were the first users with the ORCHID CD3110 +and also were very patient with the problems, which occured. + +Anybody, who is interested in these items should have a look at 'ftp.gwdg.de', +directory 'pub/linux/cdrom' and at 'ftp.cdrom.com', directory 'pub/cdrom'. + +11. PROGRAMMING ADD ONs: cdplay.c +You can use the ioctl-functions included in aztcd.c in your own programs. As +an example on how to do this, you will find a tiny CD Player for audio CDs +named 'cdplay.c'. It allows you to play audio CDs. You can play a specified +track, pause and resume or skip tracks forward and backwards. If you quit the +program without stopping the drive, playing is continued. You can also +(mis)use cdplay to read and hexdump data disks. +'cdplay.c' can be found as a separate file in package AZTECH.CDROM.Vxx.tgz. +If you don't have it, you can find the code in the APPENDIX of this file, +which you should cut out with an editor and store in a separate file +'cdplay.c'. To compile it and make it executable, do + gcc -s -Wall -O2 -L/usr/lib cdplay.c -o /usr/local/bin/cdplay # compiles it + chmod +755 /usr/local/bin/cdplay # makes it executable + ln -s /dev/aztcd0 /dev/cdrom # creates a link + (for /usr/lib substitute the top level directory, where your include files + reside, and for /usr/local/bin the directory, where you want the executable + binary to reside ) + +You have to set the correct permissions for cdplay *and* for /dev/mcd0 or +/dev/aztcd0 in order to use it. Remember, that you should not have /dev/cdrom +mounted, when you're playing audio CDs. + +This program is just a hack for testing the ioctl-functions in aztcd.c, I will +not maintain it, so if you run into problems, discard it or have a look into +the source code 'cdplay.c'. The program does only contain a minimum of user +protection and input error detection. If you use the commands in the wrong +order or if you try to read a CD at wrong addresses, you may get error messages +or even hang your machine. If you get STEN_LOW, STEN_LOW_WAIT or segment violation +error messages when using cdplay, after that, the system might not be stable +any more, so you'd better reboot. As the ioctl-functions run in kernel mode, +most normal Linux-multitasking protection features do not work. By using +uninitialized 'wild' pointers etc., it is easy to write to other users data and +program areas, destroy kernel tables etc.. So if you experiment with ioctls +as always when you are doing systems programming and kernel hacking, you +should have a backup copy of your system in a safe place (and you also +should try before, how to restore from a backup copy)! + + +Werner Zimmermann +Fachhochschule fuer Technik Esslingen +(EMail: zimmerma@rz.fht-esslingen.de) +Jan. 8, 1994 + +--------------------------------------------------------------------------- +APPENDIX: Source code of cdplay.c + +/* Tiny Audio CD Player + + Copyright 1994, 1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de) + +This programm originally was written to test the audio functions of the +AZTECH.CDROM-driver, but it should work with every CD-ROM drive. Before +using it, you should set a symlink from /dev/cdrom to your real CDROM +device. + +The GNU General Public License applies to this program. + +History: V0.1 W.Zimmermann: First release. Nov. 8, 1994 + V0.2 W.Zimmermann: Enhanced functionality. Nov. 9, 1994 + V0.3 W.Zimmermann: Additional functions. Nov. 28, 1994 + V0.4 W.Zimmermann: fixed some bugs. Dec. 17, 1994 + V0.5 W.Zimmermann: clean 'scanf' commands without compiler warnings + Jan. 6, 1995 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +void help(void) +{ printf("Available Commands: STOP s EJECT e QUIT q\n"); + 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"); +} + +int main(void) +{ int handle; + unsigned char command=' ', ini=0, first=1, last=1; + unsigned int cmd, i,j,k, arg1,arg2,arg3; + struct cdrom_ti ti; + struct cdrom_tochdr tocHdr; + struct cdrom_subchnl subchnl; + struct cdrom_tocentry entry; + struct cdrom_msf msf; + union { struct cdrom_msf msf; + unsigned char buf[2336]; + } azt; + + printf("\nMini-Audio CD-Player V0.5 (C) 1994 W.Zimmermann\n"); + handle=open("/dev/cdrom",O_RDWR); + ioctl(handle,CDROMRESUME); + + if (handle<=0) + { printf("Drive Error: already playing, no audio disk, door open\n"); + printf(" or no permission (you must be ROOT in order to use this program)\n"); + } + else + { help(); + while (1) + { printf("Type command (h = help): "); + scanf("%s",&command); + switch (command) + { case 'e': cmd=CDROMEJECT; + ioctl(handle,cmd); + break; + case 'p': if (!ini) + { printf("Command not allowed - play track first\n"); + } + else + { cmd=CDROMPAUSE; + if (ioctl(handle,cmd)) printf("Drive Error\n"); + } + break; + case 'r': if (!ini) + { printf("Command not allowed - play track first\n"); + } + else + { cmd=CDROMRESUME; + if (ioctl(handle,cmd)) printf("Drive Error\n"); + } + break; + case 's': cmd=CDROMPAUSE; + if (ioctl(handle,cmd)) printf("Drive error or already stopped\n"); + ini=0; + cmd=CDROMSTOP; + if (ioctl(handle,cmd)) printf("Drive error\n"); + ini=0; + break; + case 't': cmd=CDROMREADTOCHDR; + if (ioctl(handle,cmd,&tocHdr)) printf("Drive Error\n"); + first=tocHdr.cdth_trk0; + last= tocHdr.cdth_trk1; + if ((first==0)||(first>last)) + { printf ("--could not read TOC\n"); + } + else + { printf("--first track: %d --last track: %d --enter track number: ",first,last); + cmd=CDROMPLAYTRKIND; + scanf("%i",&arg1); + ti.cdti_trk0=arg1; + if (ti.cdti_trk0last) ti.cdti_trk0=last; + ti.cdti_ind0=0; + ti.cdti_trk1=last; + ti.cdti_ind1=0; + if (ioctl(handle,cmd,&ti)) printf("Drive Error\n"); + ini=1; + } + break; + case 'n': if (!ini++) + { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n"); + first=tocHdr.cdth_trk0; + last= tocHdr.cdth_trk1; + ti.cdti_trk0=first-1; + } + if ((first==0)||(first>last)) + { printf ("--could not read TOC\n"); + } + else + { cmd=CDROMPLAYTRKIND; + if (++ti.cdti_trk0 > last) ti.cdti_trk0=last; + ti.cdti_ind0=0; + ti.cdti_trk1=last; + ti.cdti_ind1=0; + if (ioctl(handle,cmd,&ti)) printf("Drive Error\n"); + ini=1; + } + break; + case 'l': if (!ini++) + { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n"); + first=tocHdr.cdth_trk0; + last= tocHdr.cdth_trk1; + ti.cdti_trk0=first+1; + } + if ((first==0)||(first>last)) + { printf ("--could not read TOC\n"); + } + else + { cmd=CDROMPLAYTRKIND; + if (--ti.cdti_trk0 < first) ti.cdti_trk0=first; + ti.cdti_ind0=0; + ti.cdti_trk1=last; + ti.cdti_ind1=0; + if (ioctl(handle,cmd,&ti)) printf("Drive Error\n"); + ini=1; + } + break; + case 'c': subchnl.cdsc_format=CDROM_MSF; + if (ioctl(handle,CDROMSUBCHNL,&subchnl)) + printf("Drive Error\n"); + else + { printf("AudioStatus:%s Track:%d Mode:%d MSF=%d:%d:%d\n", \ + subchnl.cdsc_audiostatus==CDROM_AUDIO_PLAY ? "PLAYING":"NOT PLAYING",\ + subchnl.cdsc_trk,subchnl.cdsc_adr, \ + subchnl.cdsc_absaddr.msf.minute, subchnl.cdsc_absaddr.msf.second, \ + subchnl.cdsc_absaddr.msf.frame); + } + break; + case 'i': if (!ini) + { printf("Command not allowed - play track first\n"); + } + else + { cmd=CDROMREADTOCENTRY; + printf("Track No.: "); + scanf("%d",&arg1); + entry.cdte_track=arg1; + if (entry.cdte_tracklast) entry.cdte_track=last; + entry.cdte_format=CDROM_MSF; + if (ioctl(handle,cmd,&entry)) + { printf("Drive error or invalid track no.\n"); + } + else + { printf("Mode %d Track, starts at %d:%d:%d\n", \ + entry.cdte_adr,entry.cdte_addr.msf.minute, \ + entry.cdte_addr.msf.second,entry.cdte_addr.msf.frame); + } + } + break; + case 'a': cmd=CDROMPLAYMSF; + printf("Adress (min:sec:frame) "); + scanf("%d:%d:%d",&arg1,&arg2,&arg3); + msf.cdmsf_min0 =arg1; + msf.cdmsf_sec0 =arg2; + msf.cdmsf_frame0=arg3; + if (msf.cdmsf_sec0 > 59) msf.cdmsf_sec0 =59; + if (msf.cdmsf_frame0> 74) msf.cdmsf_frame0=74; + msf.cdmsf_min1=60; + msf.cdmsf_sec1=00; + msf.cdmsf_frame1=00; + if (ioctl(handle,cmd,&msf)) + { printf("Drive error or invalid adress\n"); + } + break; +#ifdef AZT_PRIVATE_IOCTLS + case 'd': cmd=CDROMREADMODE1; + printf("Adress (min:sec:frame) "); + scanf("%d:%d:%d",&arg1,&arg2,&arg3); + azt.msf.cdmsf_min0 =arg1; + azt.msf.cdmsf_sec0 =arg2; + azt.msf.cdmsf_frame0=arg3; + if (azt.msf.cdmsf_sec0 > 59) azt.msf.cdmsf_sec0 =59; + if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74; + if (ioctl(handle,cmd,&azt.msf)) + { printf("Drive error or invalid adress\n"); + } + k=0; + getchar(); + for (i=0;i<128;i++) + { printf("%4d:",i*16); + for (j=0;j<16;j++) + { printf("%2x ",azt.buf[i*16+j]); + } + for (j=0;j<16;j++) + { if (isalnum(azt.buf[i*16+j])) + printf("%c",azt.buf[i*16+j]); + else + printf("."); + } + printf("\n"); + k++; + if (k>=20) + { printf("press ENTER to continue\n"); + getchar(); + k=0; + } + } + break; + case 'w': cmd=CDROMREADMODE2; + printf("Adress (min:sec:frame) "); + scanf("%d:%d:%d",&arg1,&arg2,&arg3); + azt.msf.cdmsf_min0 =arg1; + azt.msf.cdmsf_sec0 =arg2; + azt.msf.cdmsf_frame0=arg3; + if (azt.msf.cdmsf_sec0 > 59) azt.msf.cdmsf_sec0 =59; + if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74; + if (ioctl(handle,cmd,&azt)) + { printf("Drive error or invalid adress\n"); + } + k=0; + for (i=0;i<146;i++) + { printf("%4d:",i*16); + for (j=0;j<16;j++) + { printf("%2x ",azt.buf[i*16+j]); + } + for (j=0;j<16;j++) + { if (isalnum(azt.buf[i*16+j])) + printf("%c",azt.buf[i*16+j]); + else + printf("."); + } + printf("\n"); + k++; + if (k>=20) + { getchar(); + k=0; + } + } + break; +#endif + case 'q': if (close(handle)) printf("Drive Error: CLOSE\n"); + exit(0); + case 'h': help(); + break; + default: printf("unknown command\n"); + break; + } + } + } + return 0; +} + diff -u --recursive --new-file v1.1.82/linux/drivers/block/aztcd.c linux/drivers/block/aztcd.c --- v1.1.82/linux/drivers/block/aztcd.c Thu Jan 1 02:00:00 1970 +++ linux/drivers/block/aztcd.c Wed Jan 18 21:17:50 1995 @@ -0,0 +1,1580 @@ +#define AZT_VERSION "V0.72" +/* $Id: aztcd.c,v 0.72 1995/01/13 15:21:09 root Exp root $ + 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. + + 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 + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + HISTORY + V0.0 Adaption to Adaptec CD268-01A Version 1.3 + Version is PRE_ALPHA, unresolved points: + 1. I use busy wait instead of timer wait in STEN_LOW,DTEN_LOW + thus driver causes CPU overhead and is very slow + 2. could not find a way to stop the drive, when it is + in data read mode, therefore I had to set + msf.end.min/sec/frame to 0:0:1 (in azt_poll); so only one + frame can be read in sequence, this is also the reason for + 3. getting 'timeout in state 4' messages, but nevertheless + it works + W.Zimmermann, Okt. 31, 1994 + V0.1 Version is ALPHA, problems #2 and #3 resolved. + W.Zimmermann, Nov. 3, 1994 + V0.2 Modification to some comments, debugging aids for partial test + with Borland C under DOS eliminated. Timer interrupt wait + STEN_LOW_WAIT additionally to busy wait for STEN_LOW implemented; + use it only for the 'slow' commands (ACMD_GET_Q_CHANNEL, ACMD_ + SEEK_TO_LEAD_IN), all other commands are so 'fast', that busy + waiting seems better to me than interrupt rescheduling. + Besides that, when used in the wrong place, STEN_LOW_WAIT causes + kernel panic. + In function aztPlay command ACMD_PLAY_AUDIO added, should make + audio functions work. The Aztech drive needs different commands + to read data tracks and play audio tracks. + W.Zimmermann, Nov. 8, 1994 + V0.3 Recognition of missing drive during boot up improved (speeded up). + W.Zimmermann, Nov. 13, 1994 + V0.35 Rewrote the control mechanism in azt_poll (formerly mcd_poll) + including removal of all 'goto' commands. :-); + J. Nardone, Nov. 14, 1994 + V0.4 Renamed variables and constants to 'azt' instead of 'mcd'; had + to make some "compatibility" defines in azt.h; please note, + that the source file was renamed to azt.c, the include file to + azt.h + Speeded up drive recognition during init (will be a little bit + slower than before if no drive is installed!); suggested by + Robby Schirmer. + read_count declared volatile and set to AZT_BUF_SIZ to make + drive faster (now 300kB/sec, was 60kB/sec before, measured + by 'time dd if=/dev/cdrom of=/dev/null bs=2048 count=4096'; + different AZT_BUF_SIZes were test, above 16 no further im- + provement seems to be possible; suggested by E.Moenkeberg. + W.Zimmermann, Nov. 18, 1994 + V0.42 Included getAztStatus command in GetQChannelInfo() to allow + reading Q-channel info on audio disks, if drive is stopped, + and some other bug fixes in the audio stuff, suggestet by + Robby Schirmer. + Added more ioctls (reading data in mode 1 and mode 2). + Completely removed the old azt_poll() routine. + Detection of ORCHID CDS-3110 in aztcd_init implemented. + Additional debugging aids (see the readme file). + W.Zimmermann, Dez. 9, 1994 + V0.50 Autodetection of drives inplemented. + W.Zimmermann, Dez. 12, 1994 + V0.52 Prepared for including in the standard kernel, renamed most + variables to contain 'azt', included autoconf.h + W.Zimmermann, Dez. 16, 1994 + V0.6 Version for being included in the standard Linux kernel. + Renamed source and header file to aztcd.c and aztcd.h + W.Zimmermann, Dez. 24, 1994 + V0.7 Changed VERIFY_READ to VERIFY_WRITE in aztcd_ioctl, case + CDROMREADMODE1 and CDROMREADMODE2; bug fix in the ioctl, + which causes kernel crashs, when playing audio, changed + include-files (config.h instead of autoconf.h, removed + delay.h) + W.Zimmermann, Jan. 8, 1995 + V0.72 Some more modifications for adaption to the standard kernel. + W.Zimmermann, Jan. 16, 1995 + + NOTE: + Points marked with ??? are questionable ! +*/ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_AZTCD +#define MAJOR_NR AZTECH_CDROM_MAJOR +#else +#define MAJOR_NR MITSUMI_CDROM_MAJOR /*use Mitsumi major number, if Aztech*/ +#endif /*major number is not configured*/ + +#include "blk.h" +#include + +static int aztPresent = 0; + +#if 0 +#define AZT_TEST1 /* */ +#define AZT_TEST2 /* do_aztcd_request */ +#define AZT_TEST3 /* AZT_S_state */ +#define AZT_TEST4 /* QUICK_LOOP-counter */ +#define AZT_TEST5 /* port(1) state */ +#define AZT_DEBUG +#define AZT_PRIVATE_IOCTLS /*incompatible ioctls*/ +#endif + +#define CURRENT_VALID \ + (CURRENT && MAJOR(CURRENT -> dev) == MAJOR_NR && CURRENT -> cmd == READ \ + && CURRENT -> sector != -1) + +#define AFL_STATUSorDATA (AFL_STATUS | AFL_DATA) +#define AZT_BUF_SIZ 16 + +static volatile int azt_transfer_is_active=0; + +static char azt_buf[2048*AZT_BUF_SIZ]; /*buffer for block size conversion*/ +#ifdef AZT_PRIVATE_IOCTLS +static char buf[2336]; /*separate buffer for the ioctls*/ +#endif + +static volatile int azt_buf_bn[AZT_BUF_SIZ], azt_next_bn; +static volatile int azt_buf_in, azt_buf_out = -1; +static volatile int azt_error=0; +static int azt_open_count=0; +enum azt_state_e { + AZT_S_IDLE, /* 0 */ + AZT_S_START, /* 1 */ + AZT_S_MODE, /* 2 */ + AZT_S_READ, /* 3 */ + AZT_S_DATA, /* 4 */ + AZT_S_STOP, /* 5 */ + AZT_S_STOPPING /* 6 */ +}; +static volatile enum azt_state_e azt_state = AZT_S_IDLE; +#ifdef AZT_TEST3 +static volatile enum azt_state_e azt_state_old = AZT_S_STOP; +static volatile int azt_st_old = 0; +#endif + +static int azt_mode = -1; +static int ACMD_DATA_READ= ACMD_PLAY_READ; +static volatile int azt_read_count = 1; + +#define READ_TIMEOUT 3000 + +static short azt_port = AZT_BASE_ADDR; +static char azt_cont = 0; +static char azt_init_end = 0; + +static int AztTimeout, AztTries; +static struct wait_queue *azt_waitq = NULL; + +static struct azt_DiskInfo DiskInfo; +static struct azt_Toc Toc[MAX_TRACKS]; +static struct azt_Play_msf azt_Play; + +static int aztAudioStatus = CDROM_AUDIO_NO_STATUS; +static char aztDiskChanged = 1; +static char aztTocUpToDate = 0; + + +static void azt_transfer(void); +static void azt_poll(void); +static void azt_invalidate_buffers(void); +static void do_aztcd_request(void); +static void azt_hsg2msf(long hsg, struct msf *msf); +static void azt_bin2bcd(unsigned char *p); +static int azt_bcd2bin(unsigned char bcd); +static int aztStatus(void); +static int getAztStatus(void); +static int aztSendCmd(int cmd); +static int sendAztCmd(int cmd, struct azt_Play_msf *params); +static int aztGetQChannelInfo(struct azt_Toc *qp); +static int aztUpdateToc(void); +static int aztGetDiskInfo(void); +static int aztGetToc(void); +static int aztGetValue(unsigned char *result); +static void aztStatTimer(void); + +static unsigned char aztIndatum; +static unsigned long aztTimeOutCount; + +/* Macros for the drive hardware interface handshake, these macros use + busy waiting */ +/* Wait for OP_OK = drive answers with AFL_OP_OK after recieving a command*/ +# define OP_OK op_ok() +void op_ok(void) +{ aztTimeOutCount=0; + do { aztIndatum=inb(DATA_PORT); + aztTimeOutCount++; + if (aztTimeOutCount>=AZT_TIMEOUT) + { printk("aztcd: Error Wait OP_OK\n"); + break; + } + } while (aztIndatum!=AFL_OP_OK); +} + +/* Wait for PA_OK = drive answers with AFL_PA_OK after recieving parameters*/ +# define PA_OK pa_ok() +void pa_ok(void) +{ aztTimeOutCount=0; + do { aztIndatum=inb(DATA_PORT); + aztTimeOutCount++; + if (aztTimeOutCount>=AZT_TIMEOUT) + { printk("aztcd: Error Wait PA_OK\n"); + break; + } + } while (aztIndatum!=AFL_PA_OK); +} + +/* Wait for STEN=Low = handshake signal 'AFL_.._OK available or command executed*/ +# define STEN_LOW sten_low() +void sten_low(void) +{ aztTimeOutCount=0; + do { aztIndatum=inb(STATUS_PORT); + aztTimeOutCount++; + if (aztTimeOutCount>=AZT_TIMEOUT) + { if (azt_init_end) printk("aztcd: Error Wait STEN_LOW\n"); + break; + } + } while (aztIndatum&AFL_STATUS); +} + +/* Wait for DTEN=Low = handshake signal 'Data available'*/ +# define DTEN_LOW dten_low() +void dten_low(void) +{ aztTimeOutCount=0; + do { aztIndatum=inb(STATUS_PORT); + aztTimeOutCount++; + if (aztTimeOutCount>=AZT_TIMEOUT) + { printk("aztcd: Error Wait DTEN_OK\n"); + break; + } + } while (aztIndatum&AFL_DATA); +} + +/* + * Macro for timer wait on STEN=Low, should only be used for 'slow' commands; + * may cause kernel panic when used in the wrong place +*/ +#define STEN_LOW_WAIT statusAzt() +void statusAzt(void) +{ AztTimeout = AZT_STATUS_DELAY; + SET_TIMER(aztStatTimer, 1); + sleep_on(&azt_waitq); + if (AztTimeout <= 0) printk("aztcd: Error Wait STEN_LOW_WAIT\n"); + return; +} + +static void aztStatTimer(void) +{ if (!(inb(STATUS_PORT) & AFL_STATUS)) + { wake_up(&azt_waitq); + return; + } + AztTimeout--; + if (AztTimeout <= 0) + { wake_up(&azt_waitq); + return; + } + SET_TIMER(aztStatTimer, 1); +} + + +void aztcd_setup(char *str, int *ints) +{ if (ints[0] > 0) + azt_port = ints[1]; + if (ints[0] > 1) + azt_cont = ints[2]; +} + +/* + * Send a single command, return -1 on error, else 0 +*/ +static int aztSendCmd(int cmd) +{ unsigned char data; + int retry; + +#ifdef AZT_DEBUG + printk("aztcd: Executing command %x\n",cmd); +#endif + outb(POLLED,MODE_PORT); + do { if (inb(STATUS_PORT)&AFL_STATUS) break; + inb(DATA_PORT); /* if status left from last command, read and */ + } while (1); /* discard it */ + do { if (inb(STATUS_PORT)&AFL_DATA) break; + inb(DATA_PORT); /* if data left from last command, read and */ + } while (1); /* discard it */ + for (retry=0;retry=AZT_RETRY_ATTEMPTS) + { printk("### Error 2 aztcd: aztSendCmd %x \n",cmd); + azt_error=0xA5; + } + return -1; +} + +/* + * Send a play or read command to the drive, return -1 on error, else 0 +*/ +static int sendAztCmd(int cmd, struct azt_Play_msf *params) +{ unsigned char data; + int retry; + +#ifdef AZT_DEBUG + printk("start=%02x:%02x:%02x end=%02x:%02x:%02x\n", \ + params->start.min, params->start.sec, params->start.frame, \ + params->end.min, params->end.sec, params->end.frame); +#endif + for (retry=0;retry start.min,CMD_PORT); + outb(params -> start.sec,CMD_PORT); + outb(params -> start.frame,CMD_PORT); + outb(params -> end.min,CMD_PORT); + outb(params -> end.sec,CMD_PORT); + outb(params -> end.frame,CMD_PORT); + STEN_LOW; + data=inb(DATA_PORT); + if (data==AFL_PA_OK) + { return 0;} /*PA_OK ?*/ + if (data==AFL_PA_ERR) + { STEN_LOW; + data=inb(DATA_PORT); + printk("### Error 1 aztcd: sendAztCmd %x Error Code %x\n",cmd,data); + } + } + if (retry>=AZT_RETRY_ATTEMPTS) + { printk("### Error 2 aztcd: sendAztCmd %x\n ",cmd); + azt_error=0xA5; + } + return -1; +} + + +/* + * Checking if the media has been changed not yet implemented +*/ +static int check_aztcd_media_change(dev_t full_dev) +{ return 0; +} + + +/* used in azt_poll to poll the status, expects another programm to issue a + * ACMD_GET_STATUS directly before + */ +static int aztStatus(void) +{ int st; + int i; + + i = inb(STATUS_PORT) & AFL_STATUS; /* is STEN=0? ???*/ + if (!i) + { + st = inb(DATA_PORT) & 0xFF; + return st; + } + else + return -1; +} + +/* + * Get the drive status + */ +static int getAztStatus(void) +{ int st; + + if (aztSendCmd(ACMD_GET_STATUS)) return -1; + STEN_LOW; + st = inb(DATA_PORT) & 0xFF; +#ifdef AZT_DEBUG + printk("aztcd: Status = %x\n",st); +#endif + if ((st == 0xFF)||(st&AST_CMD_CHECK)) + { printk("aztcd: AST_CMD_CHECK error or no status available\n"); + return -1; + } + + if (((st&AST_MODE_BITS)!=AST_BUSY) && (aztAudioStatus == CDROM_AUDIO_PLAY)) + /* XXX might be an error? look at q-channel? */ + aztAudioStatus = CDROM_AUDIO_COMPLETED; + + if (st & AST_DSK_CHG) + { + aztDiskChanged = 1; + aztTocUpToDate = 0; + aztAudioStatus = CDROM_AUDIO_NO_STATUS; + } + return st; +} + + +/* + * Send a 'Play' command and get the status. Use only from the top half. + */ +static int aztPlay(struct azt_Play_msf *arg) +{ if (sendAztCmd(ACMD_PLAY_AUDIO, arg) < 0) return -1; + return 0; +} + + +long azt_msf2hsg(struct msf *mp) +{ +#ifdef AZT_DEBUG + if (mp->min >=70) printk("aztcd: Error msf2hsg address Minutes\n"); + if (mp->sec >=60) printk("aztcd: Error msf2hsg address Seconds\n"); + if (mp->frame>=75) printk("aztcd: Error msf2hsg address Frames\n"); +#endif + return azt_bcd2bin(mp -> frame) + + azt_bcd2bin(mp -> sec) * 75 + + azt_bcd2bin(mp -> min) * 4500 + - 150; +} + +static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg) +{ int i, st; + struct azt_Toc qInfo; + struct cdrom_ti ti; + struct cdrom_tochdr tocHdr; + struct cdrom_msf msf; + struct cdrom_tocentry entry; + struct azt_Toc *tocPtr; + struct cdrom_subchnl subchnl; + +#ifdef AZT_DEBUG + printk("aztcd: starting aztcd_ioctl - Command:%x\n",cmd); +#endif + if (!ip) return -EINVAL; + if (getAztStatus()<0) return -EIO; + if (!aztTocUpToDate) + { if ((i=aztUpdateToc())<0) return i; /* error reading TOC */ + } + + 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. + */ + break; + case CDROMSTOP: /* Spin down the drive */ + if (aztSendCmd(ACMD_STOP)) return -1; + STEN_LOW_WAIT; + /* should we do anything if it fails? */ + aztAudioStatus = CDROM_AUDIO_NO_STATUS; + break; + case CDROMPAUSE: /* Pause the drive */ +/* if (aztAudioStatus != CDROM_AUDIO_PLAY) return -EINVAL; +*/ + if (aztGetQChannelInfo(&qInfo) < 0) + { /* didn't get q channel info */ + aztAudioStatus = CDROM_AUDIO_NO_STATUS; + return 0; + } + azt_Play.start = qInfo.diskTime; /* remember restart point */ + if (aztSendCmd(ACMD_PAUSE)) return -1; + STEN_LOW_WAIT; + aztAudioStatus = CDROM_AUDIO_PAUSED; + break; + case CDROMRESUME: /* Play it again, Sam */ + if (aztAudioStatus != CDROM_AUDIO_PAUSED) return -EINVAL; + /* restart the drive at the saved position. */ + i = aztPlay(&azt_Play); + if (i < 0) + { aztAudioStatus = CDROM_AUDIO_ERROR; + return -EIO; + } + aztAudioStatus = CDROM_AUDIO_PLAY; + break; + case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ + st = verify_area(VERIFY_READ, (void *) arg, sizeof ti); + if (st) return st; + memcpy_fromfs(&ti, (void *) arg, sizeof ti); + if (ti.cdti_trk0 < DiskInfo.first + || ti.cdti_trk0 > DiskInfo.last + || ti.cdti_trk1 < ti.cdti_trk0) + { return -EINVAL; + } + if (ti.cdti_trk1 > DiskInfo.last) + ti. cdti_trk1 = DiskInfo.last; + azt_Play.start = Toc[ti.cdti_trk0].diskTime; + azt_Play.end = Toc[ti.cdti_trk1 + 1].diskTime; +#ifdef AZT_DEBUG +printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n", + azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame, + azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame); +#endif + i = aztPlay(&azt_Play); + if (i < 0) + { aztAudioStatus = CDROM_AUDIO_ERROR; + return -EIO; + } + aztAudioStatus = CDROM_AUDIO_PLAY; + break; + case CDROMPLAYMSF: /* Play starting at the given MSF address. */ +/* if (aztAudioStatus == CDROM_AUDIO_PLAY) + { if (aztSendCmd(ACMD_STOP)) return -1; + STEN_LOW; + aztAudioStatus = CDROM_AUDIO_NO_STATUS; + } +*/ + st = verify_area(VERIFY_READ, (void *) arg, sizeof msf); + if (st) return st; + memcpy_fromfs(&msf, (void *) arg, sizeof msf); + /* convert to bcd */ + azt_bin2bcd(&msf.cdmsf_min0); + azt_bin2bcd(&msf.cdmsf_sec0); + azt_bin2bcd(&msf.cdmsf_frame0); + azt_bin2bcd(&msf.cdmsf_min1); + azt_bin2bcd(&msf.cdmsf_sec1); + azt_bin2bcd(&msf.cdmsf_frame1); + azt_Play.start.min = msf.cdmsf_min0; + azt_Play.start.sec = msf.cdmsf_sec0; + azt_Play.start.frame = msf.cdmsf_frame0; + azt_Play.end.min = msf.cdmsf_min1; + azt_Play.end.sec = msf.cdmsf_sec1; + azt_Play.end.frame = msf.cdmsf_frame1; +#ifdef AZT_DEBUG +printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n", +azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame, +azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame); +#endif + i = aztPlay(&azt_Play); + if (i < 0) + { aztAudioStatus = CDROM_AUDIO_ERROR; + return -EIO; + } + aztAudioStatus = CDROM_AUDIO_PLAY; + break; + + case CDROMREADTOCHDR: /* Read the table of contents header */ + st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr); + if (st) return st; + tocHdr.cdth_trk0 = DiskInfo.first; + tocHdr.cdth_trk1 = DiskInfo.last; + memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr); + break; + case CDROMREADTOCENTRY: /* Read an entry in the table of contents */ + st = verify_area(VERIFY_READ, (void *) arg, sizeof entry); + if (st) return st; + st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry); + if (st) return st; + memcpy_fromfs(&entry, (void *) arg, sizeof entry); + if (!aztTocUpToDate) aztGetDiskInfo(); + if (entry.cdte_track == CDROM_LEADOUT) + tocPtr = &Toc[DiskInfo.last + 1]; /* ??? */ + else if (entry.cdte_track > DiskInfo.last + || entry.cdte_track < DiskInfo.first) + { return -EINVAL; + } + else + tocPtr = &Toc[entry.cdte_track]; + entry.cdte_adr = tocPtr -> ctrl_addr; + entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4; + if (entry.cdte_format == CDROM_LBA) + entry.cdte_addr.lba = azt_msf2hsg(&tocPtr -> diskTime); + else if (entry.cdte_format == CDROM_MSF) + { entry.cdte_addr.msf.minute = azt_bcd2bin(tocPtr -> diskTime.min); + entry.cdte_addr.msf.second = azt_bcd2bin(tocPtr -> diskTime.sec); + entry.cdte_addr.msf.frame = azt_bcd2bin(tocPtr -> diskTime.frame); + } + else + { return -EINVAL; + } + memcpy_tofs((void *) arg, &entry, sizeof entry); + break; + case CDROMSUBCHNL: /* Get subchannel info */ + st = verify_area(VERIFY_READ, (void *) arg, sizeof subchnl); + if (st) return st; + st = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl); + if (st) return st; + memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl); + if (aztGetQChannelInfo(&qInfo) < 0) + return -EIO; + subchnl.cdsc_audiostatus = aztAudioStatus; + subchnl.cdsc_adr = qInfo.ctrl_addr; + subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4; + subchnl.cdsc_trk = azt_bcd2bin(qInfo.track); + subchnl.cdsc_ind = azt_bcd2bin(qInfo.pointIndex); + if (subchnl.cdsc_format == CDROM_LBA) + { subchnl.cdsc_absaddr.lba = azt_msf2hsg(&qInfo.diskTime); + subchnl.cdsc_reladdr.lba = azt_msf2hsg(&qInfo.trackTime); + } + else if (subchnl.cdsc_format == CDROM_MSF) + { subchnl.cdsc_absaddr.msf.minute = azt_bcd2bin(qInfo.diskTime.min); + subchnl.cdsc_absaddr.msf.second = azt_bcd2bin(qInfo.diskTime.sec); + subchnl.cdsc_absaddr.msf.frame = azt_bcd2bin(qInfo.diskTime.frame); + subchnl.cdsc_reladdr.msf.minute = azt_bcd2bin(qInfo.trackTime.min); + subchnl.cdsc_reladdr.msf.second = azt_bcd2bin(qInfo.trackTime.sec); + subchnl.cdsc_reladdr.msf.frame = azt_bcd2bin(qInfo.trackTime.frame); + } + else + 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 CDROMEJECT: + /* all drives can atleast stop! */ + if (aztAudioStatus == CDROM_AUDIO_PLAY) + { if (aztSendCmd(ACMD_STOP)) return -1; + STEN_LOW_WAIT; + } + if (aztSendCmd(ACMD_EJECT)) return -1; + aztAudioStatus = CDROM_AUDIO_NO_STATUS; + break; + case CDROMREADMODE1: /*read data in mode 1 (2048 Bytes)*/ + case CDROMREADMODE2: /*read data in mode 2 (2336 Bytes)*/ +/*Take care, the following code is not compatible with other CD-ROM drivers, + use it at your own risk with cdplay.c. Normally it is not activated, as + AZT_PRIVATE_IOCTLS is not defined +*/ +#ifdef AZT_PRIVATE_IOCTLS + { st = verify_area(VERIFY_READ, (void *) arg, sizeof msf); + if (st) return st; + st = verify_area(VERIFY_WRITE, (void *) arg, sizeof buf); + if (st) return st; + memcpy_fromfs(&msf, (void *) arg, sizeof msf); + /* convert to bcd */ + azt_bin2bcd(&msf.cdmsf_min0); + azt_bin2bcd(&msf.cdmsf_sec0); + azt_bin2bcd(&msf.cdmsf_frame0); + msf.cdmsf_min1=0; + msf.cdmsf_sec1=0; + msf.cdmsf_frame1=1; /*read only one frame*/ + azt_Play.start.min = msf.cdmsf_min0; + azt_Play.start.sec = msf.cdmsf_sec0; + azt_Play.start.frame = msf.cdmsf_frame0; + azt_Play.end.min = msf.cdmsf_min1; + azt_Play.end.sec = msf.cdmsf_sec1; + azt_Play.end.frame = msf.cdmsf_frame1; + if (cmd==CDROMREADMODE1) + { sendAztCmd(ACMD_DATA_READ, &azt_Play); + DTEN_LOW; + insb(DATA_PORT,buf,2048); + memcpy_tofs((void *) arg, &buf, 2048); + } + else /*CDROMREADMODE2*/ + { sendAztCmd(ACMD_DATA_READ_RAW, &azt_Play); + DTEN_LOW; + insb(DATA_PORT,buf,2336); + memcpy_tofs((void *) arg, &buf, 2336); + } + } +#endif /*end of incompatible code*/ + break; + default: + return -EINVAL; + } +#ifdef AZT_DEBUG + printk("aztcd: exiting aztcd_ioctl\n"); +#endif + return 0; +} + + +/* + * Take care of the different block sizes between cdrom and Linux. + * When Linux gets variable block sizes this will probably go away. + */ +static void azt_transfer(void) +{ +#ifdef AZT_TEST + printk("aztcd: executing azt_transfer\n"); +#endif + if (CURRENT_VALID) { + while (CURRENT -> nr_sectors) { + int bn = CURRENT -> sector / 4; + int i; + for (i = 0; i < AZT_BUF_SIZ && azt_buf_bn[i] != bn; ++i) + ; + if (i < AZT_BUF_SIZ) { + int offs = (i * 4 + (CURRENT -> sector & 3)) * 512; + int nr_sectors = 4 - (CURRENT -> sector & 3); + if (azt_buf_out != i) { + azt_buf_out = i; + if (azt_buf_bn[i] != bn) { + azt_buf_out = -1; + continue; + } + } + if (nr_sectors > CURRENT -> nr_sectors) + nr_sectors = CURRENT -> nr_sectors; + memcpy(CURRENT -> buffer, azt_buf + offs, nr_sectors * 512); + CURRENT -> nr_sectors -= nr_sectors; + CURRENT -> sector += nr_sectors; + CURRENT -> buffer += nr_sectors * 512; + } else { + azt_buf_out = -1; + break; + } + } + } +} + + +static void do_aztcd_request(void) +{ +#ifdef AZT_TEST + printk(" do_aztcd_request(%ld+%ld)\n", CURRENT -> sector, CURRENT -> nr_sectors); +#endif + azt_transfer_is_active = 1; + while (CURRENT_VALID) { + if (CURRENT->bh) { + if (!CURRENT->bh->b_lock) + panic(DEVICE_NAME ": block not locked"); + } + azt_transfer(); + if (CURRENT -> nr_sectors == 0) { + end_request(1); + } else { + azt_buf_out = -1; /* Want to read a block not in buffer */ + if (azt_state == AZT_S_IDLE) { + if (!aztTocUpToDate) { + if (aztUpdateToc() < 0) { + while (CURRENT_VALID) + end_request(0); + break; + } + } + azt_state = AZT_S_START; + AztTries = 5; + SET_TIMER(azt_poll, 1); + } + break; + } + } + azt_transfer_is_active = 0; +#ifdef AZT_TEST2 + printk("azt_next_bn:%x azt_buf_in:%x azt_buf_out:%x azt_buf_bn:%x\n", \ + azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]); + printk(" do_aztcd_request ends\n"); +#endif + +} + +static void azt_poll(void) +{ + int st = 0; + int loop_ctl = 1; + int skip = 0; + + if (azt_error) { /* ???*/ + if (aztSendCmd(ACMD_GET_ERROR)) return; + STEN_LOW; + azt_error=inb(DATA_PORT)&0xFF; + printk("aztcd: I/O error 0x%02x\n", azt_error); + azt_invalidate_buffers(); +#ifdef WARN_IF_READ_FAILURE + if (AztTries == 5) + printk("aztcd: read of block %d failed - maybe audio disk?\n", azt_next_bn); +#endif + if (!AztTries--) { + printk("aztcd: read of block %d failed, maybe audio disk? Giving up\n", azt_next_bn); + if (azt_transfer_is_active) { + AztTries = 0; + loop_ctl = 0; + } + if (CURRENT_VALID) + end_request(0); + AztTries = 5; + } + azt_error = 0; + azt_state = AZT_S_STOP; + } + + while (loop_ctl) + { + loop_ctl = 0; /* each case must flip this back to 1 if we want + to come back up here */ + switch (azt_state) { + + case AZT_S_IDLE: +#ifdef AZT_TEST3 + if (azt_state!=azt_state_old) { + azt_state_old=azt_state; + printk("AZT_S_IDLE\n"); + } +#endif + return; + + case AZT_S_START: +#ifdef AZT_TEST3 + if (azt_state!=azt_state_old) { + azt_state_old=azt_state; + printk("AZT_S_START\n"); + } +#endif + + if(aztSendCmd(ACMD_GET_STATUS)) return; /*result will be checked by aztStatus() */ + azt_state = azt_mode == 1 ? AZT_S_READ : AZT_S_MODE; + AztTimeout = 3000; + break; + + case AZT_S_MODE: +#ifdef AZT_TEST3 + if (azt_state!=azt_state_old) { + azt_state_old=azt_state; + printk("AZT_S_MODE\n"); + } +#endif + if (!skip) { + if ((st = aztStatus()) != -1) { + if (st & AST_DSK_CHG) { + aztDiskChanged = 1; + aztTocUpToDate = 0; + azt_invalidate_buffers(); + } + } else break; + } + skip = 0; + + if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) { + aztDiskChanged = 1; + aztTocUpToDate = 0; + printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n"); + if (azt_transfer_is_active) { + azt_state = AZT_S_START; + loop_ctl = 1; /* goto immediately */ + break; + } + azt_state = AZT_S_IDLE; + while (CURRENT_VALID) + end_request(0); + return; + } + /*???*/ + if (aztSendCmd(ACMD_SET_MODE)) return; + outb(0x01, DATA_PORT); /*Mode 1*/ + PA_OK; + STEN_LOW; + if (aztSendCmd(ACMD_GET_STATUS)) return; + azt_mode = 1; + azt_state = AZT_S_READ; + AztTimeout = 3000; + + break; + + + case AZT_S_READ: +#ifdef AZT_TEST3 + if (azt_state!=azt_state_old) { + azt_state_old=azt_state; + printk("AZT_S_READ\n"); + } +#endif + if (!skip) { + if ((st = aztStatus()) != -1) { + if (st & AST_DSK_CHG) { + aztDiskChanged = 1; + aztTocUpToDate = 0; + azt_invalidate_buffers(); + } + } else break; + } + + skip = 0; + if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) { + aztDiskChanged = 1; + aztTocUpToDate = 0; + printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n"); + if (azt_transfer_is_active) { + azt_state = AZT_S_START; + loop_ctl = 1; + break; + } + azt_state = AZT_S_IDLE; + while (CURRENT_VALID) + end_request(0); + return; + } + + if (CURRENT_VALID) { + struct azt_Play_msf msf; + azt_next_bn = CURRENT -> sector / 4; + azt_hsg2msf(azt_next_bn, &msf.start); + azt_read_count=AZT_BUF_SIZ; /*??? fast, because we read ahead*/ +/* azt_read_count= CURRENT->nr_sectors; slow +*/ + msf.end.min = 0; + msf.end.sec = 0; + msf.end.frame = azt_read_count ;/*Mitsumi here reads 0xffffff sectors*/ +#ifdef AZT_TEST3 + printk("---reading msf-address %x:%x:%x %x:%x:%x\n",msf.start.min,msf.start.sec,msf.start.frame,msf.end.min,msf.end.sec,msf.end.frame); + printk("azt_next_bn:%x azt_buf_in:%x azt_buf_out:%x azt_buf_bn:%x\n", \ + azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]); +#endif + sendAztCmd(ACMD_DATA_READ, &msf); + azt_state = AZT_S_DATA; + AztTimeout = READ_TIMEOUT; + } else { + azt_state = AZT_S_STOP; + loop_ctl = 1; + break; + } + + break; + + + case AZT_S_DATA: +#ifdef AZT_TEST3 + if (azt_state!=azt_state_old) { + azt_state_old=azt_state; + printk("AZT_S_DATA\n"); + } +#endif + + st = inb(STATUS_PORT) & AFL_STATUSorDATA; /*???*/ + + switch (st) { + + case AFL_DATA: +#ifdef AZT_TEST3 + if (st!=azt_st_old) { + azt_st_old=st; + printk("---AFL_DATA st:%x\n",st); + } +#endif +#ifdef WARN_IF_READ_FAILURE + if (AztTries == 5) + printk("aztcd: read of block %d failed - maybe audio disk?\n", azt_next_bn); +#endif + if (!AztTries--) { + printk("aztcd: read of block %d failed, maybe audio disk ? Giving up\n", azt_next_bn); + if (azt_transfer_is_active) { + AztTries = 0; + break; + } + if (CURRENT_VALID) + end_request(0); + AztTries = 5; + } + azt_state = AZT_S_START; + AztTimeout = READ_TIMEOUT; + loop_ctl = 1; + break; + + case AFL_STATUSorDATA: +#ifdef AZT_TEST3 + if (st!=azt_st_old) { + azt_st_old=st; + printk("---AFL_STATUSorDATA st:%x\n",st); + } +#endif + break; + + default: +#ifdef AZT_TEST3 + if (st!=azt_st_old) { + azt_st_old=st; + printk("---default: st:%x\n",st); + } +#endif + AztTries = 5; + if (!CURRENT_VALID && azt_buf_in == azt_buf_out) { + azt_state = AZT_S_STOP; + loop_ctl = 1; + break; + } + if (azt_read_count<=0) + printk("aztcd: warning - try to read 0 frames\n"); + while (azt_read_count) /*??? fast read ahead loop*/ + { azt_buf_bn[azt_buf_in] = -1; + DTEN_LOW; /*??? unsolved problem, very + seldom we get timeouts + here, don't now the real + reason. With my drive this + sometimes also happens with + Aztech's original driver under + DOS. Is it a hardware bug? + I tried to recover from such + situations here. Zimmermann*/ + if (aztTimeOutCount>=AZT_TIMEOUT) + { printk("read_count:%d CURRENT->nr_sectors:%ld azt_buf_in:%d\n", azt_read_count,CURRENT->nr_sectors,azt_buf_in); + printk("azt_transfer_is_active:%x\n",azt_transfer_is_active); + azt_read_count=0; + azt_state = AZT_S_STOP; + loop_ctl = 1; + end_request(1); /*should we have here (1) or (0)? */ + } + else + { insb(DATA_PORT, azt_buf + 2048 * azt_buf_in, 2048); + azt_read_count--; +#ifdef AZT_TEST3 + printk("AZT_S_DATA; ---I've read data- read_count: %d\n",azt_read_count); + printk("azt_next_bn:%d azt_buf_in:%d azt_buf_out:%d azt_buf_bn:%d\n", \ + azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]); +#endif + azt_buf_bn[azt_buf_in] = azt_next_bn++; + if (azt_buf_out == -1) + azt_buf_out = azt_buf_in; + azt_buf_in = azt_buf_in + 1 == AZT_BUF_SIZ ? 0 : azt_buf_in + 1; + } + } + if (!azt_transfer_is_active) { + while (CURRENT_VALID) { + azt_transfer(); + if (CURRENT -> nr_sectors == 0) + end_request(1); + else + break; + } + } + + if (CURRENT_VALID + && (CURRENT -> sector / 4 < azt_next_bn || + CURRENT -> sector / 4 > azt_next_bn + AZT_BUF_SIZ)) { + azt_state = AZT_S_STOP; + loop_ctl = 1; + break; + } + AztTimeout = READ_TIMEOUT; + if (azt_read_count==0) { + azt_state = AZT_S_STOP; /*???*/ + loop_ctl = 1; + break; + } + break; + } + break; + + + case AZT_S_STOP: +#ifdef AZT_TEST3 + if (azt_state!=azt_state_old) { + azt_state_old=azt_state; + printk("AZT_S_STOP\n"); + } +#endif + if (azt_read_count!=0) printk("aztcd: discard data=%x frames\n",azt_read_count); /*???*/ + while (azt_read_count!=0) { + int i; + if ( !(inb(STATUS_PORT) & AFL_DATA) ) { + for (i=0; i<2048; i++) { + inb(DATA_PORT); + } + } + azt_read_count--; + } + if (aztSendCmd(ACMD_GET_STATUS)) return; + azt_state = AZT_S_STOPPING; + AztTimeout = 1000; + break; + + case AZT_S_STOPPING: +#ifdef AZT_TEST3 + if (azt_state!=azt_state_old) { + azt_state_old=azt_state; + printk("AZT_S_STOPPING\n"); + } +#endif + + if ((st = aztStatus()) == -1 && AztTimeout) + break; + + if ((st != -1) && (st & AST_DSK_CHG)) { + aztDiskChanged = 1; + aztTocUpToDate = 0; + azt_invalidate_buffers(); + } + + +#ifdef AZT_TEST3 + printk("CURRENT_VALID %d azt_mode %d\n", + CURRENT_VALID, azt_mode); +#endif + + if (CURRENT_VALID) { + if (st != -1) { + if (azt_mode == 1) { + azt_state = AZT_S_READ; + loop_ctl = 1; + skip = 1; + break; + } else { + azt_state = AZT_S_MODE; + loop_ctl = 1; + skip = 1; + break; + } + } else { + azt_state = AZT_S_START; + AztTimeout = 1; + } + } else { + azt_state = AZT_S_IDLE; + return; + } + break; + + default: + printk("aztcd: invalid state %d\n", azt_state); + return; + } /* case */ + } /* while */ + + + if (!AztTimeout--) + { printk("aztcd: timeout in state %d\n", azt_state); + azt_state = AZT_S_STOP; + if (aztSendCmd(ACMD_STOP)) return; + STEN_LOW_WAIT; + }; + + SET_TIMER(azt_poll, 1); +} + +static void azt_invalidate_buffers(void) +{ int i; + +#ifdef AZT_DEBUG + printk("aztcd: executing azt_invalidate_buffers\n"); +#endif + for (i = 0; i < AZT_BUF_SIZ; ++i) + azt_buf_bn[i] = -1; + azt_buf_out = -1; +} + +/* + * Open the device special file. Check that a disk is in. + */ +int aztcd_open(struct inode *ip, struct file *fp) +{ int st; + +#ifdef AZT_DEBUG + printk("aztcd: starting aztcd_open\n"); +#endif + if (aztPresent == 0) + return -ENXIO; /* no hardware */ + + if (!azt_open_count && azt_state == AZT_S_IDLE) { + + azt_invalidate_buffers(); + + st = getAztStatus(); /* check drive status */ + if (st == -1) + return -EIO; /* drive doesn't respond */ + + if ((st&AST_DOOR_OPEN)||(st&AST_NOT_READY)) /* no disk in drive or door open*/ + { /*???*/ + printk("aztcd: no disk in drive or door open\n"); + return -EIO; + } + + if (aztUpdateToc() < 0) + return -EIO; + + } + ++azt_open_count; +#ifdef AZT_DEBUG + printk("aztcd: exiting aztcd_open\n"); +#endif + return 0; +} + + +/* + * On close, we flush all azt blocks from the buffer cache. + */ +static void aztcd_release(struct inode * inode, struct file * file) +{ +#ifdef AZT_DEBUG + printk("aztcd: executing aztcd_release\n"); + printk("inode: %p, inode->i_rdev: %x file: %p\n",inode,inode->i_rdev,file); +#endif + if (!--azt_open_count) { + azt_invalidate_buffers(); + sync_dev(inode->i_rdev); /*??? isn't it a read only dev?*/ + invalidate_buffers(inode -> i_rdev); + } + return; +} + + +static struct file_operations azt_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + aztcd_ioctl, /* ioctl */ + NULL, /* mmap */ + aztcd_open, /* open */ + aztcd_release, /* release */ + NULL, /* fsync */ + NULL, /* fasync*/ + check_aztcd_media_change, /*media change*/ + NULL /* revalidate*/ +}; + +/* + * Test for presence of drive and initialize it. Called at boot time. + */ +unsigned long aztcd_init(unsigned long mem_start, unsigned long mem_end) +{ long int count, max_count; + unsigned char result[50]; + int st; + + if (azt_port <= 0) { + printk("aztcd: no Aztech CD-ROM Initialization"); + return (mem_start); + } + printk("Aztech CD-ROM Init: Aztech, Orchid, Okano, Wearnes CD-ROM Driver\n"); + printk("Aztech CD-ROM Init: (C) 1994,1995 Werner Zimmermann\n"); + printk("Aztech CD-ROM Init: DriverVersion=%s BaseAddress=0x%x \n",AZT_VERSION,azt_port); + + if (check_region(azt_port, 4)) { + printk("aztcd: conflict, I/O port (%X) already used\n", + azt_port); + return (mem_start); + } + + /* check for card */ + outb(POLLED,MODE_PORT); /*???*/ + inb(CMD_PORT); + inb(CMD_PORT); + outb(ACMD_GET_VERSION,CMD_PORT); /*Try to get version info*/ + STEN_LOW; + if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK? If not, reset and try again*/ + { printk("aztcd: drive reset - please wait\n"); + for (count=0;count<50;count++) + { inb(STATUS_PORT); /*removing all data from earlier tries*/ + inb(DATA_PORT); + } + outb(POLLED,MODE_PORT); /*???*/ + inb(CMD_PORT); + inb(CMD_PORT); + outb(ACMD_SOFT_RESET,CMD_PORT); /*send reset*/ + STEN_LOW; + if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK?*/ + { printk("aztcd: no AZTECH CD-ROM drive found\n"); + return (mem_start); + } + for (count = 0; count < AZT_TIMEOUT; count++); /* delay a bit */ + if ((st=getAztStatus())==-1) + { printk("aztcd: Drive Status Error Status=%x\n",st); + return (mem_start); + } +#ifdef AZT_DEBUG + printk("aztcd: Status = %x\n",st); +#endif + outb(POLLED,MODE_PORT); /*???*/ + inb(CMD_PORT); + inb(CMD_PORT); + outb(ACMD_GET_VERSION,CMD_PORT); /*GetVersion*/ + STEN_LOW; + OP_OK; + } + azt_init_end=1; + STEN_LOW; + result[0]=inb(DATA_PORT); /*reading in a null byte???*/ + for (count=1;count<50;count++) /*Reading version string*/ + { aztTimeOutCount=0; /*here we must implement STEN_LOW differently*/ + do { aztIndatum=inb(STATUS_PORT);/*because we want to exit by timeout*/ + aztTimeOutCount++; + if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; + } while (aztIndatum&AFL_STATUS); + if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; /*all chars read?*/ + result[count]=inb(DATA_PORT); + } + if (count>30) max_count=30; /*print max.30 chars of the version string*/ + else max_count=count; + printk("Aztech CD-ROM Init: FirmwareVersion="); + for (count=1;count min = hsg / 4500; + hsg %= 4500; + msf -> sec = hsg / 75; + msf -> frame = hsg % 75; +#ifdef AZT_DEBUG + if (msf->min >=70) printk("aztcd: Error hsg2msf address Minutes\n"); + if (msf->sec >=60) printk("aztcd: Error hsg2msf address Seconds\n"); + if (msf->frame>=75) printk("aztcd: Error hsg2msf address Frames\n"); +#endif + azt_bin2bcd(&msf -> min); /* convert to BCD */ + azt_bin2bcd(&msf -> sec); + azt_bin2bcd(&msf -> frame); +} + + +static void azt_bin2bcd(unsigned char *p) +{ int u, t; + + u = *p % 10; + t = *p / 10; + *p = u | (t << 4); +} + +static int azt_bcd2bin(unsigned char bcd) +{ return (bcd >> 4) * 10 + (bcd & 0xF); +} + + + +/* + * Read a value from the drive. Should return quickly, so a busy wait + * is used to avoid excessive rescheduling. The read command itself must + * be issued with aztSendCmd() directly before + */ +static int aztGetValue(unsigned char *result) +{ int s; + + STEN_LOW; + if (aztTimeOutCount>=AZT_TIMEOUT) + { printk("aztcd: aztGetValue timeout\n"); + return -1; + } + s = inb(DATA_PORT) & 0xFF; + *result = (unsigned char) s; + return 0; +} + + +/* + * Read the current Q-channel info. Also used for reading the + * table of contents. + */ +int aztGetQChannelInfo(struct azt_Toc *qp) +{ unsigned char notUsed; + int st; + +#ifdef AZT_DEBUG + printk("aztcd: starting aztGetQChannelInfo\n"); +#endif + if ((st=getAztStatus())==-1) return -1; + if (aztSendCmd(ACMD_GET_Q_CHANNEL)) return -1; + STEN_LOW_WAIT; + if (aztGetValue(¬Used) <0) return -1; /*Nullbyte ein-*/ + /*lesen ???*/ + if ((st&AST_MODE_BITS)==AST_INITIAL) + { qp->ctrl_addr=0; /* when audio stop ACMD_GET_Q_CHANNEL returns */ + qp->track=0; /* only one byte with Aztech drives */ + qp->pointIndex=0; + qp->trackTime.min=0; + qp->trackTime.sec=0; + qp->trackTime.frame=0; + qp->diskTime.min=0; + qp->diskTime.sec=0; + qp->diskTime.frame=0; + return 0; + } + else + { if (aztGetValue(&qp -> ctrl_addr) < 0) return -1; + if (aztGetValue(&qp -> track) < 0) return -1; + if (aztGetValue(&qp -> pointIndex) < 0) return -1; + if (aztGetValue(&qp -> trackTime.min) < 0) return -1; + if (aztGetValue(&qp -> trackTime.sec) < 0) return -1; + if (aztGetValue(&qp -> trackTime.frame) < 0) return -1; + if (aztGetValue(¬Used) < 0) return -1; + if (aztGetValue(&qp -> diskTime.min) < 0) return -1; + if (aztGetValue(&qp -> diskTime.sec) < 0) return -1; + if (aztGetValue(&qp -> diskTime.frame) < 0) return -1; + } +#ifdef AZT_DEBUG + printk("aztcd: exiting aztGetQChannelInfo\n"); +#endif + return 0; +} + +/* + * Read the table of contents (TOC) and TOC header if neccessary + */ +static int aztUpdateToc() +{ +#ifdef AZT_DEBUG + printk("aztcd: starting aztUpdateToc\n"); +#endif + if (aztTocUpToDate) + return 0; + + if (aztGetDiskInfo() < 0) + return -EIO; + + if (aztGetToc() < 0) + return -EIO; + + aztTocUpToDate = 1; +#ifdef AZT_DEBUG + printk("aztcd: exiting aztUpdateToc\n"); +#endif + return 0; +} + + +/* + * Read the table of contents header + */ +static int aztGetDiskInfo() +{ int limit; + unsigned char test; + struct azt_Toc qInfo; + +#ifdef AZT_DEBUG + printk("aztcd: starting aztGetDiskInfo\n"); +#endif + if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) return -1; + STEN_LOW_WAIT; + test=0; + for (limit=300;limit>0;limit--) + { if (aztGetQChannelInfo(&qInfo)<0) return -1; + if (qInfo.pointIndex==0xA0) /*Number of FirstTrack*/ + { DiskInfo.first=qInfo.diskTime.min; + DiskInfo.first = azt_bcd2bin(DiskInfo.first); + test=test|0x01; + } + if (qInfo.pointIndex==0xA1) /*Number of LastTrack*/ + { DiskInfo.last=qInfo.diskTime.min; + DiskInfo.last = azt_bcd2bin(DiskInfo.last); + test=test|0x02; + } + if (qInfo.pointIndex==0xA2) /*DiskLength*/ + { DiskInfo.diskLength.min=qInfo.diskTime.min; + DiskInfo.diskLength.sec=qInfo.diskTime.sec-2; + DiskInfo.diskLength.frame=qInfo.diskTime.frame; + test=test|0x04; + } + if ((qInfo.pointIndex==DiskInfo.first)&&(test&0x01)) /*StartTime of First Track*/ + { DiskInfo.firstTrack.min=qInfo.diskTime.min; + DiskInfo.firstTrack.sec=qInfo.diskTime.sec; + DiskInfo.firstTrack.frame=qInfo.diskTime.frame; + test=test|0x08; + } + if (test==0x0F) break; + } +#ifdef AZT_DEBUG +printk ("aztcd: exiting aztGetDiskInfo\n"); +printk("Disk Info: first %d last %d length %02x:%02x.%02x first %02x:%02x.%02x\n", + DiskInfo.first, + DiskInfo.last, + DiskInfo.diskLength.min, + DiskInfo.diskLength.sec, + DiskInfo.diskLength.frame, + DiskInfo.firstTrack.min, + DiskInfo.firstTrack.sec, + DiskInfo.firstTrack.frame); +#endif + if (test!=0x0F) return -1; + return 0; +} + + +/* + * Read the table of contents (TOC) + */ +static int aztGetToc() +{ int i, px; + int limit; + struct azt_Toc qInfo; + +#ifdef AZT_DEBUG + printk("aztcd: starting aztGetToc\n"); +#endif + for (i = 0; i < MAX_TRACKS; i++) + Toc[i].pointIndex = 0; + + i = DiskInfo.last + 3; + + if (aztSendCmd(ACMD_STOP)) return -1; + STEN_LOW_WAIT; + + azt_mode = 0x05; + if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) return -1; /*???*/ + STEN_LOW_WAIT; + + for (limit = 300; limit > 0; limit--) + { + if (aztGetQChannelInfo(&qInfo) < 0) + break; + + px = azt_bcd2bin(qInfo.pointIndex); + if (px > 0 && px < MAX_TRACKS && qInfo.track == 0) + if (Toc[px].pointIndex == 0) + { + Toc[px] = qInfo; + i--; + } + + if (i <= 0) + break; + } + + Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength; + +#ifdef AZT_DEBUG +printk("aztcd: exiting aztGetToc\n"); +for (i = 1; i <= DiskInfo.last+1; i++) +printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n", +i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, +Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, +Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); +for (i = 100; i < 103; i++) +printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n", +i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, +Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, +Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); +#endif + + return limit > 0 ? 0 : -1; +} + diff -u --recursive --new-file v1.1.82/linux/drivers/block/blk.h linux/drivers/block/blk.h --- v1.1.82/linux/drivers/block/blk.h Mon Jan 9 07:22:00 1995 +++ linux/drivers/block/blk.h Wed Jan 18 21:00:26 1995 @@ -44,6 +44,9 @@ extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end); extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end); +#ifdef CONFIG_AZTCD +extern unsigned long aztcd_init(unsigned long mem_start, unsigned long mem_end); +#endif #ifdef CONFIG_BLK_DEV_HD extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end); #endif @@ -166,6 +169,14 @@ #define DEVICE_NAME "Mitsumi CD-ROM" /* #define DEVICE_INTR do_mcd */ #define DEVICE_REQUEST do_mcd_request +#define DEVICE_NR(device) (MINOR(device)) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#elif (MAJOR_NR == AZTECH_CDROM_MAJOR) + +#define DEVICE_NAME "Aztech CD-ROM" +#define DEVICE_REQUEST do_aztcd_request #define DEVICE_NR(device) (MINOR(device)) #define DEVICE_ON(device) #define DEVICE_OFF(device) diff -u --recursive --new-file v1.1.82/linux/drivers/block/cdu31a.c linux/drivers/block/cdu31a.c --- v1.1.82/linux/drivers/block/cdu31a.c Mon Jan 16 14:18:15 1995 +++ linux/drivers/block/cdu31a.c Wed Jan 18 09:38:59 1995 @@ -78,16 +78,9 @@ * * Multi-Session * - * A multi-session disk is treated like a partitioned disk, each session - * has it's own minor device number, starting with 0. The support is - * pretty transparent, music, TOC operations, and read operations should - * all work transparently on any session. Note that since the driver - * writer doesn't have a multi-session disk, this is all theoretical. - * Also, music operation will obviously only work on one session at a - * time. - * - * NOTE: At the current time, multi-session still doesn't work. Maybe - * I'll get a multi-session disk soon so I can play with it. + * A multi-session disk looks just like a normal disk to the user. + * Just mount one normally, and all the data should be there. + * A special thanks to Koen for help with this! * * Raw sector I/O * @@ -226,7 +219,7 @@ static int handle_sony_cd_attention(void); static int read_subcode(void); -static void sony_get_toc(int dev); +static void sony_get_toc(void); static int scd_open(struct inode *inode, struct file *filp); static void do_sony_cd_cmd(unsigned char cmd, unsigned char *params, @@ -274,9 +267,12 @@ static int sony_pas_init = 0; /* Initialize the Pro-Audio Spectrum card? */ -static struct s_sony_session_toc *(ses_tocs[MAX_TRACKS]); /* Points to the - table of - contents. */ +static struct s_sony_session_toc *sony_toc; /* Points to the + table of + contents. */ + +static int sony_toc_read = 0; /* Has the TOC been read for + the drive? */ static struct s_sony_subcode * volatile last_sony_subcode; /* Points to the last subcode address read */ @@ -317,11 +313,10 @@ static int curr_control_reg = 0; /* Current value of the control register */ -/* A disk changed variable for every possible session. When a disk change - is detected, these will all be set to TRUE. As the upper layers ask - for disk_changed status for individual minor numbers, they will be - cleared. */ -static char disk_changed[MAX_TRACKS]; +/* A disk changed variable. When a disk change is detected, it will + all be set to TRUE. As the upper layers ask for disk_changed status + it will be cleared. */ +static char disk_changed; /* Variable for using the readahead buffer. The readahead buffer is used for raw sector reads and for blocksizes that are smaller @@ -338,18 +333,10 @@ static int scd_disk_change(dev_t full_dev) { - int retval, target; - - - target = MINOR(full_dev); - - if (target >= MAX_TRACKS) { - printk("Sony CD-ROM request error: invalid device.\n"); - return 0; - } + int retval; - retval = disk_changed[target]; - disk_changed[target] = 0; + retval = disk_changed; + disk_changed = 0; return retval; } @@ -859,7 +846,6 @@ { unsigned char atten_code; static int num_consecutive_attentions = 0; - int i; if (is_attention()) @@ -879,15 +865,8 @@ { /* Someone changed the CD. Mark it as changed */ case SONY_MECH_LOADED_ATTN: - for (i=0; i= ses_tocs[dev]->lead_out_start_lba) + else if ((sector + nsect) >= sony_toc->lead_out_start_lba) { - read_size = ses_tocs[dev]->lead_out_start_lba - sector; + read_size = sony_toc->lead_out_start_lba - sector; } /* Read the full readahead amount. */ else @@ -1374,7 +1352,6 @@ do_cdu31a_request(void) { int block; - unsigned int dev; int nblock; unsigned char res_reg[12]; unsigned int res_size; @@ -1440,36 +1417,24 @@ } } - dev = MINOR(CURRENT->dev); block = CURRENT->sector; nblock = CURRENT->nr_sectors; - /* Check for multi-session disks. */ - if (dev > MAX_TRACKS) - { - printk("CDU31A: Invalid device request: %d\n", dev); - end_request(0); - goto cdu31a_request_startover; - } - else if (ses_tocs[dev] == NULL) + if (!sony_toc_read) { - printk("CDU31A: session TOC not read: %d\n", dev); + printk("CDU31A: TOC not read\n"); end_request(0); goto cdu31a_request_startover; } - /* Check for base read of multi-session disk. */ - if ((dev != 0) && (block == 64)) + /* Check for base read of multi-session disk. This will still work + for single session disks, so just do it. Blocks less than 80 + are for the volume info, so offset them by the start track (which + should be zero for a single-session disk). */ + if (block < 80) { - if (ses_tocs[dev]->first_track_num == ses_tocs[dev]->last_track_num) - { - printk("CDU31A: Not a multi-session disk: %d\n", dev); - end_request(0); - goto cdu31a_request_startover; - } - /* Offset the request into the session. */ - block += (ses_tocs[dev]->start_track_lba * 4); + block += (sony_toc->start_track_lba * 4); } switch(CURRENT->cmd) @@ -1479,19 +1444,21 @@ * If the block address is invalid or the request goes beyond the end of * the media, return an error. */ - if ((block / 4) < ses_tocs[dev]->start_track_lba) +#if 0 + if ((block / 4) < sony_toc->start_track_lba) { printk("CDU31A: Request before beginning of media\n"); end_request(0); goto cdu31a_request_startover; } - if ((block / 4) >= ses_tocs[dev]->lead_out_start_lba) +#endif + if ((block / 4) >= sony_toc->lead_out_start_lba) { printk("CDU31A: Request past end of media\n"); end_request(0); goto cdu31a_request_startover; } - if (((block + nblock) / 4) >= ses_tocs[dev]->lead_out_start_lba) + if (((block + nblock) / 4) >= sony_toc->lead_out_start_lba) { printk("CDU31A: Request past end of media\n"); end_request(0); @@ -1504,9 +1471,9 @@ while (handle_sony_cd_attention()) ; - if (ses_tocs[dev] == NULL) + if (!sony_toc_read) { - printk("CDU31A: session TOC not read: %d\n", dev); + printk("CDU31A: TOC not read\n"); end_request(0); goto cdu31a_request_startover; } @@ -1515,7 +1482,7 @@ next request. */ if (sony_blocks_left == 0) { - if (start_request(block / 4, CDU31A_READAHEAD / 4, 0, dev)) + if (start_request(block / 4, CDU31A_READAHEAD / 4, 0)) { end_request(0); goto cdu31a_request_startover; @@ -1532,13 +1499,13 @@ sony_next_block); #endif abort_read(); - if (ses_tocs[dev] == NULL) + if (!sony_toc_read) { - printk("CDU31A: session TOC not read: %d\n", dev); + printk("CDU31A: TOC not read\n"); end_request(0); goto cdu31a_request_startover; } - else if (start_request(block / 4, CDU31A_READAHEAD / 4, 0, dev)) + if (start_request(block / 4, CDU31A_READAHEAD / 4, 0)) { end_request(0); goto cdu31a_request_startover; @@ -1581,7 +1548,7 @@ } end_do_cdu31a_request: -#if 0 +#if 1 /* After finished, cancel any pending operations. */ abort_read(); #endif @@ -1614,93 +1581,137 @@ * successful. */ static void -sony_get_toc(int dev) +sony_get_toc(void) { + unsigned char res_reg[2]; unsigned int res_size; unsigned char parms[1]; + int session; - if (dev >= MAX_TRACKS) - { - printk("CDU31A: Request for invalid TOC track: %d\n", dev); - } - else if (ses_tocs[dev] == NULL) +#if DEBUG + printk("Entering sony_get_toc\n"); +#endif + + if (!sony_toc_read) { - ses_tocs[dev] = kmalloc(sizeof(struct s_sony_session_toc), 0); - if (ses_tocs[dev] == NULL) - { - printk("CDU31A: Unable to alloc mem for TOC\n"); - } - else + /* The idea here is we keep asking for sessions until the command + fails. Then we know what the last valid session on the disk is. + No need to check session 0, since session 0 is the same as session + 1; the command returns different information if you give it 0. + Don't check session 1 because that is the first session, it must + be there. */ + session = 2; + while (1) { - parms[0] = dev+1; - do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD, +#if DEBUG + printk("Trying session %d\n", session); +#endif + parms[0] = session; + do_sony_cd_cmd(SONY_READ_TOC_SPEC_CMD, parms, 1, - (unsigned char *) ses_tocs[dev], + res_reg, &res_size); - if ((res_size < 2) || ((ses_tocs[dev]->exec_status[0] & 0xf0) == 0x20)) - { - kfree_s(ses_tocs[dev], sizeof(struct s_sony_session_toc)); - ses_tocs[dev] = NULL; - return; - } - /* For points that do not exist, move the data over them - to the right location. */ - if (ses_tocs[dev]->pointb0 != 0xb0) - { - mcovlp(((char *) ses_tocs[dev]) + 27, - ((char *) ses_tocs[dev]) + 18, - res_size - 18); - res_size += 9; - } - if (ses_tocs[dev]->pointb1 != 0xb1) - { - mcovlp(((char *) ses_tocs[dev]) + 36, - ((char *) ses_tocs[dev]) + 27, - res_size - 27); - res_size += 9; - } - if (ses_tocs[dev]->pointb2 != 0xb2) - { - mcovlp(((char *) ses_tocs[dev]) + 45, - ((char *) ses_tocs[dev]) + 36, - res_size - 36); - res_size += 9; - } - if (ses_tocs[dev]->pointb3 != 0xb3) - { - mcovlp(((char *) ses_tocs[dev]) + 54, - ((char *) ses_tocs[dev]) + 45, - res_size - 45); - res_size += 9; - } - if (ses_tocs[dev]->pointb4 != 0xb4) - { - mcovlp(((char *) ses_tocs[dev]) + 63, - ((char *) ses_tocs[dev]) + 54, - res_size - 54); - res_size += 9; +#if DEBUG + printk("%2.2x %2.2x\n", res_reg[0], res_reg[1]); +#endif + + if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) + { + /* An error reading the TOC, this must be past the last session. */ + break; } - if (ses_tocs[dev]->pointc0 != 0xc0) + + session++; + + /* Let's not get carried away... */ + if (session > 20) { - mcovlp(((char *) ses_tocs[dev]) + 72, - ((char *) ses_tocs[dev]) + 63, - res_size - 63); - res_size += 9; + return; } + } + + session--; - ses_tocs[dev]->start_track_lba = msf_to_log(ses_tocs[dev]->tracks[0].track_start_msf); - ses_tocs[dev]->lead_out_start_lba = msf_to_log(ses_tocs[dev]->lead_out_start_msf); #if DEBUG - printk("Disk session %d, start track: %d, stop track: %d\n", - dev, - ses_tocs[dev]->start_track_lba, - ses_tocs[dev]->lead_out_start_lba); + printk("Reading session %d\n", session); #endif + + parms[0] = session; + do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD, + parms, + 1, + (unsigned char *) sony_toc, + &res_size); + if ((res_size < 2) || ((sony_toc->exec_status[0] & 0xf0) == 0x20)) + { + /* An error reading the TOC. Return without sony_toc_read + set. */ + return; + } + + sony_toc_read = 1; + + /* For points that do not exist, move the data over them + to the right location. */ + if (sony_toc->pointb0 != 0xb0) + { + mcovlp(((char *) sony_toc) + 27, + ((char *) sony_toc) + 18, + res_size - 18); + res_size += 9; + } + if (sony_toc->pointb1 != 0xb1) + { + mcovlp(((char *) sony_toc) + 36, + ((char *) sony_toc) + 27, + res_size - 27); + res_size += 9; } + if (sony_toc->pointb2 != 0xb2) + { + mcovlp(((char *) sony_toc) + 45, + ((char *) sony_toc) + 36, + res_size - 36); + res_size += 9; + } + if (sony_toc->pointb3 != 0xb3) + { + mcovlp(((char *) sony_toc) + 54, + ((char *) sony_toc) + 45, + res_size - 45); + res_size += 9; + } + if (sony_toc->pointb4 != 0xb4) + { + mcovlp(((char *) sony_toc) + 63, + ((char *) sony_toc) + 54, + res_size - 54); + res_size += 9; + } + if (sony_toc->pointc0 != 0xc0) + { + mcovlp(((char *) sony_toc) + 72, + ((char *) sony_toc) + 63, + res_size - 63); + res_size += 9; + } + + sony_toc->start_track_lba = msf_to_log(sony_toc->tracks[0].track_start_msf); + sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf); + +#if DEBUG + printk("Disk session %d, start track: %d, stop track: %d\n", + session, + sony_toc->start_track_lba, + sony_toc->lead_out_start_lba); +#endif } +#if DEBUG + printk("Leaving sony_get_toc\n"); +#endif } @@ -1708,17 +1719,16 @@ * Search for a specific track in the table of contents. */ static int -find_track(int track, - int dev) +find_track(int track) { int i; int num_tracks; - num_tracks = ses_tocs[dev]->last_track_num - ses_tocs[dev]->first_track_num + 1; + num_tracks = sony_toc->last_track_num - sony_toc->first_track_num + 1; for (i = 0; i < num_tracks; i++) { - if (ses_tocs[dev]->tracks[i].track == track) + if (sony_toc->tracks[i].track == track) { return i; } @@ -1761,8 +1771,7 @@ * (not BCD), so all the conversions are done. */ static int -sony_get_subchnl_info(long arg, - int dev) +sony_get_subchnl_info(long arg) { struct cdrom_subchnl schi; @@ -1771,8 +1780,8 @@ while (handle_sony_cd_attention()) ; - sony_get_toc(dev); - if (ses_tocs[dev] == NULL) + sony_get_toc(); + if (!sony_toc_read) { return -EIO; } @@ -2025,7 +2034,7 @@ retval = 0; /* start_request clears out any readahead data, so it should be safe. */ - if (start_request(ra->addr.lba, ra->nframes, 1, 0)) + if (start_request(ra->addr.lba, ra->nframes, 1)) { retval = -EIO; goto exit_read_audio; @@ -2064,7 +2073,7 @@ } /* Restart the request on the current frame. */ - if (start_request(ra->addr.lba + cframe, ra->nframes - cframe, 1, 0)) + if (start_request(ra->addr.lba + cframe, ra->nframes - cframe, 1)) { retval = -EIO; goto exit_read_audio; @@ -2164,7 +2173,6 @@ unsigned int cmd, unsigned long arg) { - unsigned int dev; unsigned char res_reg[12]; unsigned int res_size; unsigned char params[7]; @@ -2175,11 +2183,6 @@ { return -EINVAL; } - dev = MINOR(inode->i_rdev); - if (dev > MAX_TRACKS) - { - return -EINVAL; - } switch (cmd) { @@ -2289,16 +2292,16 @@ struct cdrom_tochdr *hdr; struct cdrom_tochdr loc_hdr; - sony_get_toc(dev); - if (ses_tocs[dev] == NULL) + sony_get_toc(); + if (!sony_toc_read) { return -EIO; } hdr = (struct cdrom_tochdr *) arg; verify_area(VERIFY_WRITE, hdr, sizeof(*hdr)); - loc_hdr.cdth_trk0 = bcd_to_int(ses_tocs[dev]->first_track_num); - loc_hdr.cdth_trk1 = bcd_to_int(ses_tocs[dev]->last_track_num); + loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num); + loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num); memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr)); } return 0; @@ -2311,8 +2314,8 @@ int track_idx; unsigned char *msf_val = NULL; - sony_get_toc(dev); - if (ses_tocs[dev] == NULL) + sony_get_toc(); + if (!sony_toc_read) { return -EIO; } @@ -2326,21 +2329,21 @@ /* Lead out is handled separately since it is special. */ if (loc_entry.cdte_track == CDROM_LEADOUT) { - loc_entry.cdte_adr = ses_tocs[dev]->address2; - loc_entry.cdte_ctrl = ses_tocs[dev]->control2; - msf_val = ses_tocs[dev]->lead_out_start_msf; + loc_entry.cdte_adr = sony_toc->address2; + loc_entry.cdte_ctrl = sony_toc->control2; + msf_val = sony_toc->lead_out_start_msf; } else { - track_idx = find_track(int_to_bcd(loc_entry.cdte_track), dev); + track_idx = find_track(int_to_bcd(loc_entry.cdte_track)); if (track_idx < 0) { return -EINVAL; } - loc_entry.cdte_adr = ses_tocs[dev]->tracks[track_idx].address; - loc_entry.cdte_ctrl = ses_tocs[dev]->tracks[track_idx].control; - msf_val = ses_tocs[dev]->tracks[track_idx].track_start_msf; + loc_entry.cdte_adr = sony_toc->tracks[track_idx].address; + loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control; + msf_val = sony_toc->tracks[track_idx].track_start_msf; } /* Logical buffer address or MSF format requested? */ @@ -2364,8 +2367,8 @@ struct cdrom_ti ti; int track_idx; - sony_get_toc(dev); - if (ses_tocs[dev] == NULL) + sony_get_toc(); + if (!sony_toc_read) { return -EIO; } @@ -2373,39 +2376,39 @@ verify_area(VERIFY_READ, (char *) arg, sizeof(ti)); memcpy_fromfs(&ti, (char *) arg, sizeof(ti)); - if ( (ti.cdti_trk0 < ses_tocs[dev]->first_track_num) - || (ti.cdti_trk0 > ses_tocs[dev]->last_track_num) + if ( (ti.cdti_trk0 < sony_toc->first_track_num) + || (ti.cdti_trk0 > sony_toc->last_track_num) || (ti.cdti_trk1 < ti.cdti_trk0)) { return -EINVAL; } - track_idx = find_track(int_to_bcd(ti.cdti_trk0), dev); + track_idx = find_track(int_to_bcd(ti.cdti_trk0)); if (track_idx < 0) { return -EINVAL; } - params[1] = ses_tocs[dev]->tracks[track_idx].track_start_msf[0]; - params[2] = ses_tocs[dev]->tracks[track_idx].track_start_msf[1]; - params[3] = ses_tocs[dev]->tracks[track_idx].track_start_msf[2]; + params[1] = sony_toc->tracks[track_idx].track_start_msf[0]; + params[2] = sony_toc->tracks[track_idx].track_start_msf[1]; + params[3] = sony_toc->tracks[track_idx].track_start_msf[2]; /* * If we want to stop after the last track, use the lead-out * MSF to do that. */ - if (ti.cdti_trk1 >= bcd_to_int(ses_tocs[dev]->last_track_num)) + if (ti.cdti_trk1 >= bcd_to_int(sony_toc->last_track_num)) { - log_to_msf(msf_to_log(ses_tocs[dev]->lead_out_start_msf)-1, + log_to_msf(msf_to_log(sony_toc->lead_out_start_msf)-1, &(params[4])); } else { - track_idx = find_track(int_to_bcd(ti.cdti_trk1+1), dev); + track_idx = find_track(int_to_bcd(ti.cdti_trk1+1)); if (track_idx < 0) { return -EINVAL; } - log_to_msf(msf_to_log(ses_tocs[dev]->tracks[track_idx].track_start_msf)-1, + log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf)-1, &(params[4])); } params[0] = 0x03; @@ -2430,7 +2433,7 @@ } case CDROMSUBCHNL: /* Get subchannel info */ - return sony_get_subchnl_info(arg, dev); + return sony_get_subchnl_info(arg); case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */ { @@ -2471,8 +2474,8 @@ struct cdrom_read_audio ra; - sony_get_toc(dev); - if (ses_tocs[dev] == NULL) + sony_get_toc(); + if (!sony_toc_read) { return -EIO; } @@ -2484,8 +2487,8 @@ if (ra.addr_format == CDROM_LBA) { - if ( (ra.addr.lba >= ses_tocs[dev]->lead_out_start_lba) - || (ra.addr.lba + ra.nframes >= ses_tocs[dev]->lead_out_start_lba)) + if ( (ra.addr.lba >= sony_toc->lead_out_start_lba) + || (ra.addr.lba + ra.nframes >= sony_toc->lead_out_start_lba)) { return -EINVAL; } @@ -2502,8 +2505,8 @@ ra.addr.lba = ( (ra.addr.msf.minute * 4500) + (ra.addr.msf.second * 75) + ra.addr.msf.frame); - if ( (ra.addr.lba >= ses_tocs[dev]->lead_out_start_lba) - || (ra.addr.lba + ra.nframes >= ses_tocs[dev]->lead_out_start_lba)) + if ( (ra.addr.lba >= sony_toc->lead_out_start_lba) + || (ra.addr.lba + ra.nframes >= sony_toc->lead_out_start_lba)) { return -EINVAL; } @@ -2541,7 +2544,6 @@ unsigned int res_size; int num_spin_ups; unsigned char params[2]; - int dev; if ((filp) && filp->f_mode & 2) @@ -2589,9 +2591,8 @@ return -EIO; } - dev = MINOR(inode->i_rdev); - sony_get_toc(dev); - if (ses_tocs[dev] == NULL) + sony_get_toc(); + if (!sony_toc_read) { do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); return -EIO; @@ -2599,7 +2600,7 @@ /* For XA on the CDU31A only, we have to do special reads. The CDU33A handles XA automagically. */ - if ( (ses_tocs[dev]->disk_type == SONY_XA_DISK_TYPE) + if ( (sony_toc->disk_type == SONY_XA_DISK_TYPE) && (!is_double_speed)) { params[0] = SONY_SD_DECODE_PARAM; @@ -2954,13 +2955,12 @@ mem_start += sizeof(*last_sony_subcode); readahead_buffer = (unsigned char *) mem_start; mem_start += CD_FRAMESIZE_RAW; + sony_toc = (struct s_sony_session_toc *) mem_start; + mem_start += sizeof(struct s_sony_session_toc); } - for (i=0; i> 4) | (old << 4)) & 0x7700), p); + writew((old & 0x88ff) | (((old >> 4) | ((old & 0xff00) << 4)) & 0x7700), p); } else for (p = (unsigned short *)origin; p < (unsigned short *)scr_end; p++) { @@ -2004,6 +2004,7 @@ scr_end = video_mem_start + video_num_lines * video_size_row; gotoxy(currcons,orig_x,orig_y); set_origin(currcons); + csi_J(currcons, 0); printable = 1; printk("Console: %s %s %ldx%ld, %d virtual console%s (max %d)\n", can_do_color ? "colour" : "mono", diff -u --recursive --new-file v1.1.82/linux/drivers/char/serial.c linux/drivers/char/serial.c --- v1.1.82/linux/drivers/char/serial.c Mon Jan 16 14:18:19 1995 +++ linux/drivers/char/serial.c Wed Jan 18 22:52:36 1995 @@ -941,6 +941,10 @@ info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS; } +#ifdef __alpha__ + info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2; + info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2; +#endif if (info->irq == 0) info->MCR = info->MCR_noint; serial_outp(info, UART_MCR, info->MCR); @@ -2358,7 +2362,15 @@ /* * Reset the UART. */ +#ifdef __alpha__ + /* + * I wonder what DEC did to the OUT1 and OUT2 lines? + * clearing them results in endless interrupts. + */ + serial_outp(info, UART_MCR, 0x0c); +#else serial_outp(info, UART_MCR, 0x00); +#endif serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); (void)serial_in(info, UART_RX); diff -u --recursive --new-file v1.1.82/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v1.1.82/linux/drivers/char/tty_io.c Mon Jan 9 07:22:04 1995 +++ linux/drivers/char/tty_io.c Wed Jan 18 21:26:34 1995 @@ -1443,7 +1443,8 @@ arg = get_fs_long((unsigned long *) arg); return tty_set_ldisc(tty, arg); case TIOCLINUX: - if (current->tty != tty && !suser()) + if ((current->tty != tty || + tty->driver.type != TTY_DRIVER_TYPE_CONSOLE) && !suser()) return -EPERM; retval = verify_area(VERIFY_READ, (void *) arg, 1); if (retval) diff -u --recursive --new-file v1.1.82/linux/drivers/char/vt.c linux/drivers/char/vt.c --- v1.1.82/linux/drivers/char/vt.c Sun Jan 1 19:49:19 1995 +++ linux/drivers/char/vt.c Tue Jan 17 00:57:01 1995 @@ -364,7 +364,7 @@ const struct kbentry * a = (struct kbentry *)arg; ushort *key_map; u_char s; - u_short v; + u_short v, ov; if (!perm) return -EPERM; @@ -379,7 +379,7 @@ if (!i && v == K_NOSUCHMAP) { /* disallocate map */ key_map = key_maps[s]; - if (key_map) { + if (s && key_map) { key_maps[s] = 0; if (key_map[0] == U(K_ALLOCATED)) { kfree_s(key_map, sizeof(plain_map)); @@ -401,6 +401,8 @@ return 0; if (!(key_map = key_maps[s])) { + int j; + if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !suser()) return -EPERM; @@ -410,18 +412,22 @@ return -ENOMEM; key_maps[s] = key_map; key_map[0] = U(K_ALLOCATED); - for (s = 1; s < NR_KEYS; s++) - key_map[s] = U(K_HOLE); + for (j = 1; j < NR_KEYS; j++) + key_map[j] = U(K_HOLE); keymap_count++; } + ov = U(key_map[i]); + if (v == ov) + return 0; /* nothing to do */ /* * Only the Superuser can set or unset the Secure * Attention Key. */ - if (((key_map[i] == U(K_SAK)) || (v == K_SAK)) && - !suser()) + if (((ov == K_SAK) || (v == K_SAK)) && !suser()) return -EPERM; key_map[i] = U(v); + if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT)) + compute_shiftstate(); return 0; } diff -u --recursive --new-file v1.1.82/linux/drivers/net/de600.c linux/drivers/net/de600.c --- v1.1.82/linux/drivers/net/de600.c Mon Jan 16 14:18:20 1995 +++ linux/drivers/net/de600.c Wed Jan 18 10:43:57 1995 @@ -96,6 +96,7 @@ #include #include #include +#include #include #include #include @@ -261,7 +262,6 @@ /* * D-Link driver variables: */ -extern struct device *irq2dev_map[16]; static volatile int rx_page = 0; #define TX_PAGES 2 @@ -686,6 +686,12 @@ return ENODEV; } + if (check_region(DE600_IO, 3)) { + printk(", port 0x%x busy\n", DE600_IO); + return EBUSY; + } + request_region(DE600_IO, 3, "de600"); + printk(", Ethernet Address: %02X", dev->dev_addr[0]); for (i = 1; i < ETH_ALEN; i++) printk(":%02X",dev->dev_addr[i]); @@ -838,5 +844,6 @@ printk("de600: device busy, remove delayed\n"); else unregister_netdev(&de600_dev); + release_region(DE600_IO, 3); } #endif /* MODULE */ diff -u --recursive --new-file v1.1.82/linux/drivers/net/de620.c linux/drivers/net/de620.c --- v1.1.82/linux/drivers/net/de620.c Mon Jan 16 14:18:20 1995 +++ linux/drivers/net/de620.c Wed Jan 18 10:43:57 1995 @@ -100,6 +100,13 @@ #define COUNT_LOOPS */ #endif +static int bnc, utp; +/* + * Force media with insmod: + * insmod de620.o bnc=1 + * or + * insmod de620.o utp=1 + */ #include #include @@ -107,6 +114,7 @@ #include #include #include +#include #include #include #include @@ -192,7 +200,6 @@ #define DE620_RX_START_PAGE 12 /* 12 pages (=3k) reserved for tx */ #define DEF_NIC_CMD IRQEN | ICEN | DS1 -extern struct device *irq2dev_map[16]; unsigned int de620_debug = DE620_DEBUG; static volatile byte NIC_Cmd; @@ -719,6 +726,11 @@ EIPRegister = NCTL0 | NIS0; } + if (utp) + EIPRegister = NCTL0 | NIS0; + if (bnc) + EIPRegister = NCTL0; + de620_send_command(W_CR | RNOP | CLEAR); de620_send_command(W_CR | RNOP); @@ -812,6 +824,12 @@ return ENODEV; } + if (check_region(DE620_IO, 3)) { + printk(", port 0x%x busy\n", DE620_IO); + return EBUSY; + } + request_region(DE620_IO, 3, "de620"); + /* else, got it! */ printk(", Ethernet Address: %2.2X", dev->dev_addr[0] = nic_data.NodeID[0]); @@ -973,6 +991,7 @@ printk("de620: device busy, remove delayed\n"); else unregister_netdev(&de620_dev); + release_region(DE620_IO, 3); } #endif /* MODULE */ diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/ChangeLog linux/drivers/scsi/ChangeLog --- v1.1.82/linux/drivers/scsi/ChangeLog Mon Jan 16 14:18:21 1995 +++ linux/drivers/scsi/ChangeLog Wed Jan 18 09:38:36 1995 @@ -1,3 +1,97 @@ +Mon Jan 16 07:18:23 1995 Eric Youngdale (eric@andante) + + * Linux 1.1.82 released. + + Throughout. + - Change all interrupt handlers to accept new calling convention. + In particular, we now receive the irq number as one of the arguments. + + * More minor spelling corrections in some of the new files. + + * aha1542.c, buslogic.c: Clean up interrupt handler a little now + that we receive the irq as an arg. + + * aha274x.c: s/snarf_region/request_region/ + + * eata.c: Update to version 1.12. Fix some comments and display a + message if we cannot reserve the port addresses. + + * u14-34f.c: Update to version 1.13. Fix some comments and display a + message if we cannot reserve the port addresses. + + * eata_dma.c: Define get_board_data function (send INQUIRY command). + Use to improve detection of variants of different DPT boards. Change + version subnumber to "0g". + + * fdomain.c: Update to version 5.26. Improve detection of some boards + repackaged by IBM. + + * scsi.c (scsi_register_host): Change "name" to const char *. + + * sr.c: Fix problem in set mode command for Toshiba drives. + + * sr.c: Fix typo from patch 81. + +Fri Jan 13 12:54:46 1995 Eric Youngdale (eric@andante) + + * Linux 1.1.81 released. Codefreeze for 1.2 release announced. + + Big changes here. + + * eata_dma.*: New files from Michael Neuffer. + (neuffer@goofy.zdv.uni-mainz.de). Should support + all eata/dpt cards. + + * hosts.c, Makefile: Add eata_dma. + + * README.st: Document MTEOM. + + Patches from me (ERY) to finish support for low-level loadable scsi. + It now works, and is actually useful. + + * Throughout - add new argument to scsi_init_malloc that takes an + additional parameter. This is used as a priority to kmalloc, + and you can specify the GFP_DMA flag if you need DMA-able memory. + + * Makefile: For source files that are loadable, always add name + to SCSI_SRCS. Fill in modules: target. + + * hosts.c: Change next_host to next_scsi_host, and make global. + Print hosts after we have identified all of them. Use info() + function if present, otherwise use name field. + + * hosts.h: Change attach function to return int, not void. + Define number of device slots to allow for loadable devices. + Define tags to tell scsi module code what type of module we + are loading. + + * scsi.c: Fix scan_scsis so that it can be run by a user process. + Do not use waiting loops - use up and down mechanism as long + as current != task[0]. + + * scsi.c(scan_scsis): Do not use stack variables for I/O - this + could be > 16Mb if we are loading a module at runtime (i.e. use + scsi_init_malloc to get some memory we know will be safe). + + * scsi.c: Change dma freelist to be a set of pages. This allows + us to dynamicly adjust the size of the list by adding more pages + to the pagelist. Fix scsi_malloc and scsi_free accordingly. + + * scsi_module.c: Fix include. + + * sd.c: Declare detach function. Increment/decrement module usage + count as required. Fix init functions to allow loaded devices. + Revalidate all new disks so we get the partition tables. Define + detach function. + + * sr.c: Likewise. + + * sg.c: Declare detach function. Allow attachment of devices on + loaded drivers. + + * st.c: Declare detach function. Increment/decrement module usage + count as required. + Tue Jan 10 10:09:58 1995 Eric Youngdale (eric@andante) * Linux 1.1.79 released. diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c --- v1.1.82/linux/drivers/scsi/aha1542.c Mon Jan 16 14:18:22 1995 +++ linux/drivers/scsi/aha1542.c Wed Jan 18 09:38:36 1995 @@ -2,7 +2,7 @@ * linux/kernel/aha1542.c * * Copyright (C) 1992 Tommy Thorn - * Copyright (C) 1993, 1994 Eric Youngdale + * Copyright (C) 1993, 1994, 1995 Eric Youngdale * * Modified by Eric Youngdale * Use request_irq and request_dma to help prevent unexpected conflicts @@ -1093,7 +1093,6 @@ int aha1542_abort(Scsi_Cmnd * SCpnt) { #if 0 - int intval[3]; unchar ahacmd = CMD_START_SCSI; unsigned long flags; struct mailbox * mb; @@ -1119,8 +1118,7 @@ if(mb[mbi].status) { printk("Lost interrupt discovered on irq %d - attempting to recover\n", SCpnt->host->irq); - intval[0] = SCpnt->host->irq; - aha1542_intr_handle((int) &intval[2]); + aha1542_intr_handle(SCpnt->host->irq, NULL); return 0; } diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/buslogic.c linux/drivers/scsi/buslogic.c --- v1.1.82/linux/drivers/scsi/buslogic.c Mon Jan 16 14:18:22 1995 +++ linux/drivers/scsi/buslogic.c Tue Jan 17 00:42:53 1995 @@ -1408,10 +1408,7 @@ " - attempting to recover...\n", scpnt->host->irq); { - int intval[3]; - - intval[0] = scpnt->host->irq; - buslogic_interrupt((int)&intval[2]); + buslogic_interrupt(scpnt->host->irq, NULL); return SCSI_ABORT_SUCCESS; } } diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/eata_dma.c linux/drivers/scsi/eata_dma.c --- v1.1.82/linux/drivers/scsi/eata_dma.c Mon Jan 16 14:18:22 1995 +++ linux/drivers/scsi/eata_dma.c Mon Jan 16 23:35:54 1995 @@ -695,8 +695,8 @@ buff = get_board_data((uint)base, gc->IRQ, gc->scsi_id[3]); - if(!(strncmp("PM2322", &buff[16], 6) || strncmp("PM3021", &buff[16], 6) - || strncmp("PM3222", &buff[16], 6) || strncmp("PM3224", &buff[16], 6))) + 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->MAX_CHAN) { @@ -732,7 +732,9 @@ sh->can_queue = ntohs(gc->queuesiz) / (gc->MAX_CHAN + 1); if (gc->OCS_enabled == TRUE) { - sh->cmd_per_lun = sh->can_queue/C_P_L_DIV; + sh->cmd_per_lun = sh->can_queue/C_P_L_DIV; + if (sh->cmd_per_lun < 2) + sh->cmd_per_lun = 2; } else { sh->cmd_per_lun = 1; } diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/eata_dma.h linux/drivers/scsi/eata_dma.h --- v1.1.82/linux/drivers/scsi/eata_dma.h Mon Jan 16 14:18:22 1995 +++ linux/drivers/scsi/eata_dma.h Tue Jan 17 00:57:01 1995 @@ -377,7 +377,7 @@ unchar p_code:6, null:1, p_save:1; - unchar p_lenght; + unchar p_length; ushort cylinder; unchar heads; unchar sectors; diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c --- v1.1.82/linux/drivers/scsi/hosts.c Fri Jan 13 16:57:05 1995 +++ linux/drivers/scsi/hosts.c Wed Jan 18 09:38:36 1995 @@ -1,7 +1,10 @@ /* * hosts.c Copyright (C) 1992 Drew Eckhardt - * mid to lowlevel SCSI driver interface by - * Drew Eckhardt + * Copyright (C) 1993, 1994, 1995 Eric Youngdale + * + * mid to lowlevel SCSI driver interface + * Initial versions: Drew Eckhardt + * Subsequent revisions: Eric Youngdale * * */ @@ -329,7 +332,8 @@ shpnt->host_no, name); } - printk ("scsi : %d hosts.\n", next_scsi_host); + printk ("scsi : %d host%s.\n", next_scsi_host, + (next_scsi_host == 1) ? "" : "s"); { int block_count = 0, index; diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- v1.1.82/linux/drivers/scsi/scsi.c Mon Jan 16 14:18:23 1995 +++ linux/drivers/scsi/scsi.c Wed Jan 18 09:38:36 1995 @@ -1,9 +1,10 @@ /* * scsi.c Copyright (C) 1992 Drew Eckhardt - * Copyright (C) 1993, 1994 Eric Youngdale + * Copyright (C) 1993, 1994, 1995 Eric Youngdale * - * generic mid-level SCSI driver by - * Drew Eckhardt + * generic mid-level SCSI driver + * Initial versions: Drew Eckhardt + * Subsequent revisions: Eric Youngdale * * * @@ -1132,10 +1133,7 @@ case NO_SENSE: return 0; case RECOVERED_ERROR: - if (SCpnt->device->type == TYPE_TAPE) - return SUGGEST_IS_OK; - else - return 0; + return SUGGEST_IS_OK; case ABORTED_COMMAND: return SUGGEST_RETRY; diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c --- v1.1.82/linux/drivers/scsi/sd.c Fri Jan 13 16:57:06 1995 +++ linux/drivers/scsi/sd.c Wed Jan 18 09:38:36 1995 @@ -1,14 +1,19 @@ /* * sd.c Copyright (C) 1992 Drew Eckhardt - * Copyright (C) 1993, 1994 Eric Youngdale - * Linux scsi disk driver by - * Drew Eckhardt + * Copyright (C) 1993, 1994, 1995 Eric Youngdale + * + * Linux scsi disk driver + * Initial versions: Drew Eckhardt + * Subsequent revisions: Eric Youngdale * * * * Modified by Eric Youngdale ericy@cais.com to * add scatter-gather, multiple outstanding request, and other * enhancements. + * + * Modified by Eric Youngdale eric@aib.com to support loadable + * low-level scsi drivers. */ #include @@ -80,6 +85,12 @@ if(target >= sd_template.dev_max || !rscsi_disks[target].device) return -ENXIO; /* No such device */ + /* + * See if we are requesting a non-existant 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. */ diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c --- v1.1.82/linux/drivers/scsi/seagate.c Mon Jan 16 14:18:23 1995 +++ linux/drivers/scsi/seagate.c Wed Jan 18 09:38:36 1995 @@ -96,7 +96,9 @@ used to calculate memory mapped register location. */ +#ifdef notyet static volatile int abort_confirm = 0; +#endif static volatile void *st0x_cr_sr; /* control register write, diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c --- v1.1.82/linux/drivers/scsi/sr.c Mon Jan 16 14:18:23 1995 +++ linux/drivers/scsi/sr.c Wed Jan 18 09:38:36 1995 @@ -1,6 +1,6 @@ /* * sr.c Copyright (C) 1992 David Giller - * Copyright (C) 1993, 1994 Eric Youngdale + * Copyright (C) 1993, 1994, 1995 Eric Youngdale * * adapted from: * sd.c Copyright (C) 1992 Drew Eckhardt @@ -12,6 +12,9 @@ * Modified by Eric Youngdale ericy@cais.com to * add scatter-gather, multiple outstanding request, and other * enhancements. + * + * Modified by Eric Youngdale eric@aib.com to support loadable + * low-level scsi drivers. */ #include @@ -31,7 +34,7 @@ #include "constants.h" #define MAX_RETRIES 3 -#define SR_TIMEOUT 5000 +#define SR_TIMEOUT 15000 static void sr_init(void); static void sr_finish(void); @@ -236,14 +239,16 @@ } if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) { - printk("CD-ROM error: Drive reports ILLEGAL REQUEST.\n"); + printk("CD-ROM error: "); + print_sense("sr", SCpnt); + printk("command was: "); + print_command(SCpnt->cmnd); if (scsi_CDs[DEVICE_NR(SCpnt->request.dev)].ten) { scsi_CDs[DEVICE_NR(SCpnt->request.dev)].ten = 0; requeue_sr_request(SCpnt); result = 0; return; } else { - printk("CD-ROM error: Drive reports %d.\n", SCpnt->sense_buffer[2]); SCpnt = end_scsi_request(SCpnt, 0, this_count); requeue_sr_request(SCpnt); /* Do next request */ return; @@ -958,8 +963,14 @@ /* If we have already seen this, then skip it. Comes up with loadable modules. */ if (scsi_CDs[i].capacity) continue; + scsi_CDs[i].capacity = 0x1fffff; + scsi_CDs[i].sector_size = 2048; /* A guess, just in case */ + scsi_CDs[i].needs_sector_size = 1; +#if 0 + /* seems better to leave this for later */ get_sectorsize(i); printk("Scd sectorsize = %d bytes.\n", scsi_CDs[i].sector_size); +#endif scsi_CDs[i].use = 1; scsi_CDs[i].ten = 1; scsi_CDs[i].remap = 1; diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c --- v1.1.82/linux/drivers/scsi/sr_ioctl.c Wed Jan 11 21:14:27 1995 +++ linux/drivers/scsi/sr_ioctl.c Wed Jan 18 07:51:45 1995 @@ -420,11 +420,6 @@ return (0); } - case CDROMMULTISESSION_SYS: /* tell start-of-last-session to kernel */ - if(!suser()) return -EACCES; - *((unsigned int *)arg)=scsi_CDs[target].mpcd_sector; - return (0); - case BLKRASET: if(!suser()) return -EACCES; if(!inode->i_rdev) return -EINVAL; diff -u --recursive --new-file v1.1.82/linux/drivers/scsi/ultrastor.c linux/drivers/scsi/ultrastor.c --- v1.1.82/linux/drivers/scsi/ultrastor.c Mon Jan 16 14:18:23 1995 +++ linux/drivers/scsi/ultrastor.c Mon Jan 16 16:48:19 1995 @@ -873,7 +873,7 @@ printk("Ux4F: abort while completed command pending\n"); restore_flags(flags); cli(); - ultrastor_interrupt(0); + ultrastor_interrupt(0, NULL); restore_flags(flags); return SCSI_ABORT_SUCCESS; /* FIXME - is this correct? -ERY */ } diff -u --recursive --new-file v1.1.82/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c --- v1.1.82/linux/drivers/sound/ad1848.c Tue Oct 11 08:51:31 1994 +++ linux/drivers/sound/ad1848.c Tue Jan 17 07:49:57 1995 @@ -93,7 +93,7 @@ static int ad1848_prepare_for_IO (int dev, int bsize, int bcount); static void ad1848_reset (int dev); static void ad1848_halt (int dev); -void ad1848_interrupt (int dev); +void ad1848_interrupt (int dev, struct pt_regs * regs); static int ad_read (ad1848_info * devc, int reg) @@ -866,7 +866,7 @@ } void -ad1848_interrupt (int irq) +ad1848_interrupt (int irq, struct pt_regs * regs) { unsigned char status; ad1848_info *devc; diff -u --recursive --new-file v1.1.82/linux/drivers/sound/configure.c linux/drivers/sound/configure.c --- v1.1.82/linux/drivers/sound/configure.c Sun Nov 6 13:43:22 1994 +++ linux/drivers/sound/configure.c Wed Jan 18 09:38:59 1995 @@ -135,7 +135,7 @@ "SoundBlaster Pro support", "SoundBlaster 16 support", - "digitized voice support", + "/dev/dsp and /dev/audio support (_recommended_)", "This should not be asked", "MIDI interface support", "This should not be asked", diff -u --recursive --new-file v1.1.82/linux/drivers/sound/gus_card.c linux/drivers/sound/gus_card.c --- v1.1.82/linux/drivers/sound/gus_card.c Mon Jul 18 09:50:55 1994 +++ linux/drivers/sound/gus_card.c Tue Jan 17 07:49:57 1995 @@ -33,7 +33,7 @@ #include "gus_hw.h" -void gusintr (int); +void gusintr (int, struct pt_regs * regs); int gus_base, gus_irq, gus_dma; extern int gus_wave_volume; @@ -118,7 +118,7 @@ } void -gusintr (int irq) +gusintr (int irq, struct pt_regs * regs) { unsigned char src; diff -u --recursive --new-file v1.1.82/linux/drivers/sound/gus_wave.c linux/drivers/sound/gus_wave.c --- v1.1.82/linux/drivers/sound/gus_wave.c Mon Dec 12 21:19:00 1994 +++ linux/drivers/sound/gus_wave.c Tue Jan 17 07:49:57 1995 @@ -781,7 +781,7 @@ gus_select_voice (0); /* This disables writes to IRQ/DMA reg */ - gusintr (0); /* Serve pending interrupts */ + gusintr (0,NULL); /* Serve pending interrupts */ RESTORE_INTR (flags); } diff -u --recursive --new-file v1.1.82/linux/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c --- v1.1.82/linux/drivers/sound/mpu401.c Mon Jul 18 09:50:55 1994 +++ linux/drivers/sound/mpu401.c Tue Jan 17 07:49:57 1995 @@ -479,7 +479,7 @@ } void -mpuintr (int irq) +mpuintr (int irq, struct pt_regs * regs) { struct mpu_config *devc; int dev; diff -u --recursive --new-file v1.1.82/linux/drivers/sound/pas2_card.c linux/drivers/sound/pas2_card.c --- v1.1.82/linux/drivers/sound/pas2_card.c Mon Jul 18 09:50:55 1994 +++ linux/drivers/sound/pas2_card.c Tue Jan 17 07:49:57 1995 @@ -79,7 +79,7 @@ /******************* Begin of the Interrupt Handler ********************/ void -pasintr (int unused) +pasintr (int unused, struct pt_regs * regs) { int status; diff -u --recursive --new-file v1.1.82/linux/drivers/sound/sb_dsp.c linux/drivers/sound/sb_dsp.c --- v1.1.82/linux/drivers/sound/sb_dsp.c Fri Aug 19 08:54:09 1994 +++ linux/drivers/sound/sb_dsp.c Wed Jan 18 09:38:50 1995 @@ -131,7 +131,7 @@ } void -sbintr (int unit) +sbintr (int unit, struct pt_regs *regs) { int status; @@ -803,7 +803,7 @@ if (sbc_major >= 3) { -#ifndef SCO +#if !defined(SCO) && !defined(EXCLUDE_AUDIO) # ifdef __SGNXPRO__ if (mixer_type == 2) { diff -u --recursive --new-file v1.1.82/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h --- v1.1.82/linux/drivers/sound/sound_calls.h Wed Jul 20 15:00:55 1994 +++ linux/drivers/sound/sound_calls.h Tue Jan 17 07:49:57 1995 @@ -100,7 +100,7 @@ void request_sound_timer (int count); void sound_stop_timer(void); int snd_ioctl_return(int *addr, int value); -int snd_set_irq_handler (int interrupt_level, void(*hndlr)(int)); +int snd_set_irq_handler (int interrupt_level, void(*hndlr)(int, struct pt_regs *)); void snd_release_irq(int vect); void sound_dma_malloc(int dev); void sound_dma_free(int dev); @@ -177,7 +177,7 @@ long attach_gus_card(long mem_start, struct address_info * hw_config); int probe_gus(struct address_info *hw_config); int gus_set_midi_irq(int num); -void gusintr(int); +void gusintr(int, struct pt_regs * regs); long attach_gus_db16(long mem_start, struct address_info * hw_config); int probe_gus_db16(struct address_info *hw_config); diff -u --recursive --new-file v1.1.82/linux/drivers/sound/uart6850.c linux/drivers/sound/uart6850.c --- v1.1.82/linux/drivers/sound/uart6850.c Mon Jul 18 09:50:55 1994 +++ linux/drivers/sound/uart6850.c Tue Jan 17 07:49:57 1995 @@ -93,7 +93,7 @@ } void -m6850intr (int unit) +m6850intr (int unit, struct pt_regs * regs) { printk ("M"); if (input_avail ()) diff -u --recursive --new-file v1.1.82/linux/fs/buffer.c linux/fs/buffer.c --- v1.1.82/linux/fs/buffer.c Mon Jan 9 07:22:08 1995 +++ linux/fs/buffer.c Wed Jan 18 09:31:39 1995 @@ -741,20 +741,19 @@ } -static char buffer_disposition[] = {BUF_CLEAN, BUF_SHARED, BUF_LOCKED, BUF_SHARED, - BUF_DIRTY, BUF_DIRTY, BUF_DIRTY, BUF_DIRTY}; - void refile_buffer(struct buffer_head * buf){ - int i, dispose; - i = 0; + int dispose; if(buf->b_dev == 0xffff) panic("Attempt to refile free buffer\n"); - if(mem_map[MAP_NR((unsigned long) buf->b_data)] != 1) i = 1; - if(buf->b_lock) i |= 2; - if(buf->b_dirt) i |= 4; - dispose = buffer_disposition[i]; - if(buf->b_list == BUF_SHARED && dispose == BUF_CLEAN) - dispose = BUF_UNSHARED; - if(dispose == -1) panic("Bad buffer settings (%d)\n", i); + if (buf->b_dirt) + dispose = BUF_DIRTY; + else if (mem_map[MAP_NR((unsigned long) buf->b_data)] > 1) + dispose = BUF_SHARED; + else if (buf->b_lock) + dispose = BUF_LOCKED; + else if (buf->b_list == BUF_SHARED) + dispose = BUF_UNSHARED; + else + dispose = BUF_CLEAN; if(dispose == BUF_CLEAN) buf->b_lru_time = jiffies; if(dispose != buf->b_list) { if(dispose == BUF_DIRTY || dispose == BUF_UNSHARED) @@ -1211,6 +1210,9 @@ return 1; } + +/* =========== Reduce the buffer memory ============= */ + /* * try_to_free() checks if all the buffers on this particular page * are unused, and free's the page if so. @@ -1386,6 +1388,8 @@ } +/* ================== Debugging =================== */ + void show_buffers(void) { struct buffer_head * bh; @@ -1425,6 +1429,9 @@ } } + +/* ====================== Cluster patches for ext2 ==================== */ + /* * try_to_reassign() checks if all the buffers on this particular page * are unused, and reassign to a new cluster them if this is true. @@ -1556,7 +1563,7 @@ bh->b_this_page = tmp; while (nblock-- > 0) brelse(arr[nblock]); - return 4; + return 4; /* ?? */ not_aligned: while ((tmp = bh) != NULL) { bh = bh->b_this_page; @@ -1592,6 +1599,9 @@ return reassign_cluster(dev, b[0], size); } + +/* ===================== Init ======================= */ + /* * This initializes the initial buffer free list. nr_buffers_type is set * to one less the actual number of buffers, as a sop to backwards @@ -1630,6 +1640,9 @@ panic("VFS: Unable to initialize buffer free list!"); return; } + + +/* ====================== bdflush support =================== */ /* This is a simple kernel daemon, whose job it is to provide a dynamically * response to dirty buffers. Once this process is activated, we write back diff -u --recursive --new-file v1.1.82/linux/fs/exec.c linux/fs/exec.c --- v1.1.82/linux/fs/exec.c Wed Jan 11 21:14:27 1995 +++ linux/fs/exec.c Wed Jan 18 09:31:39 1995 @@ -505,7 +505,6 @@ int i; int ch; char * name; - struct vm_area_struct * mpnt, *mpnt1; current->dumpable = 1; name = bprm->filename; @@ -517,20 +516,9 @@ current->comm[i++] = ch; } current->comm[i] = '\0'; - /* Release all of the old mmap stuff. */ - mpnt = current->mm->mmap; - current->mm->mmap = NULL; - while (mpnt) { - mpnt1 = mpnt->vm_next; - if (mpnt->vm_ops && mpnt->vm_ops->close) - mpnt->vm_ops->close(mpnt); - remove_shared_vm_struct(mpnt); - if (mpnt->vm_inode) - iput(mpnt->vm_inode); - kfree(mpnt); - mpnt = mpnt1; - } + /* Release all of the old mmap stuff. */ + exit_mmap(current); flush_thread(); diff -u --recursive --new-file v1.1.82/linux/fs/ext/namei.c linux/fs/ext/namei.c --- v1.1.82/linux/fs/ext/namei.c Wed Jan 11 21:14:27 1995 +++ linux/fs/ext/namei.c Wed Jan 18 09:51:48 1995 @@ -285,6 +285,7 @@ de->rec_len = rec_len; } dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->i_dirt = 1; de->name_len = namelen; for (i=0; i < namelen ; i++) de->name[i] = name[i]; diff -u --recursive --new-file v1.1.82/linux/fs/isofs/inode.c linux/fs/isofs/inode.c --- v1.1.82/linux/fs/isofs/inode.c Wed Jan 11 21:14:28 1995 +++ linux/fs/isofs/inode.c Wed Jan 18 07:51:45 1995 @@ -154,6 +154,7 @@ int high_sierra; int dev=s->s_dev; int i; + struct cdrom_multisession ms_info; unsigned int vol_desc_start; struct inode inode_fake; extern struct file_operations * get_blkfops(unsigned int); @@ -204,16 +205,26 @@ * */ vol_desc_start=0; - inode_fake.i_rdev=dev; - i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake, - NULL, - CDROMMULTISESSION_SYS, - (unsigned long) &vol_desc_start); + if (get_blkfops(MAJOR(dev))->ioctl!=NULL) + { + inode_fake.i_rdev=dev; + ms_info.addr_format=CDROM_LBA; + set_fs(KERNEL_DS); + i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake, + NULL, + CDROMMULTISESSION, + (unsigned long) &ms_info); + set_fs(USER_DS); #if 0 - printk("isofs.inode: CDROMMULTISESSION_SYS rc=%d\n",i); - printk("isofs.inode: vol_desc_start = %d\n", vol_desc_start); + printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i); + if (i==0) + { + printk("isofs.inode: XA disk: %s\n", ms_info.xa_flag ? "yes":"no"); + printk("isofs.inode: vol_desc_start = %d\n", ms_info.addr.lba); + } #endif 0 - if (i!=0) vol_desc_start=0; + if ((i==0)&&(ms_info.xa_flag)) vol_desc_start=ms_info.addr.lba; + } for (iso_blknum = vol_desc_start+16; iso_blknum < vol_desc_start+100; iso_blknum++) { #if 0 printk("isofs.inode: iso_blknum=%d\n", iso_blknum); diff -u --recursive --new-file v1.1.82/linux/fs/namei.c linux/fs/namei.c --- v1.1.82/linux/fs/namei.c Wed Jan 11 21:14:28 1995 +++ linux/fs/namei.c Wed Jan 18 09:31:40 1995 @@ -16,6 +16,7 @@ #include #include #include +#include #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) @@ -31,13 +32,8 @@ if (get_fs() == KERNEL_DS) return 0; - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - return -EFAULT; - if (vma->vm_end > address) - break; - } - if (vma->vm_start > address || !(vma->vm_page_prot & PAGE_USER)) + vma = find_vma(current, address); + if (!vma || vma->vm_start > address || !(vma->vm_page_prot & PAGE_USER)) return -EFAULT; address = vma->vm_end - address; if (address > PAGE_SIZE) diff -u --recursive --new-file v1.1.82/linux/fs/proc/array.c linux/fs/proc/array.c --- v1.1.82/linux/fs/proc/array.c Fri Jan 13 10:12:23 1995 +++ linux/fs/proc/array.c Wed Jan 18 09:51:48 1995 @@ -22,6 +22,9 @@ * * Jeff Tranter : added BogoMips field to cpuinfo * + * + * Bruno Haible : remove 4K limit for the maps file + * */ #include @@ -49,6 +52,7 @@ int get_malloc(char * buffer); #endif + static int read_core(struct inode * inode, struct file * file,char * buf, int count) { unsigned long p = file->f_pos; @@ -103,6 +107,7 @@ &proc_kcore_operations, }; + #ifdef CONFIG_PROFILE extern unsigned long prof_len; @@ -161,6 +166,7 @@ #endif /* CONFIG_PROFILE */ + static int get_loadavg(char * buffer) { int a, b, c; @@ -549,21 +555,64 @@ size, resident, share, trs, lrs, drs, dt); } -static int get_maps(int pid, char *buf) +/* + * The way we support synthetic files > 4K + * - without storing their contents in some buffer and + * - without walking through the entire synthetic file until we reach the + * position of the requested data + * is to cleverly encode the current position in the file's f_pos field. + * There is no requirement that a read() call which returns `count' bytes + * of data increases f_pos by exactly `count'. + * + * This idea is Linus' one. Bruno implemented it. + */ + +/* + * For the /proc//maps file, we use fixed length records, each containing + * a single line. + */ +#define MAPS_LINE_LENGTH 1024 +#define MAPS_LINE_SHIFT 10 +/* + * f_pos = (number of the vma in the task->mm->mmap list) * MAPS_LINE_LENGTH + * + (index into the line) + */ +#define MAPS_LINE_FORMAT "%08lx-%08lx %s %08lx %02x:%02x %lu\n" +#define MAPS_LINE_MAX 49 /* sum of 8 1 8 1 4 1 8 1 2 1 2 1 10 1 */ + +static int read_maps (int pid, struct file * file, char * buf, int count) { - int sz = 0; - struct task_struct **p = get_task(pid); - struct vm_area_struct *map; + struct task_struct ** p = get_task(pid); + char * destptr; + loff_t lineno; + int column; + struct vm_area_struct * map; + int i; if (!p || !*p) + return -EINVAL; + + if (count == 0) return 0; - for(map = (*p)->mm->mmap; map != NULL; map = map->vm_next) { - char str[7], *cp = str; + /* decode f_pos */ + lineno = file->f_pos >> MAPS_LINE_SHIFT; + column = file->f_pos & (MAPS_LINE_LENGTH-1); + + /* quickly go to line lineno */ + for (map = (*p)->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++) + continue; + + destptr = buf; + + for ( ; map ; ) { + /* produce the next line */ + char line[MAPS_LINE_MAX+1]; + char str[5], *cp = str; int flags; - int end = sz + 80; /* Length of line */ dev_t dev; unsigned long ino; + int len; flags = map->vm_flags; @@ -572,12 +621,7 @@ *cp++ = flags & VM_EXEC ? 'x' : '-'; *cp++ = flags & VM_SHARED ? 's' : 'p'; *cp++ = 0; - - if (end >= PAGE_SIZE) { - sprintf(buf+sz, "...\n"); - break; - } - + if (map->vm_inode != NULL) { dev = map->vm_inode->i_dev; ino = map->vm_inode->i_ino; @@ -586,16 +630,44 @@ ino = 0; } - sz += sprintf(buf+sz, "%08lx-%08lx %s %08lx %02x:%02x %lu\n", + len = sprintf(line, MAPS_LINE_FORMAT, map->vm_start, map->vm_end, str, map->vm_offset, MAJOR(dev),MINOR(dev), ino); - if (sz > end) { - printk("get_maps: end(%d) < sz(%d)\n", end, sz); - break; + + if (column >= len) { + column = 0; /* continue with next line at column 0 */ + lineno++; + map = map->vm_next; + continue; + } + + i = len-column; + if (i > count) + i = count; + memcpy_tofs(destptr, line+column, i); + destptr += i; count -= i; + column += i; + if (column >= len) { + column = 0; /* next time: next line at column 0 */ + lineno++; + map = map->vm_next; } + + /* done? */ + if (count == 0) + break; + + /* By writing to user space, we might have slept. + * Stop the loop, to avoid a race condition. + */ + if (*p != current) + break; } - - return sz; + + /* encode f_pos */ + file->f_pos = (lineno << MAPS_LINE_SHIFT) + column; + + return destptr-buf; } extern int get_module_list(char *); @@ -673,8 +745,6 @@ return get_stat(pid, page); case PROC_PID_STATM: return get_statm(pid, page); - case PROC_PID_MAPS: - return get_maps(pid, page); } return -EBADF; } @@ -734,6 +804,52 @@ struct inode_operations proc_array_inode_operations = { &proc_array_operations, /* default base directory file-ops */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +static int arraylong_read (struct inode * inode, struct file * file, char * buf, int count) +{ + unsigned int pid = inode->i_ino >> 16; + unsigned int type = inode->i_ino & 0x0000ffff; + + if (count < 0) + return -EINVAL; + + switch (type) { + case PROC_PID_MAPS: + return read_maps(pid, file, buf, count); + } + return -EINVAL; +} + +static struct file_operations proc_arraylong_operations = { + NULL, /* array_lseek */ + arraylong_read, + NULL, /* array_write */ + NULL, /* array_readdir */ + NULL, /* array_select */ + NULL, /* array_ioctl */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + NULL /* can't fsync */ +}; + +struct inode_operations proc_arraylong_inode_operations = { + &proc_arraylong_operations, /* default base directory file-ops */ NULL, /* create */ NULL, /* lookup */ NULL, /* link */ diff -u --recursive --new-file v1.1.82/linux/fs/proc/inode.c linux/fs/proc/inode.c --- v1.1.82/linux/fs/proc/inode.c Wed Jan 11 21:14:28 1995 +++ linux/fs/proc/inode.c Wed Jan 18 09:51:48 1995 @@ -205,9 +205,12 @@ case PROC_PID_CMDLINE: case PROC_PID_STAT: case PROC_PID_STATM: - case PROC_PID_MAPS: inode->i_mode = S_IFREG | S_IRUGO; inode->i_op = &proc_array_inode_operations; + return; + case PROC_PID_MAPS: + inode->i_mode = S_IFIFO | S_IRUGO; + inode->i_op = &proc_arraylong_inode_operations; return; } switch (ino >> 8) { diff -u --recursive --new-file v1.1.82/linux/fs/super.c linux/fs/super.c --- v1.1.82/linux/fs/super.c Mon Jan 2 14:20:58 1995 +++ linux/fs/super.c Wed Jan 18 09:31:40 1995 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -517,15 +518,9 @@ if (!data) return 0; - for (vma = current->mm->mmap ; ; ) { - if (!vma || - (unsigned long) data < vma->vm_start) { - return -EFAULT; - } - if ((unsigned long) data < vma->vm_end) - break; - vma = vma->vm_next; - } + vma = find_vma(current, (unsigned long) data); + if (!vma || (unsigned long) data < vma->vm_start) + return -EFAULT; i = vma->vm_end - (unsigned long) data; if (PAGE_SIZE <= (unsigned long) i) i = PAGE_SIZE-1; diff -u --recursive --new-file v1.1.82/linux/fs/sysv/file.c linux/fs/sysv/file.c --- v1.1.82/linux/fs/sysv/file.c Sat Nov 5 13:32:13 1994 +++ linux/fs/sysv/file.c Wed Jan 18 09:51:48 1995 @@ -189,8 +189,10 @@ if (!read) return -EIO; filp->f_reada = 1; - if (!IS_RDONLY(inode)) + if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } return read; } diff -u --recursive --new-file v1.1.82/linux/fs/sysv/inode.c linux/fs/sysv/inode.c --- v1.1.82/linux/fs/sysv/inode.c Wed Jan 11 21:14:28 1995 +++ linux/fs/sysv/inode.c Wed Jan 18 09:51:48 1995 @@ -510,13 +510,9 @@ unsigned long old_time = *sb->sv_sb_time; if (sb->sv_convert) old_time = from_coh_ulong(old_time); - switch (sb->sv_type) { - case FSTYPE_SYSV4: - if (*sb->sv_sb_state == 0x7c269d38 - old_time) - *sb->sv_sb_state = 0x7c269d38 - time; - default: - break; - } + if (sb->sv_type == FSTYPE_SYSV4) + if (*sb->sv_sb_state == 0x7c269d38 - old_time) + *sb->sv_sb_state = 0x7c269d38 - time; if (sb->sv_convert) time = to_coh_ulong(time); *sb->sv_sb_time = time; diff -u --recursive --new-file v1.1.82/linux/fs/sysv/namei.c linux/fs/sysv/namei.c --- v1.1.82/linux/fs/sysv/namei.c Sat Nov 5 13:32:13 1994 +++ linux/fs/sysv/namei.c Wed Jan 18 09:51:48 1995 @@ -184,6 +184,7 @@ } } else { dir->i_mtime = dir->i_ctime = CURRENT_TIME; + dir->i_dirt = 1; for (i = 0; i < SYSV_NAMELEN ; i++) de->name[i] = (i < namelen) ? name[i] : 0; mark_buffer_dirty(bh, 1); diff -u --recursive --new-file v1.1.82/linux/fs/xiafs/namei.c linux/fs/xiafs/namei.c --- v1.1.82/linux/fs/xiafs/namei.c Wed Jan 11 21:14:28 1995 +++ linux/fs/xiafs/namei.c Wed Jan 18 09:51:48 1995 @@ -356,6 +356,7 @@ inode->i_op = &xiafs_dir_inode_operations; inode->i_size = XIAFS_ZSIZE(dir->i_sb); inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; + inode->i_dirt = 1; dir_block = xiafs_bread(inode,0,1); if (!dir_block) { iput(dir); diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/bitops.h linux/include/asm-mips/bitops.h --- v1.1.82/linux/include/asm-mips/bitops.h Mon Jan 16 14:18:24 1995 +++ linux/include/asm-mips/bitops.h Wed Jan 18 08:54:13 1995 @@ -5,14 +5,14 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (c) 1994 by Ralf Baechle + * Copyright (c) 1994, 1995 Ralf Baechle */ #ifndef __ASM_MIPS_BITOPS_H #define __ASM_MIPS_BITOPS_H -#include +#ifdef __R4000__ -#if defined(__R4000__) +#include /* * The following functions will only work for the R4000! @@ -62,6 +62,90 @@ return retval; } +#else /* !defined(__R4000__) */ + +#include + +#ifdef __KERNEL__ +/* + * Only disable interrupt for kernelmode stuff to keep some + * usermode stuff alive + */ +#define __flags unsigned long flags +#define __cli() cli() +#define __save_flags(x) save_flags(x) +#define __restore_flags(x) restore_flags(x) +#endif /* __KERNEL__ */ + +extern __inline__ int set_bit(int nr, void * addr) +{ + int mask, retval; + int *a = addr; + __flags; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + __save_flags(flags); + __cli(); + retval = (mask & *a) != 0; + *a |= mask; + __restore_flags(flags); + + return retval; +} + +extern __inline__ int clear_bit(int nr, void * addr) +{ + int mask, retval; + int *a = addr; + __flags; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + __save_flags(flags); + __cli(); + retval = (mask & *a) != 0; + *a &= ~mask; + __restore_flags(flags); + + return retval; +} + +extern __inline__ int change_bit(int nr, void * addr) +{ + int mask, retval; + int *a = addr; + __flags; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + __save_flags(flags); + __cli(); + retval = (mask & *a) != 0; + *a ^= mask; + __restore_flags(flags); + + return retval; +} + +#undef __flags +#undef __cli() +#undef __save_flags(x) +#undef __restore_flags(x) + +#endif /* !defined(__R4000__) */ + +extern __inline__ int test_bit(int nr, void *addr) +{ + int mask; + unsigned long *a; + + a = addr; + addr += nr >> 5; + mask = 1 << (nr & 0x1f); + return ((mask & *a) != 0); +} + extern __inline__ int find_first_zero_bit (void *addr, unsigned size) { int res; @@ -86,38 +170,16 @@ ".set\tat\n\t" ".set\treorder\n" "2:" - : "=d" (res) - : "d" ((unsigned int) 0xffffffff), - "d" (size), + : "=r" (res) + : "r" ((unsigned int) 0xffffffff), + "r" (size), "0" ((signed int) 0), - "d" (addr) + "r" (addr) : "$1"); return res; } -#else /* !defined(__R4000__) */ - -#define __USE_PORTABLE_STRINGS_H - -#define __USE_GENERIC_set_bit -#define __USE_GENERIC_clear_bit -#define __USE_GENERIC_change_bit -#define __USE_GENERIC_find_first_zero_bit - -#endif /* !defined(__R4000__) */ - -extern __inline__ int test_bit(int nr, void *addr) -{ - int mask; - unsigned long *a; - - a = addr; - addr += nr >> 5; - mask = 1 << (nr & 0x1f); - return ((mask & *a) != 0); -} - extern __inline__ int find_next_zero_bit (void * addr, int size, int offset) { unsigned long * p = ((unsigned long *) addr) + (offset >> 5); @@ -182,9 +244,5 @@ return __res; } - -#ifdef __USE_PORTABLE_BITOPS_H -#include -#endif #endif /* __ASM_MIPS_BITOPS_H */ diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/bootinfo.h linux/include/asm-mips/bootinfo.h --- v1.1.82/linux/include/asm-mips/bootinfo.h Mon Jan 16 14:18:24 1995 +++ linux/include/asm-mips/bootinfo.h Wed Jan 18 08:54:13 1995 @@ -63,8 +63,11 @@ * bss segment directly after startup. */ -struct bootinfo { +struct drive_info_struct { + char dummy[32]; + }; +struct bootinfo { unsigned long machtype; /* machine type */ unsigned long cputype; /* system CPU & FPU */ @@ -100,6 +103,12 @@ */ unsigned long ramdisk_size; /* ramdisk size in 1024 byte blocks */ unsigned long ramdisk_base; /* address of the ram disk in mem */ + + /* + * Boot flags for the kernel + */ + unsigned long mount_root_rdonly; + struct drive_info_struct drive_info; /* * Video ram info (not in tty.h) diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/mipsconfig.h linux/include/asm-mips/mipsconfig.h --- v1.1.82/linux/include/asm-mips/mipsconfig.h Mon Jan 16 14:18:25 1995 +++ linux/include/asm-mips/mipsconfig.h Tue Jan 17 00:57:01 1995 @@ -13,7 +13,7 @@ #define __ASM_MIPS_MIPS_CONFIG_H /* - * This is the virtual adress to which all ports are being mapped. + * This is the virtual address to which all ports are being mapped. * Must be a value that can be load with a lui instruction. */ #define PORT_BASE 0xe0000000 @@ -31,7 +31,7 @@ * The virtual address where we'll map the pagetables * For a base address of 0xe3000000 this is 0xe338c000 * For a base address of 0xe4000000 this is 0xe4390000 - * FIXME: Gas misscomputes the following expression! + * FIXME: Gas miscomputes the following expression! #define TLB_ROOT (TLBMAP + (TLBMAP >> (12-2))) */ #define TLB_ROOT 0xe4390000 diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/mipsregs.h linux/include/asm-mips/mipsregs.h --- v1.1.82/linux/include/asm-mips/mipsregs.h Mon Jan 16 14:18:25 1995 +++ linux/include/asm-mips/mipsregs.h Tue Jan 17 00:57:01 1995 @@ -94,7 +94,7 @@ #define PFN(addr,pagesizeshift) (((addr) & ((1 << (pagesizeshift))-1)) << 6) /* - * Macros to access the system control copprocessor + * Macros to access the system control coprocessor */ #define read_32bit_cp0_register(source) \ ({ int __res; \ diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/mm.h linux/include/asm-mips/mm.h --- v1.1.82/linux/include/asm-mips/mm.h Mon Jan 16 14:18:25 1995 +++ linux/include/asm-mips/mm.h Tue Jan 17 00:57:01 1995 @@ -6,7 +6,7 @@ /* * Note that we shift the lower 32bits of each EntryLo[01] entry * 6 bits to the left. That way we can convert the PFN into the - * physical address by a single 'and' operation and gain 6 aditional + * physical address by a single 'and' operation and gain 6 additional * bits for storing information which isn't present in a normal * MIPS page table. * I've also changed the naming of some bits so that they conform diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/page.h linux/include/asm-mips/page.h --- v1.1.82/linux/include/asm-mips/page.h Mon Jan 16 14:18:25 1995 +++ linux/include/asm-mips/page.h Wed Jan 18 08:54:13 1995 @@ -1,9 +1,10 @@ #ifndef __ASM_MIPS_PAGE_H #define __ASM_MIPS_PAGE_H +#ifndef __ASSEMBLY__ + #include -#ifndef __ASSEMBLY__ #define invalidate() tlbflush(); extern asmlinkage void tlbflush(void); #endif @@ -14,21 +15,89 @@ #define PAGE_SIZE (1UL << PAGE_SHIFT) #define PGDIR_SIZE (1UL << PGDIR_SHIFT) +#define PAGE_OFFSET 0 +#define MAP_NR(addr) ((addr) >> PAGE_SHIFT) +#define MAP_PAGE_RESERVED (1<<15) + +typedef unsigned short mem_map_t; + +/* + * Note that we shift the lower 32bits of each EntryLo[01] entry + * 6 bits to the left. That way we can convert the PFN into the + * physical address by a single 'and' operation and gain 6 aditional + * bits for storing information which isn't present in a normal + * MIPS page table. + * I've also changed the naming of some bits so that they conform + * the i386 naming as much as possible. + * PAGE_USER isn't implemented in software yet. + */ +#define PAGE_PRESENT (1<<0) /* implemented in software */ +#define PAGE_COW (1<<1) /* implemented in software */ +#define PAGE_DIRTY (1<<2) /* implemented in software */ +#define PAGE_USER (1<<3) /* implemented in software */ +#define PAGE_UNUSED1 (1<<4) /* implemented in software */ +#define PAGE_UNUSED2 (1<<5) /* implemented in software */ +#define PAGE_GLOBAL (1<<6) +#define PAGE_ACCESSED (1<<7) /* The MIPS valid bit */ +#define PAGE_RW (1<<8) /* The MIPS dirty bit */ +#define CACHE_CACHABLE_NO_WA (0<<9) +#define CACHE_CACHABLE_WA (1<<9) +#define CACHE_UNCACHED (2<<9) +#define CACHE_CACHABLE_NONCOHERENT (3<<9) +#define CACHE_CACHABLE_CE (4<<9) +#define CACHE_CACHABLE_COW (5<<9) +#define CACHE_CACHABLE_CUW (6<<9) +#define CACHE_MASK (7<<9) + +#define PAGE_PRIVATE (PAGE_PRESENT | PAGE_ACCESSED | PAGE_DIRTY | PAGE_RW | \ + PAGE_COW | CACHE_CACHABLE_NO_WA) +#define PAGE_SHARED (PAGE_PRESENT | PAGE_ACCESSED | PAGE_DIRTY | PAGE_RW | \ + CACHE_CACHABLE_NO_WA) +#define PAGE_COPY (PAGE_PRESENT | PAGE_ACCESSED | PAGE_COW | \ + CACHE_CACHABLE_NO_WA) +#define PAGE_READONLY (PAGE_PRESENT | PAGE_ACCESSED | CACHE_CACHABLE_NO_WA) +#define PAGE_TABLE (PAGE_PRESENT | PAGE_ACCESSED | PAGE_DIRTY | PAGE_RW | \ + CACHE_CACHABLE_NO_WA) + +#define PAGE_CHG_MASK (PAGE_MASK | PAGE_ACCESSED | PAGE_DIRTY | CACHE_MASK) + #ifdef __KERNEL__ - /* number of bits that fit into a memory pointer */ +/* page table for 0-4MB for everybody */ +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 unsigned long __bad_page(void); +extern unsigned long __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)) - /* to mask away the intra-page address bits */ + +/* to mask away the intra-page address bits */ #define PAGE_MASK (~(PAGE_SIZE-1)) - /* to mask away the intra-page address bits */ + +/* to mask away the intra-page address bits */ #define PGDIR_MASK (~(PGDIR_SIZE-1)) - /* to align the pointer to the (next) page boundary */ + +/* to align the pointer to the (next) page boundary */ #define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) - /* to align the pointer to a pointer address */ + +/* to align the pointer to a pointer address */ #define PTR_MASK (~(sizeof(void*)-1)) - /* sizeof(void*)==1<interrupt) #endif /* __ASM_MIPS_PTRACE_H */ diff -u --recursive --new-file v1.1.82/linux/include/asm-mips/signal.h linux/include/asm-mips/signal.h --- v1.1.82/linux/include/asm-mips/signal.h Mon Jan 16 14:18:25 1995 +++ linux/include/asm-mips/signal.h Wed Jan 18 08:54:13 1995 @@ -1,8 +1,6 @@ #ifndef __ASM_MIPS_SIGNAL_H #define __ASM_MIPS_SIGNAL_H -#ifdef __KERNEL__ - struct sigcontext_struct { unsigned long sc_at, sc_v0, sc_v1, sc_a0, sc_a1, sc_a2, sc_a3; unsigned long sc_t0, sc_t1, sc_t2, sc_t3, sc_t4, sc_t5, sc_t6, sc_t7; @@ -14,7 +12,5 @@ unsigned long oldmask; }; - -#endif #endif /* __ASM_MIPS_SIGNAL_H */ diff -u --recursive --new-file v1.1.82/linux/include/linux/aztcd.h linux/include/linux/aztcd.h --- v1.1.82/linux/include/linux/aztcd.h Thu Jan 1 02:00:00 1970 +++ linux/include/linux/aztcd.h Wed Jan 18 21:15:26 1995 @@ -0,0 +1,124 @@ +/* $Id$ + * Definitions for a AztechCD268 CD-ROM interface + * Copyright (C) 1994, 1995 Werner Zimmermann + * + * based on Mitsumi CDROM driver by Martin Harriss + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: W.Zimmermann adaption to Aztech CD268-01A Version 1.3 + * Oktober 1994 Email: zimmerma@rz.fht-esslingen.de + * Note: Points marked with ??? are questionable ! + */ + +/* *** change this to set the I/O port address */ +#define AZT_BASE_ADDR 0x320 + +/* Increase this if you get lots of timeouts; if you get kernel panic, replace + STEN_LOW_WAIT by STEN_LOW in the source code */ +#define AZT_STATUS_DELAY 400 /*for timer wait, STEN_LOW_WAIT*/ +#define AZT_TIMEOUT 8000000 /*for busy wait STEN_LOW, DTEN_LOW*/ +#define AZT_FAST_TIMEOUT 10000 /*for reading the version string*/ + +/* number of times to retry a command before giving up */ +#define AZT_RETRY_ATTEMPTS 3 + +/*defines for compatibility with mcd.c/mcd.h for Mitsumi drive, will probably + go away, when the AZTECH driver is integrated in the standard Linux kernel*/ +#ifdef CONFIG_AZTCD +#else +#define AZTCD_TIMER MCD_TIMER +#define aztcd_init mcd_init +#define do_aztcd_request do_mcd_request +#define aztcd_setup mcd_setup +#define check_aztcd_media_change check_mcd_media_change +#endif + +/* port access macros */ +#define CMD_PORT azt_port +#define DATA_PORT azt_port +#define STATUS_PORT azt_port+1 +#define MODE_PORT azt_port+2 + +/* status bits */ +#define AST_CMD_CHECK 0x80 /* command error */ +#define AST_DSK_CHG 0x20 /* disk removed or changed */ +#define AST_NOT_READY 0x02 /* no disk in the drive */ +#define AST_DOOR_OPEN 0x40 /* door is open */ +#define AST_MODE_BITS 0x1C /* Mode Bits */ +#define AST_INITIAL 0x0C /* initial, only valid ... */ +#define AST_BUSY 0x04 /* now playing, only valid + in combination with mode + bits */ +/* flag bits */ +#define AFL_DATA 0x02 /* data available if low */ +#define AFL_STATUS 0x04 /* status available if low */ +#define AFL_OP_OK 0x01 /* OP_OK command correct*/ +#define AFL_PA_OK 0x02 /* PA_OK parameter correct*/ +#define AFL_OP_ERR 0x05 /* error in command*/ +#define AFL_PA_ERR 0x06 /* error in parameters*/ +#define POLLED 0x04 /* polled mode */ + +/* commands */ +#define ACMD_SOFT_RESET 0x10 /* reset drive */ +#define ACMD_PLAY_READ 0x20 /* read data track in cooked mode */ +#define ACMD_DATA_READ_RAW 0x21 /* reading in raw mode*/ +#define ACMD_SEEK_TO_LEADIN 0x31 /* seek to leadin track*/ +#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_PAUSE 0x80 /* pause */ +#define ACMD_STOP 0x81 /* stop play */ +#define ACMD_PLAY_AUDIO 0x90 /* play audio track */ +#define ACMD_GET_VERSION 0xA0 /* get firmware version */ +#define ACMD_SET_MODE 0xA1 /* set drive mode */ +#define ACMD_SET_VOLUME 0xAE /* set audio level */ + +/* borrowed from hd.c */ +#define SET_TIMER(func, jifs) \ + ((timer_table[AZTCD_TIMER].expires = jiffies + jifs), \ + (timer_table[AZTCD_TIMER].fn = func), \ + (timer_active |= 1<i_mmap */ -/* for shm areas, the linked list of attaches */ +/* for shm areas, the circular list of attaches */ /* otherwise unused */ struct vm_area_struct * vm_next_share; struct vm_area_struct * vm_prev_share; @@ -192,9 +196,13 @@ /* mmap.c */ extern int do_mmap(struct file * file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long off); -extern void merge_segments(struct vm_area_struct *); +extern struct vm_area_struct * find_vma (struct task_struct *, unsigned long); +extern struct vm_area_struct * find_vma_intersection (struct task_struct *, unsigned long, unsigned long); +extern void merge_segments(struct task_struct *, unsigned long, unsigned long); extern void insert_vm_struct(struct task_struct *, struct vm_area_struct *); extern void remove_shared_vm_struct(struct vm_area_struct *); +extern void build_mmap_avl(struct task_struct *); +extern void exit_mmap(struct task_struct *); extern int do_munmap(unsigned long, size_t); extern unsigned long get_unmapped_area(unsigned long); diff -u --recursive --new-file v1.1.82/linux/include/linux/pci.h linux/include/linux/pci.h --- v1.1.82/linux/include/linux/pci.h Mon Jan 16 14:18:25 1995 +++ linux/include/linux/pci.h Tue Jan 17 00:57:01 1995 @@ -360,7 +360,7 @@ {0xff, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82378, "82378IB"}, \ {0x00, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82424, "82424ZX Saturn"}, \ {0xff, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82375, "82375EB"}, \ - {0x00, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82434, "82434LX Mercury/Netpune"}, \ + {0x00, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82434, "82434LX Mercury/Neptune"}, \ {0xff, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82430, "82430ZX Aries"}, \ {0xff, PCI_VENDOR_ID_SMC, PCI_DEVICE_ID_SMC_37C665, "FDC 37C665"}, \ {0xff, PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_M32, "Mach 32"}, \ @@ -385,7 +385,7 @@ } /* An item of this structure has the following meaning */ -/* For each optimisation, the register adress, the mask */ +/* For each optimisation, the register address, the mask */ /* and value to write to turn it on. */ /* There are 5 optimizations for the moment : */ /* Cache L2 write back best than write through */ @@ -414,7 +414,7 @@ } struct bridge_mapping_type { - unsigned char adress; + unsigned char address; unsigned char mask; unsigned char value; }; @@ -430,7 +430,7 @@ /* This is a dummy entry for my tests. */ /* I have this chipset and no docs.... */ -/* I'am gathering docs. If you can help...... */ +/* I'm gathering docs. If you can help...... */ #define BRIDGE_MAPPING_NUM 3 #define BRIDGE_MAPPING_TYPE { \ diff -u --recursive --new-file v1.1.82/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h --- v1.1.82/linux/include/linux/proc_fs.h Wed Jan 11 21:14:29 1995 +++ linux/include/linux/proc_fs.h Wed Jan 18 09:51:48 1995 @@ -113,6 +113,7 @@ extern struct inode_operations proc_net_inode_operations; extern struct inode_operations proc_mem_inode_operations; extern struct inode_operations proc_array_inode_operations; +extern struct inode_operations proc_arraylong_inode_operations; extern struct inode_operations proc_kcore_inode_operations; extern struct inode_operations proc_profile_inode_operations; extern struct inode_operations proc_kmsg_inode_operations; diff -u --recursive --new-file v1.1.82/linux/include/linux/sched.h linux/include/linux/sched.h --- v1.1.82/linux/include/linux/sched.h Mon Jan 16 14:18:26 1995 +++ linux/include/linux/sched.h Wed Jan 18 09:31:40 1995 @@ -120,6 +120,7 @@ unsigned long dec_flt; /* page fault count of the last time */ unsigned long swap_cnt; /* number of pages to swap on next pass */ struct vm_area_struct * mmap; + struct vm_area_struct * mmap_avl; }; #define INIT_MMAP { &init_task, 0, 0x40000000, PAGE_SHARED, } @@ -133,7 +134,7 @@ /* ?_flt */ 0, 0, 0, 0, \ 0, \ /* swap */ 0, 0, 0, 0, \ - &init_mmap } + &init_mmap, &init_mmap } struct task_struct { /* these are hardcoded - don't touch */ diff -u --recursive --new-file v1.1.82/linux/include/linux/timer.h linux/include/linux/timer.h --- v1.1.82/linux/include/linux/timer.h Mon Jul 25 12:23:03 1994 +++ linux/include/linux/timer.h Wed Jan 18 21:04:04 1995 @@ -30,6 +30,8 @@ * QIC02_TAPE_TIMER timer for QIC-02 tape driver (it's not hardcoded) * * MCD_TIMER Mitsumi CD-ROM Timer + * + * AZTCD_TIMER Aztech CD-ROM Timer */ #define BLANK_TIMER 0 @@ -47,6 +49,7 @@ #define MCD_TIMER 23 #define HD_TIMER2 24 +#define AZTCD_TIMER 25 struct timer_struct { unsigned long expires; diff -u --recursive --new-file v1.1.82/linux/init/main.c linux/init/main.c --- v1.1.82/linux/init/main.c Mon Jan 16 14:18:26 1995 +++ linux/init/main.c Wed Jan 18 22:52:36 1995 @@ -85,6 +85,7 @@ extern void xd_setup(char *str, int *ints); extern void floppy_setup(char *str, int *ints); extern void mcd_setup(char *str, int *ints); +extern void aztcd_setup(char *str, int *ints); extern void st_setup(char *str, int *ints); extern void st0x_setup(char *str, int *ints); extern void tmc8xx_setup(char *str, int *ints); @@ -217,6 +218,9 @@ #ifdef CONFIG_MCD { "mcd=", mcd_setup }, #endif +#ifdef CONFIG_AZTCD + { "aztcd=", aztcd_setup }, +#endif #ifdef CONFIG_SOUND { "sound=", sound_setup }, #endif @@ -385,7 +389,6 @@ memory_start = kmalloc_init(memory_start,memory_end); sti(); calibrate_delay(); - cli(); memory_start = chr_dev_init(memory_start,memory_end); memory_start = blk_dev_init(memory_start,memory_end); sti(); diff -u --recursive --new-file v1.1.82/linux/ipc/shm.c linux/ipc/shm.c --- v1.1.82/linux/ipc/shm.c Fri Jan 13 10:12:24 1995 +++ linux/ipc/shm.c Wed Jan 18 09:31:40 1995 @@ -369,6 +369,44 @@ shm_swap_in /* swapin */ }; +/* Insert shmd into the circular list shp->attaches */ +static inline void insert_attach (struct shmid_ds * shp, struct vm_area_struct * shmd) +{ + struct vm_area_struct * attaches; + + if ((attaches = shp->attaches)) { + shmd->vm_next_share = attaches; + shmd->vm_prev_share = attaches->vm_prev_share; + shmd->vm_prev_share->vm_next_share = shmd; + attaches->vm_prev_share = shmd; + } else + shp->attaches = shmd->vm_next_share = shmd->vm_prev_share = shmd; +} + +/* Remove shmd from circular list shp->attaches */ +static inline void remove_attach (struct shmid_ds * shp, struct vm_area_struct * shmd) +{ + if (shmd->vm_next_share == shmd) { + if (shp->attaches != shmd) { + printk("shm_close: shm segment (id=%ld) attach list inconsistent\n", + (shmd->vm_pte >> SHM_ID_SHIFT) & SHM_ID_MASK); + printk("shm_close: %d %08lx-%08lx %c%c%c%c %08lx %08lx\n", + shmd->vm_task->pid, shmd->vm_start, shmd->vm_end, + shmd->vm_flags & VM_READ ? 'r' : '-', + shmd->vm_flags & VM_WRITE ? 'w' : '-', + shmd->vm_flags & VM_EXEC ? 'x' : '-', + shmd->vm_flags & VM_SHARED ? 's' : 'p', + shmd->vm_offset, shmd->vm_pte); + } + shp->attaches = NULL; + } else { + if (shp->attaches == shmd) + shp->attaches = shmd->vm_next_share; + shmd->vm_prev_share->vm_next_share = shmd->vm_next_share; + shmd->vm_next_share->vm_prev_share = shmd->vm_prev_share; + } +} + /* * check range is unmapped, ensure page tables exist * mark page table entries with shm_sgn. @@ -398,7 +436,7 @@ /* add new mapping */ insert_vm_struct(current, shmd); - merge_segments(current->mm->mmap); + merge_segments(current, shmd->vm_start, shmd->vm_end); /* check that the range has page_tables */ for (tmp = shmd->vm_start; tmp < shmd->vm_end; tmp += PAGE_SIZE) { @@ -475,12 +513,11 @@ return -EINVAL; } if (!(shmflg & SHM_REMAP)) - for (shmd = current->mm->mmap; shmd; shmd = shmd->vm_next) - if (!(addr >= shmd->vm_end || addr + shp->shm_segsz <= shmd->vm_start)) { - /* printk("shmat() -> EINVAL because the interval [0x%lx,0x%lx) intersects an already mapped interval [0x%lx,0x%lx).\n", - addr, addr + shp->shm_segsz, shmd->vm_start, shmd->vm_end); */ - return -EINVAL; - } + if ((shmd = find_vma_intersection(current, addr, addr + shp->shm_segsz))) { + /* printk("shmat() -> EINVAL because the interval [0x%lx,0x%lx) intersects an already mapped interval [0x%lx,0x%lx).\n", + addr, addr + shp->shm_segsz, shmd->vm_start, shmd->vm_end); */ + return -EINVAL; + } if (ipcperms(&shp->shm_perm, shmflg & SHM_RDONLY ? S_IRUGO : S_IRUGO|S_IWUGO)) return -EACCES; @@ -503,7 +540,7 @@ shmd->vm_flags = VM_SHM | VM_MAYSHARE | VM_SHARED | VM_MAYREAD | VM_MAYEXEC | VM_READ | VM_EXEC | ((shmflg & SHM_RDONLY) ? 0 : VM_MAYWRITE | VM_WRITE); - shmd->vm_next_share = NULL; + shmd->vm_next_share = shmd->vm_prev_share = NULL; shmd->vm_inode = NULL; shmd->vm_offset = 0; shmd->vm_ops = &shm_vm_ops; @@ -516,8 +553,8 @@ return err; } - shmd->vm_next_share = shp->attaches; - shp->attaches = shmd; + insert_attach(shp,shmd); /* insert shmd into shp->attaches */ + shp->shm_lpid = current->pid; shp->shm_atime = CURRENT_TIME; @@ -537,8 +574,7 @@ printk("shm_open: unused id=%d PANIC\n", id); return; } - shmd->vm_next_share = shp->attaches; - shp->attaches = shmd; + insert_attach(shp,shmd); /* insert shmd into shp->attaches */ shp->shm_nattch++; shp->shm_atime = CURRENT_TIME; shp->shm_lpid = current->pid; @@ -552,7 +588,6 @@ */ static void shm_close (struct vm_area_struct *shmd) { - struct vm_area_struct **shmdp; struct shmid_ds *shp; int id; @@ -561,21 +596,7 @@ /* remove from the list of attaches of the shm segment */ id = (shmd->vm_pte >> SHM_ID_SHIFT) & SHM_ID_MASK; shp = shm_segs[id]; - for (shmdp = &shp->attaches; *shmdp; shmdp = &(*shmdp)->vm_next_share) - if (*shmdp == shmd) { - *shmdp = shmd->vm_next_share; - goto found; - } - printk("shm_close: shm segment (id=%d) attach list inconsistent\n",id); - printk("shm_close: %d %08lx-%08lx %c%c%c%c %08lx %08lx\n", - shmd->vm_task->pid, shmd->vm_start, shmd->vm_end, - shmd->vm_flags & VM_READ ? 'r' : '-', - shmd->vm_flags & VM_WRITE ? 'w' : '-', - shmd->vm_flags & VM_EXEC ? 'x' : '-', - shmd->vm_flags & VM_SHARED ? 's' : 'p', - shmd->vm_offset, shmd->vm_pte); - - found: + remove_attach(shp,shmd); /* remove from shp->attaches */ shp->shm_lpid = current->pid; shp->shm_dtime = CURRENT_TIME; if (--shp->shm_nattch <= 0 && shp->shm_perm.mode & SHM_DEST) @@ -714,7 +735,8 @@ swap_free (swap_nr); return 0; } - for (shmd = shp->attaches; shmd; shmd = shmd->vm_next_share) { + for (shmd = shp->attaches; ; ) { + do { unsigned long tmp, *pte; if ((shmd->vm_pte >> SHM_ID_SHIFT & SHM_ID_MASK) != id) { printk ("shm_swap: id=%ld does not match shmd->vm_pte.id=%ld\n", id, shmd->vm_pte >> SHM_ID_SHIFT & SHM_ID_MASK); @@ -743,6 +765,10 @@ mem_map[MAP_NR(page)]--; shmd->vm_task->mm->rss--; invalid++; + /* continue looping through circular list */ + } while (0); + if ((shmd = shmd->vm_next_share) == shp->attaches) + break; } if (mem_map[MAP_NR(page)] != 1) diff -u --recursive --new-file v1.1.82/linux/kernel/exit.c linux/kernel/exit.c --- v1.1.82/linux/kernel/exit.c Mon Jan 9 07:22:11 1995 +++ linux/kernel/exit.c Wed Jan 18 09:31:39 1995 @@ -354,26 +354,6 @@ } } -static void exit_mm(void) -{ - struct vm_area_struct * mpnt; - - mpnt = current->mm->mmap; - current->mm->mmap = NULL; - while (mpnt) { - struct vm_area_struct * next = mpnt->vm_next; - if (mpnt->vm_ops && mpnt->vm_ops->close) - mpnt->vm_ops->close(mpnt); - remove_shared_vm_struct(mpnt); - if (mpnt->vm_inode) - iput(mpnt->vm_inode); - kfree(mpnt); - mpnt = next; - } - - free_page_tables(current); -} - static void exit_files(void) { int i; @@ -402,7 +382,8 @@ fake_volatile: if (current->semundo) sem_exit(); - exit_mm(); + exit_mmap(current); + free_page_tables(current); exit_files(); exit_fs(); exit_thread(); diff -u --recursive --new-file v1.1.82/linux/kernel/fork.c linux/kernel/fork.c --- v1.1.82/linux/kernel/fork.c Fri Jan 13 10:12:24 1995 +++ linux/kernel/fork.c Wed Jan 18 09:31:40 1995 @@ -93,8 +93,10 @@ p = &tsk->mm->mmap; for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) { tmp = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); - if (!tmp) + if (!tmp) { + exit_mmap(tsk); return -ENOMEM; + } *tmp = *mpnt; tmp->vm_task = tsk; tmp->vm_next = NULL; @@ -110,6 +112,7 @@ *p = tmp; p = &tmp->vm_next; } + build_mmap_avl(tsk); return 0; } diff -u --recursive --new-file v1.1.82/linux/kernel/sys.c linux/kernel/sys.c --- v1.1.82/linux/kernel/sys.c Mon Jan 9 07:22:12 1995 +++ linux/kernel/sys.c Wed Jan 18 09:31:40 1995 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -380,7 +381,6 @@ int freepages; unsigned long rlim; unsigned long newbrk, oldbrk; - struct vm_area_struct * vma; if (brk < current->mm->end_code) return current->mm->brk; @@ -409,12 +409,8 @@ /* * Check against existing mmap mappings. */ - for (vma = current->mm->mmap; vma; vma = vma->vm_next) { - if (newbrk <= vma->vm_start) - break; - if (oldbrk < vma->vm_end) - return current->mm->brk; - } + if (find_vma_intersection(current, oldbrk, newbrk)) + return current->mm->brk; /* * stupid algorithm to decide if we have enough memory: while * simple, it hopefully works in most obvious cases.. Easy to diff -u --recursive --new-file v1.1.82/linux/kernel/time.c linux/kernel/time.c --- v1.1.82/linux/kernel/time.c Mon Jan 9 07:22:12 1995 +++ linux/kernel/time.c Wed Jan 18 08:54:13 1995 @@ -203,16 +203,19 @@ save_flags(flags); cli(); -#ifdef __i386__ +#if defined (__i386__) || defined (__mips__) *tv = xtime; tv->tv_usec += do_gettimeoffset(); if (tv->tv_usec >= 1000000) { tv->tv_usec -= 1000000; tv->tv_sec++; } -#else /* not __i386__ */ + sti(); +#else /* !defined (__i386__) && !defined (__mips__) */ + cli(); *tv = xtime; -#endif /* not __i386__ */ + sti(); +#endif /* !defined (__i386__) && !defined (__mips__) */ restore_flags(flags); } diff -u --recursive --new-file v1.1.82/linux/lib/string.c linux/lib/string.c --- v1.1.82/linux/lib/string.c Mon Jan 16 14:18:33 1995 +++ linux/lib/string.c Wed Jan 18 10:01:26 1995 @@ -67,7 +67,7 @@ register char __res; while (1) { - if ((__res = *cs - *ct++) != 0 && *cs++) + if ((__res = *cs - *ct++) != 0 || !*cs++) break; } @@ -190,10 +190,20 @@ void * memmove(void * dest,const void *src,size_t count) { - char *tmp = (char *) dest, *s = (char *) src; + char *tmp, *s; - while (count--) - *tmp++ = *s++; + if (dest <= src) { + tmp = (char *) dest; + s = (char *) src; + while (count--) + *tmp++ = *s++; + } + else { + tmp = (char *) dest + count; + s = (char *) src + count; + while (count--) + *--tmp = *--s; + } return dest; } diff -u --recursive --new-file v1.1.82/linux/mm/memory.c linux/mm/memory.c --- v1.1.82/linux/mm/memory.c Mon Jan 16 14:18:33 1995 +++ linux/mm/memory.c Wed Jan 18 09:31:40 1995 @@ -615,12 +615,9 @@ if (get_fs() == get_ds()) return 0; - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - goto bad_area; - if (vma->vm_end > start) - break; - } + vma = find_vma(current, start); + if (!vma) + goto bad_area; if (vma->vm_start <= start) goto good_area; if (!(vma->vm_flags & VM_GROWSDOWN)) diff -u --recursive --new-file v1.1.82/linux/mm/mmap.c linux/mm/mmap.c --- v1.1.82/linux/mm/mmap.c Thu Dec 1 11:48:12 1994 +++ linux/mm/mmap.c Wed Jan 18 09:31:40 1995 @@ -73,7 +73,7 @@ } if ((flags & MAP_DENYWRITE) && (file->f_inode->i_wcount > 0)) return -ETXTBSY; - } else if ((flags & MAP_TYPE) == MAP_SHARED) + } else if ((flags & MAP_TYPE) != MAP_PRIVATE) return -EINVAL; /* @@ -147,7 +147,7 @@ return error; } insert_vm_struct(current, vma); - merge_segments(current->mm->mmap); + merge_segments(current, vma->vm_start, vma->vm_end); return addr; } @@ -195,6 +195,428 @@ get_fs_long(buffer+2), flags, get_fs_long(buffer+5)); } + +/* + * Searching a VMA in the linear list task->mm->mmap is horribly slow. + * Use an AVL (Adelson-Velskii and Landis) tree to speed up this search + * from O(n) to O(log n), where n is the number of VMAs of the task + * (typically around 6, but may reach 3000 in some cases). + * Written by Bruno Haible . + */ + +/* We keep the list and tree sorted by address. */ +#define vm_avl_key vm_end +#define vm_avl_key_t unsigned long /* typeof(vma->avl_key) */ + +/* + * task->mm->mmap_avl is the AVL tree corresponding to task->mm->mmap + * or, more exactly, its root. + * A vm_area_struct has the following fields: + * vm_avl_left left son of a tree node + * vm_avl_right right son of a tree node + * vm_avl_height 1+max(heightof(left),heightof(right)) + * The empty tree is represented as NULL. + */ +#define avl_empty (struct vm_area_struct *) NULL + +/* Since the trees are balanced, their height will never be large. */ +#define avl_maxheight 41 /* why this? a small exercise */ +#define heightof(tree) ((tree) == avl_empty ? 0 : (tree)->vm_avl_height) +/* + * Consistency and balancing rules: + * 1. tree->vm_avl_height == 1+max(heightof(tree->vm_avl_left),heightof(tree->vm_avl_right)) + * 2. abs( heightof(tree->vm_avl_left) - heightof(tree->vm_avl_right) ) <= 1 + * 3. foreach node in tree->vm_avl_left: node->vm_avl_key <= tree->vm_avl_key, + * foreach node in tree->vm_avl_right: node->vm_avl_key >= tree->vm_avl_key. + */ + +/* Look up the first VMA which satisfies addr < vm_end, NULL if none. */ +struct vm_area_struct * find_vma (struct task_struct * task, unsigned long addr) +{ +#if 0 /* equivalent, but slow */ + struct vm_area_struct * vma; + + for (vma = task->mm->mmap ; ; vma = vma->vm_next) { + if (!vma) + return NULL; + if (vma->vm_end > addr) + return vma; + } +#else + struct vm_area_struct * result = NULL; + struct vm_area_struct * tree; + + for (tree = task->mm->mmap_avl ; ; ) { + if (tree == avl_empty) + return result; + if (tree->vm_end > addr) { + if (tree->vm_start <= addr) + return tree; + result = tree; + tree = tree->vm_avl_left; + } else + tree = tree->vm_avl_right; + } +#endif +} + +/* Look up the first VMA which intersects the interval start_addr..end_addr-1, + NULL if none. Assume start_addr < end_addr. */ +struct vm_area_struct * find_vma_intersection (struct task_struct * task, unsigned long start_addr, unsigned long end_addr) +{ + struct vm_area_struct * vma; + +#if 0 /* equivalent, but slow */ + for (vma = task->mm->mmap; vma; vma = vma->vm_next) { + if (end_addr <= vma->vm_start) + break; + if (start_addr < vma->vm_end) + return vma; + } + return NULL; +#else + vma = find_vma(task,start_addr); + if (!vma || end_addr <= vma->vm_start) + return NULL; + return vma; +#endif +} + +/* Look up the nodes at the left and at the right of a given node. */ +static void avl_neighbours (struct vm_area_struct * node, struct vm_area_struct * tree, struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right) +{ + vm_avl_key_t key = node->vm_avl_key; + + *to_the_left = *to_the_right = NULL; + for (;;) { + if (tree == avl_empty) { + printk("avl_neighbours: node not found in the tree\n"); + return; + } + if (key == tree->vm_avl_key) + break; + if (key < tree->vm_avl_key) { + *to_the_right = tree; + tree = tree->vm_avl_left; + } else { + *to_the_left = tree; + tree = tree->vm_avl_right; + } + } + if (tree != node) { + printk("avl_neighbours: node not exactly found in the tree\n"); + return; + } + if (tree->vm_avl_left != avl_empty) { + struct vm_area_struct * node; + for (node = tree->vm_avl_left; node->vm_avl_right != avl_empty; node = node->vm_avl_right) + continue; + *to_the_left = node; + } + if (tree->vm_avl_right != avl_empty) { + struct vm_area_struct * node; + for (node = tree->vm_avl_right; node->vm_avl_left != avl_empty; node = node->vm_avl_left) + continue; + *to_the_right = node; + } + if ((*to_the_left && ((*to_the_left)->vm_next != node)) || (node->vm_next != *to_the_right)) + printk("avl_neighbours: tree inconsistent with list\n"); +} + +/* + * Rebalance a tree. + * After inserting or deleting a node of a tree we have a sequence of subtrees + * nodes[0]..nodes[k-1] such that + * nodes[0] is the root and nodes[i+1] = nodes[i]->{vm_avl_left|vm_avl_right}. + */ +static void avl_rebalance (struct vm_area_struct *** nodeplaces_ptr, int count) +{ + for ( ; count > 0 ; count--) { + struct vm_area_struct ** nodeplace = *--nodeplaces_ptr; + struct vm_area_struct * node = *nodeplace; + struct vm_area_struct * nodeleft = node->vm_avl_left; + struct vm_area_struct * noderight = node->vm_avl_right; + int heightleft = heightof(nodeleft); + int heightright = heightof(noderight); + if (heightright + 1 < heightleft) { + /* */ + /* * */ + /* / \ */ + /* n+2 n */ + /* */ + struct vm_area_struct * nodeleftleft = nodeleft->vm_avl_left; + struct vm_area_struct * nodeleftright = nodeleft->vm_avl_right; + int heightleftright = heightof(nodeleftright); + if (heightof(nodeleftleft) >= heightleftright) { + /* */ + /* * n+2|n+3 */ + /* / \ / \ */ + /* n+2 n --> / n+1|n+2 */ + /* / \ | / \ */ + /* n+1 n|n+1 n+1 n|n+1 n */ + /* */ + node->vm_avl_left = nodeleftright; nodeleft->vm_avl_right = node; + nodeleft->vm_avl_height = 1 + (node->vm_avl_height = 1 + heightleftright); + *nodeplace = nodeleft; + } else { + /* */ + /* * n+2 */ + /* / \ / \ */ + /* n+2 n --> n+1 n+1 */ + /* / \ / \ / \ */ + /* n n+1 n L R n */ + /* / \ */ + /* L R */ + /* */ + nodeleft->vm_avl_right = nodeleftright->vm_avl_left; + node->vm_avl_left = nodeleftright->vm_avl_right; + nodeleftright->vm_avl_left = nodeleft; + nodeleftright->vm_avl_right = node; + nodeleft->vm_avl_height = node->vm_avl_height = heightleftright; + nodeleftright->vm_avl_height = heightleft; + *nodeplace = nodeleftright; + } + } + else if (heightleft + 1 < heightright) { + /* similar to the above, just interchange 'left' <--> 'right' */ + struct vm_area_struct * noderightright = noderight->vm_avl_right; + struct vm_area_struct * noderightleft = noderight->vm_avl_left; + int heightrightleft = heightof(noderightleft); + if (heightof(noderightright) >= heightrightleft) { + node->vm_avl_right = noderightleft; noderight->vm_avl_left = node; + noderight->vm_avl_height = 1 + (node->vm_avl_height = 1 + heightrightleft); + *nodeplace = noderight; + } else { + noderight->vm_avl_left = noderightleft->vm_avl_right; + node->vm_avl_right = noderightleft->vm_avl_left; + noderightleft->vm_avl_right = noderight; + noderightleft->vm_avl_left = node; + noderight->vm_avl_height = node->vm_avl_height = heightrightleft; + noderightleft->vm_avl_height = heightright; + *nodeplace = noderightleft; + } + } + else { + int height = (heightleftvm_avl_height) + break; + node->vm_avl_height = height; + } + } +} + +/* Insert a node into a tree. */ +static void avl_insert (struct vm_area_struct * new_node, struct vm_area_struct ** ptree) +{ + vm_avl_key_t key = new_node->vm_avl_key; + struct vm_area_struct ** nodeplace = ptree; + struct vm_area_struct ** stack[avl_maxheight]; + int stack_count = 0; + struct vm_area_struct *** stack_ptr = &stack[0]; /* = &stack[stackcount] */ + for (;;) { + struct vm_area_struct * node = *nodeplace; + if (node == avl_empty) + break; + *stack_ptr++ = nodeplace; stack_count++; + if (key < node->vm_avl_key) + nodeplace = &node->vm_avl_left; + else + nodeplace = &node->vm_avl_right; + } + new_node->vm_avl_left = avl_empty; + new_node->vm_avl_right = avl_empty; + new_node->vm_avl_height = 1; + *nodeplace = new_node; + avl_rebalance(stack_ptr,stack_count); +} + +/* Insert a node into a tree, and + * return the node to the left of it and the node to the right of it. + */ +static void avl_insert_neighbours (struct vm_area_struct * new_node, struct vm_area_struct ** ptree, + struct vm_area_struct ** to_the_left, struct vm_area_struct ** to_the_right) +{ + vm_avl_key_t key = new_node->vm_avl_key; + struct vm_area_struct ** nodeplace = ptree; + struct vm_area_struct ** stack[avl_maxheight]; + int stack_count = 0; + struct vm_area_struct *** stack_ptr = &stack[0]; /* = &stack[stackcount] */ + *to_the_left = *to_the_right = NULL; + for (;;) { + struct vm_area_struct * node = *nodeplace; + if (node == avl_empty) + break; + *stack_ptr++ = nodeplace; stack_count++; + if (key < node->vm_avl_key) { + *to_the_right = node; + nodeplace = &node->vm_avl_left; + } else { + *to_the_left = node; + nodeplace = &node->vm_avl_right; + } + } + new_node->vm_avl_left = avl_empty; + new_node->vm_avl_right = avl_empty; + new_node->vm_avl_height = 1; + *nodeplace = new_node; + avl_rebalance(stack_ptr,stack_count); +} + +/* Removes a node out of a tree. */ +static void avl_remove (struct vm_area_struct * node_to_delete, struct vm_area_struct ** ptree) +{ + vm_avl_key_t key = node_to_delete->vm_avl_key; + struct vm_area_struct ** nodeplace = ptree; + struct vm_area_struct ** stack[avl_maxheight]; + int stack_count = 0; + struct vm_area_struct *** stack_ptr = &stack[0]; /* = &stack[stackcount] */ + struct vm_area_struct ** nodeplace_to_delete; + for (;;) { + struct vm_area_struct * node = *nodeplace; + if (node == avl_empty) { + /* what? node_to_delete not found in tree? */ + printk("avl_remove: node to delete not found in tree\n"); + return; + } + *stack_ptr++ = nodeplace; stack_count++; + if (key == node->vm_avl_key) + break; + if (key < node->vm_avl_key) + nodeplace = &node->vm_avl_left; + else + nodeplace = &node->vm_avl_right; + } + nodeplace_to_delete = nodeplace; + /* Have to remove node_to_delete = *nodeplace_to_delete. */ + if (node_to_delete->vm_avl_left == avl_empty) { + *nodeplace_to_delete = node_to_delete->vm_avl_right; + stack_ptr--; stack_count--; + } else { + struct vm_area_struct *** stack_ptr_to_delete = stack_ptr; + struct vm_area_struct ** nodeplace = &node_to_delete->vm_avl_left; + struct vm_area_struct * node; + for (;;) { + node = *nodeplace; + if (node->vm_avl_right == avl_empty) + break; + *stack_ptr++ = nodeplace; stack_count++; + nodeplace = &node->vm_avl_right; + } + *nodeplace = node->vm_avl_left; + /* node replaces node_to_delete */ + node->vm_avl_left = node_to_delete->vm_avl_left; + node->vm_avl_right = node_to_delete->vm_avl_right; + node->vm_avl_height = node_to_delete->vm_avl_height; + *nodeplace_to_delete = node; /* replace node_to_delete */ + *stack_ptr_to_delete = &node->vm_avl_left; /* replace &node_to_delete->vm_avl_left */ + } + avl_rebalance(stack_ptr,stack_count); +} + +#ifdef DEBUG_AVL + +/* print a list */ +static void printk_list (struct vm_area_struct * vma) +{ + printk("["); + while (vma) { + printk("%08lX-%08lX", vma->vm_start, vma->vm_end); + vma = vma->vm_next; + if (!vma) + break; + printk(" "); + } + printk("]"); +} + +/* print a tree */ +static void printk_avl (struct vm_area_struct * tree) +{ + if (tree != avl_empty) { + printk("("); + if (tree->vm_avl_left != avl_empty) { + printk_avl(tree->vm_avl_left); + printk("<"); + } + printk("%08lX-%08lX", tree->vm_start, tree->vm_end); + if (tree->vm_avl_right != avl_empty) { + printk(">"); + printk_avl(tree->vm_avl_right); + } + printk(")"); + } +} + +static char *avl_check_point = "somewhere"; + +/* check a tree's consistency and balancing */ +static void avl_checkheights (struct vm_area_struct * tree) +{ + int h, hl, hr; + + if (tree == avl_empty) + return; + avl_checkheights(tree->vm_avl_left); + avl_checkheights(tree->vm_avl_right); + h = tree->vm_avl_height; + hl = heightof(tree->vm_avl_left); + hr = heightof(tree->vm_avl_right); + if ((h == hl+1) && (hr <= hl) && (hl <= hr+1)) + return; + if ((h == hr+1) && (hl <= hr) && (hr <= hl+1)) + return; + printk("%s: avl_checkheights: heights inconsistent\n",avl_check_point); +} + +/* check that all values stored in a tree are < key */ +static void avl_checkleft (struct vm_area_struct * tree, vm_avl_key_t key) +{ + if (tree == avl_empty) + return; + avl_checkleft(tree->vm_avl_left,key); + avl_checkleft(tree->vm_avl_right,key); + if (tree->vm_avl_key < key) + return; + printk("%s: avl_checkleft: left key %lu >= top key %lu\n",avl_check_point,tree->vm_avl_key,key); +} + +/* check that all values stored in a tree are > key */ +static void avl_checkright (struct vm_area_struct * tree, vm_avl_key_t key) +{ + if (tree == avl_empty) + return; + avl_checkright(tree->vm_avl_left,key); + avl_checkright(tree->vm_avl_right,key); + if (tree->vm_avl_key > key) + return; + printk("%s: avl_checkright: right key %lu <= top key %lu\n",avl_check_point,tree->vm_avl_key,key); +} + +/* check that all values are properly increasing */ +static void avl_checkorder (struct vm_area_struct * tree) +{ + if (tree == avl_empty) + return; + avl_checkorder(tree->vm_avl_left); + avl_checkorder(tree->vm_avl_right); + avl_checkleft(tree->vm_avl_left,tree->vm_avl_key); + avl_checkright(tree->vm_avl_right,tree->vm_avl_key); +} + +/* all checks */ +static void avl_check (struct task_struct * task, char *caller) +{ + avl_check_point = caller; +/* printk("task \"%s\", %s\n",task->comm,caller); */ +/* printk("task \"%s\" list: ",task->comm); printk_list(task->mm->mmap); printk("\n"); */ +/* printk("task \"%s\" tree: ",task->comm); printk_avl(task->mm->mmap_avl); printk("\n"); */ + avl_checkheights(task->mm->mmap_avl); + avl_checkorder(task->mm->mmap_avl); +} + +#endif + + /* * Normal function to fix up a mapping * This function is the default for when an area has no specific @@ -236,22 +658,22 @@ if (addr == area->vm_start && end == area->vm_end) { if (area->vm_ops && area->vm_ops->close) area->vm_ops->close(area); + remove_shared_vm_struct(area); if (area->vm_inode) iput(area->vm_inode); return; } /* Work out to one of the ends */ - if (addr >= area->vm_start && end == area->vm_end) + if (end == area->vm_end) area->vm_end = addr; - if (addr == area->vm_start && end <= area->vm_end) { + else + if (addr == area->vm_start) { area->vm_offset += (end - area->vm_start); area->vm_start = end; } - - /* Unmapping a hole */ - if (addr > area->vm_start && end < area->vm_end) - { + else { + /* Unmapping a hole: area->vm_start < addr <= end < area->vm_end */ /* Add end mapping -- leave beginning for below */ mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL); @@ -278,6 +700,7 @@ if (area->vm_ops && area->vm_ops->close) { area->vm_end = area->vm_start; area->vm_ops->close(area); + remove_shared_vm_struct(area); } insert_vm_struct(current, mpnt); } @@ -295,7 +718,7 @@ */ int do_munmap(unsigned long addr, size_t len) { - struct vm_area_struct *mpnt, **npp, *free; + struct vm_area_struct *mpnt, *prev, *next, **npp, *free; if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr) return -EINVAL; @@ -309,21 +732,20 @@ * every area affected in some way (by any overlap) is put * on the list. If nothing is put on, nothing is affected. */ - npp = ¤t->mm->mmap; - free = NULL; - for (mpnt = *npp; mpnt != NULL; mpnt = *npp) { - unsigned long end = addr+len; - - if ((addr < mpnt->vm_start && end <= mpnt->vm_start) || - (addr >= mpnt->vm_end && end > mpnt->vm_end)) - { - npp = &mpnt->vm_next; - continue; - } + mpnt = find_vma(current, addr); + if (!mpnt) + return 0; + avl_neighbours(mpnt, current->mm->mmap_avl, &prev, &next); + /* we have prev->vm_next == mpnt && mpnt->vm_next = next */ + /* and addr < mpnt->vm_end */ + npp = (prev ? &prev->vm_next : ¤t->mm->mmap); + free = NULL; + for ( ; mpnt && mpnt->vm_start < addr+len; mpnt = *npp) { *npp = mpnt->vm_next; mpnt->vm_next = free; free = mpnt; + avl_remove(mpnt, ¤t->mm->mmap_avl); } if (free == NULL) @@ -341,8 +763,6 @@ mpnt = free; free = free->vm_next; - remove_shared_vm_struct(mpnt); - st = addr < mpnt->vm_start ? mpnt->vm_start : addr; end = addr+len; end = end > mpnt->vm_end ? mpnt->vm_end : end; @@ -358,15 +778,48 @@ return 0; } +/* Build the AVL tree corresponding to the VMA list. */ +void build_mmap_avl(struct task_struct * task) +{ + struct vm_area_struct * vma; + + task->mm->mmap_avl = NULL; + for (vma = task->mm->mmap; vma; vma = vma->vm_next) + avl_insert(vma, &task->mm->mmap_avl); +} + +/* Release all mmaps. */ +void exit_mmap(struct task_struct * task) +{ + struct vm_area_struct * mpnt; + + mpnt = task->mm->mmap; + task->mm->mmap = NULL; + task->mm->mmap_avl = NULL; + while (mpnt) { + struct vm_area_struct * next = mpnt->vm_next; + if (mpnt->vm_ops && mpnt->vm_ops->close) + mpnt->vm_ops->close(mpnt); + remove_shared_vm_struct(mpnt); + if (mpnt->vm_inode) + iput(mpnt->vm_inode); + kfree(mpnt); + mpnt = next; + } +} + /* * Insert vm structure into process list sorted by address * and into the inode's i_mmap ring. */ void insert_vm_struct(struct task_struct *t, struct vm_area_struct *vmp) { - struct vm_area_struct **p, *mpnt, *share; + struct vm_area_struct *share; struct inode * inode; +#if 0 /* equivalent, but slow */ + struct vm_area_struct **p, *mpnt; + p = &t->mm->mmap; while ((mpnt = *p) != NULL) { if (mpnt->vm_start > vmp->vm_start) @@ -377,6 +830,18 @@ } vmp->vm_next = mpnt; *p = vmp; +#else + struct vm_area_struct * prev, * next; + + avl_insert_neighbours(vmp, &t->mm->mmap_avl, &prev, &next); + if ((prev ? prev->vm_next : t->mm->mmap) != next) + printk("insert_vm_struct: tree inconsistent with list\n"); + if (prev) + prev->vm_next = vmp; + else + t->mm->mmap = vmp; + vmp->vm_next = next; +#endif inode = vmp->vm_inode; if (!inode) @@ -417,21 +882,35 @@ } /* - * Merge a list of memory segments if possible. + * Merge the list of memory segments if possible. * Redundant vm_area_structs are freed. * This assumes that the list is ordered by address. + * We don't need to traverse the entire list, only those segments + * which intersect or are adjacent to a given interval. */ -void merge_segments(struct vm_area_struct *mpnt) +void merge_segments (struct task_struct * task, unsigned long start_addr, unsigned long end_addr) { - struct vm_area_struct *prev, *next; + struct vm_area_struct *prev, *mpnt, *next; - if (mpnt == NULL) + mpnt = find_vma(task, start_addr); + if (!mpnt) return; - - for(prev = mpnt, mpnt = mpnt->vm_next; - mpnt != NULL; - prev = mpnt, mpnt = next) - { + avl_neighbours(mpnt, task->mm->mmap_avl, &prev, &next); + /* we have prev->vm_next == mpnt && mpnt->vm_next = next */ + + if (!prev) { + prev = mpnt; + mpnt = next; + } + + /* prev and mpnt cycle through the list, as long as + * start_addr < mpnt->vm_end && prev->vm_start < end_addr + */ + for ( ; mpnt && prev->vm_start < end_addr ; prev = mpnt, mpnt = next) { +#if 0 + printk("looping in merge_segments, mpnt=0x%lX\n", (unsigned long) mpnt); +#endif + next = mpnt->vm_next; /* @@ -461,6 +940,7 @@ * big segment can possibly merge with the next one. * The old unused mpnt is freed. */ + avl_remove(mpnt, &task->mm->mmap_avl); prev->vm_end = mpnt->vm_end; prev->vm_next = mpnt->vm_next; if (mpnt->vm_ops && mpnt->vm_ops->close) { diff -u --recursive --new-file v1.1.82/linux/mm/mprotect.c linux/mm/mprotect.c --- v1.1.82/linux/mm/mprotect.c Mon Jan 16 14:18:33 1995 +++ linux/mm/mprotect.c Wed Jan 18 11:00:23 1995 @@ -172,7 +172,7 @@ asmlinkage int sys_mprotect(unsigned long start, size_t len, unsigned long prot) { - unsigned long end, tmp; + unsigned long nstart, end, tmp; struct vm_area_struct * vma, * next; int error; @@ -186,19 +186,14 @@ return -EINVAL; if (end == start) return 0; - for (vma = current->mm->mmap ; ; vma = vma->vm_next) { - if (!vma) - return -EFAULT; - if (vma->vm_end > start) - break; - } - if (vma->vm_start > start) + vma = find_vma(current, start); + if (!vma || vma->vm_start > start) return -EFAULT; - for ( ; ; ) { + for (nstart = start ; ; ) { unsigned int newflags; - /* Here we know that vma->vm_start <= start < vma->vm_end. */ + /* Here we know that vma->vm_start <= nstart < vma->vm_end. */ newflags = prot | (vma->vm_flags & ~(PROT_READ | PROT_WRITE | PROT_EXEC)); if ((newflags & ~(newflags >> 4)) & 0xf) { @@ -207,22 +202,22 @@ } if (vma->vm_end >= end) { - error = mprotect_fixup(vma, start, end, newflags); + error = mprotect_fixup(vma, nstart, end, newflags); break; } tmp = vma->vm_end; next = vma->vm_next; - error = mprotect_fixup(vma, start, tmp, newflags); + error = mprotect_fixup(vma, nstart, tmp, newflags); if (error) break; - start = tmp; + nstart = tmp; vma = next; - if (!vma || vma->vm_start != start) { + if (!vma || vma->vm_start != nstart) { error = -EFAULT; break; } } - merge_segments(current->mm->mmap); + merge_segments(current, start, end); return error; } diff -u --recursive --new-file v1.1.82/linux/mm/swap.c linux/mm/swap.c --- v1.1.82/linux/mm/swap.c Mon Jan 16 14:18:33 1995 +++ linux/mm/swap.c Wed Jan 18 09:31:40 1995 @@ -381,14 +381,9 @@ /* * Find the proper vm-area */ - vma = p->mm->mmap; - for (;;) { - if (!vma) - return 0; - if (address <= vma->vm_end) - break; - vma = vma->vm_next; - } + vma = find_vma(p, address); + if (!vma) + return 0; if (address < vma->vm_start) address = vma->vm_start; diff -u --recursive --new-file v1.1.82/linux/net/inet/tcp.c linux/net/inet/tcp.c --- v1.1.82/linux/net/inet/tcp.c Wed Jan 11 21:14:30 1995 +++ linux/net/inet/tcp.c Wed Jan 18 20:09:54 1995 @@ -121,6 +121,16 @@ * Linus Torvalds : Fixed BSD port reuse to work first syn * Alan Cox : Reimplemented timers as per the RFC and using multiple * timers for sanity. + * Alan Cox : Small bug fixes, and a lot of new + * comments. + * Alan Cox : Fixed dual reader crash by locking + * the buffers (much like datagram.c) + * Alan Cox : Fixed stuck sockets in probe. A probe + * now gets fed up of retrying without + * (even a no space) answer. + * Alan Cox : Extracted closing code better + * Alan Cox : Fixed the closing state machine to + * resemble the RFC. * * * To Fix: @@ -137,9 +147,18 @@ * could do with it working on IPv4 * User settable/learned rtt/max window/mtu * Cope with MTU/device switches when retransmitting in tcp. + * Fix the window handling to use PR's new code. * - * - * + * Change the fundamental structure to a single send queue maintained + * by TCP (removing the bogus ip stuff [thus fixing mtu drops on + * active routes too]). Cut the queue off in tcp_retransmit/ + * tcp_transmit. + * Change the receive queue to assemble as it goes. This lets us + * dispose of most of tcp_sequence, half of tcp_ack and chunks of + * tcp_data/tcp_read as well as the window shrink crud. + * Seperate out duplicated code - tcp_alloc_skb, tcp_build_ack + * tcp_queue_skb seem obvious routines to extract. + * * 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 the Free Software Foundation; either version @@ -179,6 +198,7 @@ * * TCP_CLOSE socket is finished */ + #include #include #include @@ -206,8 +226,10 @@ #include #include -#undef TCP_FASTPATH - +/* + * The MSL timer is the 'normal' timer. + */ + #define reset_msl_timer(x,y,z) reset_timer(x,y,z) #define SEQ_TICK 3 @@ -216,11 +238,10 @@ static void tcp_close(struct sock *sk, int timeout); -#ifdef TCP_FASTPATH -unsigned long tcp_rx_miss=0, tcp_rx_hit1=0, tcp_rx_hit2=0; -#endif -/* The less said about this the better, but it works and will do for 1.2 */ +/* + * The less said about this the better, but it works and will do for 1.2 + */ static struct wait_queue *master_select_wakeup; @@ -261,20 +282,20 @@ tcp_statistics.TcpCurrEstab++; } -/* This routine picks a TCP windows for a socket based on - the following constraints - - 1. The window can never be shrunk once it is offered (RFC 793) - 2. We limit memory per socket - - For now we use NET2E3's heuristic of offering half the memory - we have handy. All is not as bad as this seems however because - of two things. Firstly we will bin packets even within the window - in order to get the data we are waiting for into the memory limit. - Secondly we bin common duplicate forms at receive time - - Better heuristics welcome -*/ +/* + * This routine picks a TCP windows for a socket based on + * the following constraints + * + * 1. The window can never be shrunk once it is offered (RFC 793) + * 2. We limit memory per socket + * + * For now we use NET2E3's heuristic of offering half the memory + * we have handy. All is not as bad as this seems however because + * of two things. Firstly we will bin packets even within the window + * in order to get the data we are waiting for into the memory limit. + * Secondly we bin common duplicate forms at receive time + * Better heuristics welcome + */ int tcp_select_window(struct sock *sk) { @@ -282,15 +303,19 @@ if(sk->window_clamp) new_window=min(sk->window_clamp,new_window); -/* - * two things are going on here. First, we don't ever offer a - * window less than min(sk->mss, MAX_WINDOW/2). This is the - * receiver side of SWS as specified in RFC1122. - * Second, we always give them at least the window they - * had before, in order to avoid retracting window. This - * is technically allowed, but RFC1122 advises against it and - * in practice it causes trouble. - */ + /* + * Two things are going on here. First, we don't ever offer a + * window less than min(sk->mss, MAX_WINDOW/2). This is the + * receiver side of SWS as specified in RFC1122. + * Second, we always give them at least the window they + * had before, in order to avoid retracting window. This + * is technically allowed, but RFC1122 advises against it and + * in practice it causes trouble. + * + * Fixme: This doesn't correctly handle the case where + * new_window > sk->window but not by enough to allow for the + * shift in sequence space. + */ if (new_window < min(sk->mss, MAX_WINDOW/2) || new_window < sk->window) return(sk->window); return(new_window); @@ -316,6 +341,23 @@ return NULL; } +/* + * Remove a completed connection and return it. This is used by + * tcp_accept() to get connections from the queue. + */ + +static struct sk_buff *tcp_dequeue_established(struct sock *s) +{ + struct sk_buff *skb; + unsigned long flags; + save_flags(flags); + cli(); + skb=tcp_find_established(s); + if(skb!=NULL) + skb_unlink(skb); /* Take it off the queue */ + restore_flags(flags); + return skb; +} /* * This routine closes sockets which have been at least partially @@ -334,20 +376,6 @@ return; } -static struct sk_buff *tcp_dequeue_established(struct sock *s) -{ - struct sk_buff *skb; - unsigned long flags; - save_flags(flags); - cli(); - skb=tcp_find_established(s); - if(skb!=NULL) - skb_unlink(skb); /* Take it off the queue */ - restore_flags(flags); - return skb; -} - - /* * Enter the time wait state. */ @@ -394,10 +422,17 @@ * changing the packet, we have to issue a new IP identifier. */ - iph = (struct iphdr *)(skb->data + dev->hard_header_len); th = (struct tcphdr *)(((char *)iph) + (iph->ihl << 2)); size = skb->len - (((unsigned char *) th) - skb->data); + + /* + * Note: We ought to check for window limits here but + * currently this is done (less efficiently) elsewhere. + * We do need to check for a route change but can't handle + * that until we have the new 1.3.x buffers in. + * + */ iph->id = htons(ip_id_count++); ip_send_check(iph); @@ -444,18 +479,21 @@ /* * Count retransmissions */ + sk->retransmits++; sk->prot->retransmits ++; /* * Only one retransmit requested. */ + if (!all) break; /* * This should cut it off before we send too many packets. */ + if (sk->retransmits >= sk->cong_window) break; skb = skb->link3; @@ -502,6 +540,10 @@ * defined in the protocol as the maximum possible RTT. I guess * we'll have to use something other than TCP to talk to the * University of Mars. + * + * PAWS allows us longer timeouts and large windows, so once + * implemented ftp to mars will work nicely. We will have to fix + * the 120 second clamps though! */ sk->retransmits++; @@ -537,10 +579,59 @@ } /* - * The TCP retransmit timer. + * A write timeout has occured. Process the after effects. */ +static int tcp_write_timeout(struct sock *sk) +{ + /* + * Look for a 'soft' timeout. + */ + if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) + || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) + { + /* + * Attempt to recover if arp has changed (unlikely!) or + * a route has shifted (not supported prior to 1.3). + */ + arp_destroy (sk->daddr, 0); + ip_route_check (sk->daddr); + } + /* + * Has it gone just too far ? + */ + if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) + { + sk->err = ETIMEDOUT; + /* + * Time wait the socket + */ + if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 || sk->state == TCP_CLOSING) + { + tcp_set_state(sk,TCP_TIME_WAIT); + reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + } + else + { + /* + * Clean up time. + */ + sk->prot->close (sk, 1); + return 0; + } + } + return 1; +} +/* + * The TCP retransmit timer. This lacks a few small details. + * + * 1. An initial rtt timeout on the probe0 should cause what we can + * of the first write queue buffer to be split and sent. + * 2. On a 'major timeout' as defined by RFC1122 we shouldn't report + * ETIMEDOUT if we know an additional 'soft' error caused this. + * tcp_err should save a 'soft error' for us. + */ static void retransmit_timer(unsigned long data) { @@ -554,7 +645,8 @@ cli(); if (sk->inuse || in_bh) { - sk->retransmit_timer.expires = 10; + /* Try again in 1 second */ + sk->retransmit_timer.expires = HZ; add_timer(&sk->retransmit_timer); sti(); return; @@ -579,7 +671,8 @@ /* Window probing */ case TIME_PROBE0: tcp_send_probe0(sk); - release_sock (sk); + if(tcp_write_timeout(sk)) + release_sock (sk); break; /* Retransmitting */ case TIME_WRITE: @@ -599,6 +692,10 @@ } else { + /* + * Kicked by a delayed ack. Reset timer + * correctly now + */ if (jiffies < skb->when + sk->rto) { reset_xmit_timer (sk, TIME_WRITE, skb->when + sk->rto - jiffies); @@ -607,29 +704,12 @@ break; } restore_flags(flags); - /* printk("timer: seq %d retrans %d out %d cong %d\n", sk->send_head->h.seq, - sk->retransmits, sk->packets_out, sk->cong_window); */ + /* + * Retransmission + */ sk->prot->retransmit (sk, 0); - if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) - || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) - { - arp_destroy (sk->daddr, 0); - ip_route_check (sk->daddr); - } - if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) - { - sk->err = ETIMEDOUT; - if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 || sk->state == TCP_CLOSING) - { - sk->state = TCP_TIME_WAIT; - reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); - } - else - { - sk->prot->close (sk, 1); - break; - } - } + if(!tcp_write_timeout(sk)) + break; } release_sock (sk); break; @@ -646,37 +726,8 @@ if (sk->prot->write_wakeup) sk->prot->write_wakeup (sk); sk->retransmits++; - if (sk->shutdown == SHUTDOWN_MASK) - { - sk->prot->close (sk, 1); - sk->state = TCP_CLOSE; - } - if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7)) - || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) - { - arp_destroy (sk->daddr, 0); - ip_route_check (sk->daddr); + if(tcp_write_timeout(sk)) release_sock (sk); - break; - } - if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) - { - arp_destroy (sk->daddr, 0); - sk->err = ETIMEDOUT; - if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) - { - sk->state = TCP_TIME_WAIT; - if (!sk->dead) - sk->state_change(sk); - release_sock (sk); - } - else - { - sk->prot->close (sk, 1); - } - break; - } - release_sock (sk); break; default: printk ("rexmit_timer: timer expired - reason unknown\n"); @@ -752,7 +803,8 @@ /* * Walk down the receive queue counting readable data until we hit the end or we find a gap - * in the received data queue (ie a frame missing that needs sending to us) + * in the received data queue (ie a frame missing that needs sending to us). Not + * sorting using two queues as data arrives makes life so much harder. */ static int tcp_readable(struct sock *sk) @@ -779,7 +831,10 @@ counted = sk->copied_seq; /* Where we are at the moment */ amount = 0; - /* Do until a push or until we are out of data. */ + /* + * Do until a push or until we are out of data. + */ + do { if (before(counted, skb->h.th->seq)) /* Found a hole so stops here */ @@ -823,68 +878,77 @@ return(amount); } - /* - * Wait for a TCP event. Note the oddity with SEL_IN and reading. The - * listening socket has a receive queue of sockets to accept. + * LISTEN is a special case for select.. */ +static int tcp_listen_select(struct sock *sk, int sel_type, select_table *wait) +{ + if (sel_type == SEL_IN) { + int retval; + + sk->inuse = 1; + retval = (tcp_find_established(sk) != NULL); + release_sock(sk); + if (!retval) + select_wait(&master_select_wakeup,wait); + return retval; + } + return 0; +} + -static int do_tcp_select(struct sock *sk, int sel_type, select_table *wait) +/* + * Wait for a TCP event. + * + * Note that we don't need to set "sk->inuse", as the upper select layers + * take care of normal races (between the test and the event) and we don't + * go look at any of the socket buffers directly. + */ +static int tcp_select(struct sock *sk, int sel_type, select_table *wait) { - switch(sel_type) - { - case SEL_IN: - if (sk->err) - return 1; - if (sk->state == TCP_LISTEN) { - select_wait(&master_select_wakeup,wait); - return (tcp_find_established(sk) != NULL); - } - if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) - return 0; - if (sk->acked_seq != sk->copied_seq) - return 1; - if (sk->shutdown & RCV_SHUTDOWN) - return 1; - return 0; + if (sk->state == TCP_LISTEN) + return tcp_listen_select(sk, sel_type, wait); - case SEL_OUT: - if (sk->shutdown & SEND_SHUTDOWN) { - /* FIXME: should this return an error? */ - return 0; - } + switch(sel_type) { + case SEL_IN: + if (sk->err) + return 1; + if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) + break; - /* - * This is now right thanks to a small fix - * by Matt Dillon. - */ + if (sk->shutdown & RCV_SHUTDOWN) + return 1; - if (sk->prot->wspace(sk) >= sk->mtu+128+sk->prot->max_header) - { - /* This should cause connect to work ok. */ - if (sk->state == TCP_SYN_RECV || - sk->state == TCP_SYN_SENT) return 0; - return 1; - } - return 0; + if (sk->acked_seq == sk->copied_seq) + break; - case SEL_EX: - if (sk->err || sk->urg_data) - return 1; + if (sk->urg_seq != sk->copied_seq || + sk->acked_seq != sk->copied_seq+1 || + sk->urginline || !sk->urg_data) + return 1; + break; + + case SEL_OUT: + if (sk->shutdown & SEND_SHUTDOWN) return 0; - } - return 0; -} + if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) + break; + /* + * This is now right thanks to a small fix + * by Matt Dillon. + */ -static int tcp_select(struct sock *sk, int sel_type, select_table *wait) -{ - int retval; + if (sk->prot->wspace(sk) < sk->mtu+128+sk->prot->max_header) + break; + return 1; - sk->inuse = 1; + case SEL_EX: + if (sk->err || sk->urg_data) + return 1; + break; + } select_wait(sk->sleep, wait); - retval = do_tcp_select(sk, sel_type, wait); - release_sock(sk); - return retval; + return 0; } int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg) @@ -1037,15 +1101,26 @@ return; } +/* + * This is the main buffer sending routine. We queue the buffer + * having checked it is sane seeming. + */ + static void tcp_send_skb(struct sock *sk, struct sk_buff *skb) { int size; struct tcphdr * th = skb->h.th; - /* length of packet (not counting length of pre-tcp headers) */ + /* + * length of packet (not counting length of pre-tcp headers) + */ + size = skb->len - ((unsigned char *) th - skb->data); - /* sanity check it.. */ + /* + * Sanity check it.. + */ + if (size < sizeof(struct tcphdr) || size > skb->len) { printk("tcp_send_skb: bad skb (skb = %p, data = %p, th = %p, len = %lu)\n", @@ -1054,7 +1129,11 @@ return; } - /* If we have queued a header size packet.. */ + /* + * If we have queued a header size packet.. (these crash a few + * tcp stacks if ack is not set) + */ + if (size == sizeof(struct tcphdr)) { /* If its got a syn or fin its notionally included in the size..*/ @@ -1066,9 +1145,21 @@ } } + /* + * Actual processing. + */ + tcp_statistics.TcpOutSegs++; - skb->h.seq = ntohl(th->seq) + size - 4*th->doff; + + /* + * We must queue if + * + * a) The right edge of this frame exceeds the window + * b) We are retransmitting (Nagle's rule) + * c) We have too many packets 'in flight' + */ + if (after(skb->h.seq, sk->window_seq) || (sk->retransmits && sk->ip_xmit_timeout == TIME_WRITE) || sk->packets_out >= sk->cong_window) @@ -1082,24 +1173,58 @@ skb_unlink(skb); } skb_queue_tail(&sk->write_queue, skb); + + /* + * If we don't fit we have to start the zero window + * probes. This is broken - we really need to do a partial + * send _first_ (This is what causes the Cisco and PC/TCP + * grief). + */ + if (before(sk->window_seq, sk->write_queue.next->h.seq) && - sk->send_head == NULL && - sk->ack_backlog == 0) + sk->send_head == NULL && sk->ack_backlog == 0) reset_xmit_timer(sk, TIME_PROBE0, sk->rto); } else { + /* + * This is going straight out + */ + th->ack_seq = ntohl(sk->acked_seq); th->window = ntohs(tcp_select_window(sk)); tcp_send_check(th, sk->saddr, sk->daddr, size, sk); sk->sent_seq = sk->write_seq; + + /* + * This is mad. The tcp retransmit queue is put together + * by the ip layer. This causes half the problems with + * unroutable FIN's and other things. + */ + sk->prot->queue_xmit(sk, skb->dev, skb, 0); + + /* + * Set for next retransmit based on expected ACK time. + * FIXME: We set this every time which means our + * retransmits are really about a window behind. + */ + reset_xmit_timer(sk, TIME_WRITE, sk->rto); } } +/* + * Locking problems lead us to a messy situation where we can have + * multiple partially complete buffers queued up. This is really bad + * as we don't want to be sending partial buffers. Fix this with + * a semaphore or similar to lock tcp_write per socket. + * + * These routines are pretty self descriptive. + */ + struct sk_buff * tcp_dequeue_partial(struct sock * sk) { struct sk_buff * skb; @@ -1116,6 +1241,10 @@ return skb; } +/* + * Empty the partial queue + */ + static void tcp_send_partial(struct sock *sk) { struct sk_buff *skb; @@ -1126,6 +1255,10 @@ tcp_send_skb(sk, skb); } +/* + * Queue a partial frame + */ + void tcp_enqueue_partial(struct sk_buff * skb, struct sock * sk) { struct sk_buff * tmp; @@ -1138,6 +1271,9 @@ del_timer(&sk->partial_timer); sk->partial = skb; init_timer(&sk->partial_timer); + /* + * Wait up to 1 second for the buffer to fill. + */ sk->partial_timer.expires = HZ; sk->partial_timer.function = (void (*)(unsigned long)) tcp_send_partial; sk->partial_timer.data = (unsigned long) sk; @@ -1163,6 +1299,7 @@ if(sk->zapped) return; /* We have been reset, we may not send again */ + /* * We need to grab some memory, and put together an ack, * and then put it into the queue to be sent. @@ -1171,21 +1308,34 @@ buff = sk->prot->wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC); if (buff == NULL) { - /* Force it to send an ack. */ + /* + * Force it to send an ack. We don't have to do this + * (ACK is unreliable) but its much better use of + * bandwidth on slow links to send a spare ack than + * resend packets. + */ + sk->ack_backlog++; if (sk->ip_xmit_timeout != TIME_WRITE && tcp_connected(sk->state)) { - reset_xmit_timer(sk, TIME_WRITE, 10); + reset_xmit_timer(sk, TIME_WRITE, HZ); } return; } + /* + * Assemble a suitable TCP frame + */ + buff->len = sizeof(struct tcphdr); buff->sk = sk; buff->localroute = sk->localroute; t1 =(struct tcphdr *) buff->data; - /* Put in the IP header and routing stuff. */ + /* + * Put in the IP header and routing stuff. + */ + tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev, IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl); if (tmp < 0) @@ -1197,8 +1347,7 @@ buff->len += tmp; t1 =(struct tcphdr *)((char *)t1 +tmp); - /* FIXME: */ - memcpy(t1, th, sizeof(*t1)); /* this should probably be removed */ + memcpy(t1, th, sizeof(*t1)); /* * Swap the send and the receive. @@ -1217,6 +1366,13 @@ t1->syn = 0; t1->psh = 0; t1->fin = 0; + + /* + * If we have nothing queued for transmit and the transmit timer + * is on we are just doing an ACK timeout and need to switch + * to a keepalive. + */ + if (ack == sk->acked_seq) { sk->ack_backlog = 0; @@ -1232,6 +1388,11 @@ } } } + + /* + * Fill in the packet and send it + */ + t1->ack_seq = ntohl(ack); t1->doff = sizeof(*t1)/4; tcp_send_check(t1, sk->saddr, daddr, sizeof(*t1), sk); @@ -1249,7 +1410,6 @@ extern __inline int tcp_build_header(struct tcphdr *th, struct sock *sk, int push) { - /* FIXME: want to get rid of this. */ memcpy(th,(void *) &(sk->dummy_th), sizeof(*th)); th->seq = htonl(sk->write_seq); th->psh =(push == 0) ? 1 : 0; @@ -1297,9 +1457,9 @@ return(tmp); } - /* - * First thing we do is make sure that we are established. - */ + /* + * First thing we do is make sure that we are established. + */ if (sk->shutdown & SEND_SHUTDOWN) { @@ -1311,10 +1471,9 @@ return(-EPIPE); } - - /* - * Wait for a connection to finish. - */ + /* + * Wait for a connection to finish. + */ while(sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) { @@ -1597,6 +1756,9 @@ return(copied); } +/* + * This is just a wrapper. + */ static int tcp_sendto(struct sock *sk, unsigned char *from, int len, int nonblock, unsigned flags, @@ -1618,6 +1780,11 @@ } +/* + * Send an ack if one is backlogged at this point. Ought to merge + * this with tcp_send_ack(). + */ + static void tcp_read_wakeup(struct sock *sk) { int tmp; @@ -1643,7 +1810,7 @@ if (buff == NULL) { /* Try again real soon. */ - reset_xmit_timer(sk, TIME_WRITE, 10); + reset_xmit_timer(sk, TIME_WRITE, HZ); return; } @@ -1711,13 +1878,13 @@ left = sk->prot->rspace(sk); /* - * We have to loop through all the buffer headers, - * and try to free up all the space we can. + * We have to loop through all the buffer headers, + * and try to free up all the space we can. */ while((skb=skb_peek(&sk->receive_queue)) != NULL) { - if (!skb->used) + if (!skb->used || skb->users) break; skb_unlink(skb); skb->sk = sk; @@ -1727,10 +1894,10 @@ restore_flags(flags); /* - * FIXME: - * At this point we should send an ack if the difference - * in the window, and the amount of space is bigger than - * TCP_WINDOW_DIFF. + * FIXME: + * At this point we should send an ack if the difference + * in the window, and the amount of space is bigger than + * TCP_WINDOW_DIFF. */ if(sk->debug) @@ -1779,14 +1946,19 @@ /* - * Handle reading urgent data. + * Handle reading urgent data. BSD has very simple semantics for + * this, no blocking and very strange errors 8) */ static int tcp_read_urg(struct sock * sk, int nonblock, unsigned char *to, int len, unsigned flags) { + /* + * No URG data to read + */ if (sk->urginline || !sk->urg_data || sk->urg_data == URG_READ) - return -EINVAL; + return -EINVAL; /* Yes this is right ! */ + if (sk->err) { int tmp = -sk->err; @@ -1841,17 +2013,29 @@ struct wait_queue wait = { current, NULL }; int copied = 0; unsigned long peek_seq; - unsigned long *seq; + volatile unsigned long *seq; /* So gcc doesnt overoptimise */ unsigned long used; - /* This error should be checked. */ + /* + * This error should be checked. + */ + if (sk->state == TCP_LISTEN) return -ENOTCONN; - /* Urgent data needs to be handled specially. */ + /* + * Urgent data needs to be handled specially. + */ + if (flags & MSG_OOB) return tcp_read_urg(sk, nonblock, to, len, flags); + /* + * Copying sequence to update. This is volatile to handle + * the multi-reader case neatly (memcpy_to/fromfs might be + * inline and thus not flush cached variables otherwise). + */ + peek_seq = sk->copied_seq; seq = &sk->copied_seq; if (flags & MSG_PEEK) @@ -1867,9 +2051,14 @@ /* * Are we at urgent data? Stop if we have read anything. */ + if (copied && sk->urg_data && sk->urg_seq == *seq) break; + /* + * Next get a buffer. + */ + current->state = TASK_INTERRUPTIBLE; skb = skb_peek(&sk->receive_queue); @@ -1940,11 +2129,26 @@ continue; found_ok_skb: - /* Ok so how much can we use ? */ + /* + * Lock the buffer. We can be fairly relaxed as + * an interrupt will never steal a buffer we are + * using unless I've missed something serious in + * tcp_data. + */ + + skb->users++; + + /* + * Ok so how much can we use ? + */ + used = skb->len - offset; if (len < used) used = len; - /* do we have urgent data here? */ + /* + * Do we have urgent data here? + */ + if (sk->urg_data) { unsigned long urg_offset = sk->urg_seq - *seq; @@ -1963,17 +2167,43 @@ used = urg_offset; } } - /* Copy it */ + + /* + * Copy it - We _MUST_ update *seq first so that we + * don't ever double read when we have dual readers + */ + + *seq += used; + + /* + * This memcpy_tofs can sleep. If it sleeps and we + * do a second read it relies on the skb->users to avoid + * a crash when cleanup_rbuf() gets called. + */ + memcpy_tofs(to,((unsigned char *)skb->h.th) + skb->h.th->doff*4 + offset, used); copied += used; len -= used; to += used; - *seq += used; + + /* + * We now will not sleep again until we are finished + * with skb. Sorry if you are doing the SMP port + * but you'll just have to fix it neatly ;) + */ + + skb->users --; + if (after(sk->copied_seq,sk->urg_seq)) sk->urg_data = 0; if (used + offset < skb->len) continue; + + /* + * Process the FIN. + */ + if (skb->h.th->fin) goto found_fin_ok; if (flags & MSG_PEEK) @@ -1985,6 +2215,11 @@ ++*seq; if (flags & MSG_PEEK) break; + + /* + * All is done + */ + skb->used = 1; sk->shutdown |= RCV_SHUTDOWN; break; @@ -1999,67 +2234,92 @@ return copied; } - /* - * Shutdown the sending side of a connection. + * State processing on a close. This implements the state shift for + * sending our FIN frame. Note that we only send a FIN for some + * states. A shutdown() may have already sent the FIN, or we may be + * closed. */ - -void tcp_shutdown(struct sock *sk, int how) + +static int tcp_close_state(struct sock *sk) { - struct sk_buff *buff; - struct tcphdr *t1, *th; - struct proto *prot; - int tmp; - struct device *dev = NULL; - - /* - * We need to grab some memory, and put together a FIN, - * and then put it into the queue to be sent. - * FIXME: - * - * Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92. - * Most of this is guesswork, so maybe it will work... - */ - - if (!(how & SEND_SHUTDOWN)) - return; - - /* - * If we've already sent a FIN, return. - */ - - if (sk->state == TCP_FIN_WAIT1 || - sk->state == TCP_FIN_WAIT2 || - sk->state == TCP_CLOSING || - sk->state == TCP_LAST_ACK || - sk->state == TCP_TIME_WAIT - ) + int ns=TCP_CLOSE; + int send_fin=0; + switch(sk->state) { - return; - } - sk->inuse = 1; - - /* - * flag that the sender has shutdown - */ - - sk->shutdown |= SEND_SHUTDOWN; - + case TCP_SYN_SENT: /* No SYN back, no FIN needed */ + break; + case TCP_SYN_RECV: + case TCP_ESTABLISHED: /* Closedown begin */ + ns=TCP_FIN_WAIT1; + send_fin=1; + break; + case TCP_FIN_WAIT1: /* Already closing, or FIN sent: no change */ + case TCP_FIN_WAIT2: + case TCP_CLOSING: + ns=sk->state; + break; + case TCP_CLOSE: + case TCP_LISTEN: + break; + case TCP_CLOSE_WAIT: /* They have FIN'd us. We send our FIN and + wait only for the ACK */ + ns=TCP_LAST_ACK; + send_fin=1; + } + + tcp_set_state(sk,ns); + /* - * Clear out any half completed packets. - */ + * This is a (useful) BSD violating of the RFC. There is a + * problem with TCP as specified in that the other end could + * keep a socket open forever with no application left this end. + * We use a 3 minute timeout (about the same as BSD) then kill + * our end. If they send after that then tough - BUT: long enough + * that we won't make the old 4*rto = almost no time - whoops + * reset mistake. + */ + if(sk->dead && ns==TCP_FIN_WAIT2) + { + int timer_active=del_timer(&sk->timer); + if(timer_active) + add_timer(&sk->timer); + else + reset_msl_timer(sk, TIME_CLOSE, TCP_FIN_TIMEOUT); + } + + return send_fin; +} - if (sk->partial) - tcp_send_partial(sk); +/* + * Send a fin. + */ - prot =(struct proto *)sk->prot; - th =(struct tcphdr *)&sk->dummy_th; +static void tcp_send_fin(struct sock *sk) +{ + struct proto *prot =(struct proto *)sk->prot; + struct tcphdr *th =(struct tcphdr *)&sk->dummy_th; + struct tcphdr *t1; + struct sk_buff *buff; + struct device *dev=NULL; + int tmp; + release_sock(sk); /* in case the malloc sleeps. */ + buff = prot->wmalloc(sk, MAX_RESET_SIZE,1 , GFP_KERNEL); + sk->inuse = 1; + if (buff == NULL) + { + /* This is a disaster if it occurs */ + printk("tcp_send_fin: Impossible malloc failure"); return; - sk->inuse = 1; + } + /* + * Administrivia + */ + buff->sk = sk; buff->len = sizeof(*t1); buff->localroute = sk->localroute; @@ -2076,27 +2336,19 @@ { /* * Finish anyway, treat this as a send that got lost. - * - * Enter FIN_WAIT1 on normal shutdown, which waits for - * written data to be completely acknowledged along - * with an acknowledge to our FIN. - * - * Enter FIN_WAIT2 on abnormal shutdown -- close before - * connection established. + * (Not good). */ + buff->free = 1; prot->wfree(sk,buff->mem_addr, buff->mem_len); - - if (sk->state == TCP_ESTABLISHED) - tcp_set_state(sk,TCP_FIN_WAIT1); - else if(sk->state == TCP_CLOSE_WAIT) - tcp_set_state(sk,TCP_LAST_ACK); - else - tcp_set_state(sk,TCP_FIN_WAIT2); - - release_sock(sk); + sk->write_seq++; return; } + + /* + * We ought to check if the end of the queue is a buffer and + * if so simply add the fin to that buffer, not send it ahead. + */ t1 =(struct tcphdr *)((char *)t1 +tmp); buff->len += tmp; @@ -2123,7 +2375,7 @@ buff->free = 0; if (buff->next != NULL) { - printk("tcp_shutdown: next != NULL\n"); + printk("tcp_send_fin: next != NULL\n"); skb_unlink(buff); } skb_queue_tail(&sk->write_queue, buff); @@ -2134,14 +2386,61 @@ sk->prot->queue_xmit(sk, dev, buff, 0); reset_xmit_timer(sk, TIME_WRITE, sk->rto); } +} - if (sk->state == TCP_ESTABLISHED) - tcp_set_state(sk,TCP_FIN_WAIT1); - else if (sk->state == TCP_CLOSE_WAIT) - tcp_set_state(sk,TCP_LAST_ACK); - else - tcp_set_state(sk,TCP_FIN_WAIT2); +/* + * Shutdown the sending side of a connection. Much like close except + * that we don't receive shut down or set sk->dead=1. + */ + +void tcp_shutdown(struct sock *sk, int how) +{ + /* + * We need to grab some memory, and put together a FIN, + * and then put it into the queue to be sent. + * Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92. + */ + + if (!(how & SEND_SHUTDOWN)) + return; + + /* + * If we've already sent a FIN, or its a closed state + */ + + if (sk->state == TCP_FIN_WAIT1 || + sk->state == TCP_FIN_WAIT2 || + sk->state == TCP_CLOSING || + sk->state == TCP_LAST_ACK || + sk->state == TCP_TIME_WAIT || + sk->state == TCP_CLOSE || + sk->state == TCP_LISTEN + ) + { + return; + } + sk->inuse = 1; + /* + * flag that the sender has shutdown + */ + + sk->shutdown |= SEND_SHUTDOWN; + + /* + * Clear out any half completed packets. + */ + + if (sk->partial) + tcp_send_partial(sk); + + /* + * FIN if needed + */ + + if(tcp_close_state(sk)) + tcp_send_fin(sk); + release_sock(sk); } @@ -2626,17 +2925,22 @@ static void tcp_close(struct sock *sk, int timeout) { - struct sk_buff *buff; - struct tcphdr *t1, *th; - struct proto *prot; - struct device *dev=NULL; - int tmp; - /* * We need to grab some memory, and put together a FIN, * and then put it into the queue to be sent. */ + sk->inuse = 1; + + if(sk->state == TCP_LISTEN) + { + /* Special case */ + tcp_set_state(sk, TCP_CLOSE); + tcp_close_pending(sk, timeout); + release_sock(sk); + return; + } + sk->keepopen = 1; sk->shutdown = SHUTDOWN_MASK; @@ -2645,22 +2949,16 @@ if (timeout == 0) { + struct sk_buff *skb; + /* * We need to flush the recv. buffs. We do this only on the * descriptor close, not protocol-sourced closes, because the * reader process may not have drained the data yet! */ - - if (skb_peek(&sk->receive_queue) != NULL) - { - struct sk_buff *skb; - if(sk->debug) - printk("Clean rcv queue\n"); - while((skb=skb_dequeue(&sk->receive_queue))!=NULL) - kfree_skb(skb, FREE_READ); - if(sk->debug) - printk("Cleaned.\n"); - } + + while((skb=skb_dequeue(&sk->receive_queue))!=NULL) + kfree_skb(skb, FREE_READ); } /* @@ -2668,175 +2966,45 @@ */ if (sk->partial) - { tcp_send_partial(sk); + + /* + * Timeout is not the same thing - however the code likes + * to send both the same way (sigh). + */ + + if(timeout) + { + /* + * Time wait to avoid port reusage accidents if + * appropriate. If we have timed out from one + * of these states then move straight to close. + */ + + if( sk->state == TCP_TIME_WAIT || sk->state == TCP_LAST_ACK + || sk->state == TCP_SYN_SENT || sk->state == TCP_CLOSE) + tcp_set_state(sk, TCP_CLOSE); /* Dead */ + else + tcp_time_wait(sk); } - - switch(sk->state) + else { - case TCP_FIN_WAIT1: - case TCP_FIN_WAIT2: - case TCP_CLOSING: - /* - * These states occur when we have already closed out - * our end. If there is no timeout, we do not do - * anything. We may still be in the middle of sending - * the remainder of our buffer, for example... - * resetting the timer would be inappropriate. - * - * XXX if retransmit count reaches limit, is tcp_close() - * called with timeout == 1 ? if not, we need to fix that. - */ - if (!timeout) { - int timer_active; - - timer_active = del_timer(&sk->timer); - if (timer_active) - add_timer(&sk->timer); - else - reset_msl_timer(sk, TIME_CLOSE, 4 * sk->rto); - } - if (timeout) - tcp_time_wait(sk); - release_sock(sk); - return; /* break causes a double release - messy */ - case TCP_TIME_WAIT: - case TCP_LAST_ACK: - /* - * A timeout from these states terminates the TCB. - */ - if (timeout) - { - tcp_set_state(sk,TCP_CLOSE); - } - release_sock(sk); - return; - case TCP_LISTEN: - /* we need to drop any sockets which have been connected, - but have not yet been accepted. */ - tcp_set_state(sk,TCP_CLOSE); - tcp_close_pending(sk, timeout); - release_sock(sk); - return; - case TCP_CLOSE: - release_sock(sk); - return; - case TCP_CLOSE_WAIT: - case TCP_ESTABLISHED: - case TCP_SYN_SENT: - case TCP_SYN_RECV: - prot =(struct proto *)sk->prot; - th =(struct tcphdr *)&sk->dummy_th; - buff = prot->wmalloc(sk, MAX_FIN_SIZE, 1, GFP_ATOMIC); - if (buff == NULL) - { - /* This will force it to try again later. */ - /* Or it would have if someone released the socket - first. Anyway it might work now */ - release_sock(sk); - if (sk->state != TCP_CLOSE_WAIT) - tcp_set_state(sk,TCP_ESTABLISHED); - reset_msl_timer(sk, TIME_CLOSE, 100); - return; - } - buff->sk = sk; - buff->free = 0; - buff->len = sizeof(*t1); - buff->localroute = sk->localroute; - t1 =(struct tcphdr *) buff->data; - - /* - * Put in the IP header and routing stuff. - */ - tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev, - IPPROTO_TCP, sk->opt, - sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl); - if (tmp < 0) - { - sk->write_seq++; /* Very important 8) */ - kfree_skb(buff,FREE_WRITE); - - /* - * Enter FIN_WAIT1 to await completion of - * written out data and ACK to our FIN. - */ - - if(sk->state==TCP_ESTABLISHED) - tcp_set_state(sk,TCP_FIN_WAIT1); - else - tcp_set_state(sk,TCP_FIN_WAIT2); - reset_msl_timer(sk, TIME_CLOSE,4*sk->rto); - if(timeout) - tcp_time_wait(sk); - - release_sock(sk); - return; - } - - t1 =(struct tcphdr *)((char *)t1 +tmp); - buff->len += tmp; - buff->dev = dev; - memcpy(t1, th, sizeof(*t1)); - t1->seq = ntohl(sk->write_seq); - sk->write_seq++; - buff->h.seq = sk->write_seq; - t1->ack = 1; - - /* - * Ack everything immediately from now on. - */ - - sk->delay_acks = 0; - t1->ack_seq = ntohl(sk->acked_seq); - t1->window = ntohs(sk->window=tcp_select_window(sk)); - t1->fin = 1; - t1->rst = 0; - t1->doff = sizeof(*t1)/4; - tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk); - - tcp_statistics.TcpOutSegs++; - - if (skb_peek(&sk->write_queue) == NULL) - { - sk->sent_seq = sk->write_seq; - prot->queue_xmit(sk, dev, buff, 0); - reset_xmit_timer(sk, TIME_WRITE, sk->rto); - } - else - { - reset_xmit_timer(sk, TIME_WRITE, sk->rto); - if (buff->next != NULL) - { - printk("tcp_close: next != NULL\n"); - skb_unlink(buff); - } - skb_queue_tail(&sk->write_queue, buff); - } - - /* - * If established (normal close), enter FIN_WAIT1. - * If in CLOSE_WAIT, enter LAST_ACK - * If in CLOSING, remain in CLOSING - * otherwise enter FIN_WAIT2 - */ - - if (sk->state == TCP_ESTABLISHED) - tcp_set_state(sk,TCP_FIN_WAIT1); - else if (sk->state == TCP_CLOSE_WAIT) - tcp_set_state(sk,TCP_LAST_ACK); - else if (sk->state != TCP_CLOSING) - tcp_set_state(sk,TCP_FIN_WAIT2); + if(tcp_close_state(sk)==1) + { + tcp_send_fin(sk); + } } release_sock(sk); } /* - * This routine takes stuff off of the write queue, - * and puts it in the xmit queue. + * This routine takes stuff off of the write queue, + * and puts it in the xmit queue. This happens as incoming acks + * open up the remote window for us. */ -static void -tcp_write_xmit(struct sock *sk) + +static void tcp_write_xmit(struct sock *sk) { struct sk_buff *skb; @@ -2848,6 +3016,14 @@ if(sk->zapped) return; + /* + * Anything on the transmit queue that fits the window can + * be added providing we are not + * + * a) retransmitting (Nagle's rule) + * b) exceeding our congestion window. + */ + while((skb = skb_peek(&sk->write_queue)) != NULL && before(skb->h.seq, sk->window_seq + 1) && (sk->retransmits == 0 || @@ -2857,9 +3033,18 @@ { IS_SKB(skb); skb_unlink(skb); - /* See if we really need to send the packet. */ + + /* + * See if we really need to send the packet. + */ + if (before(skb->h.seq, sk->rcv_ack_seq +1)) { + /* + * This is acked data. We can discard it. This + * cannot currently occur. + */ + sk->retransmits = 0; kfree_skb(skb, FREE_WRITE); if (!sk->dead) @@ -2888,7 +3073,17 @@ tcp_send_check(th, sk->saddr, sk->daddr, size, sk); sk->sent_seq = skb->h.seq; + + /* + * IP manages our queue for some crazy reason + */ + sk->prot->queue_xmit(sk, skb->dev, skb, skb->free); + + /* + * Again we slide the timer wrongly + */ + reset_xmit_timer(sk, TIME_WRITE, sk->rto); } } @@ -2914,20 +3109,37 @@ if(sk->zapped) return(1); /* Dead, cant ack any more so why bother */ + /* + * Have we discovered a larger window + */ + ack = ntohl(th->ack_seq); + if (ntohs(th->window) > sk->max_window) { sk->max_window = ntohs(th->window); #ifdef CONFIG_INET_PCTCP + /* Hack because we don't send partial packets to non SWS + handling hosts */ sk->mss = min(sk->max_window>>1, sk->mtu); #else sk->mss = min(sk->max_window, sk->mtu); #endif } + /* + * We have dropped back to keepalive timeouts. Thus we have + * no retransmits pending. + */ + if (sk->retransmits && sk->ip_xmit_timeout == TIME_KEEPOPEN) sk->retransmits = 0; + /* + * If the ack is newer than sent or older than previous acks + * then we can probably ignore it. + */ + if (after(ack, sk->sent_seq) || before(ack, sk->rcv_ack_seq)) { if(sk->debug) @@ -2941,6 +3153,11 @@ { return(0); } + + /* + * Restart the keepalive timer. + */ + if (sk->keepopen) { if(sk->ip_xmit_timeout==TIME_KEEPOPEN) @@ -2949,10 +3166,16 @@ return(1); } + /* + * If there is data set flag 1 + */ + if (len != th->doff*4) flag |= 1; - /* See if our window has been shrunk. */ + /* + * See if our window has been shrunk. + */ if (after(sk->window_seq, ack+ntohs(th->window))) { @@ -2976,7 +3199,7 @@ * queue and a smarter send routine when we send all. */ - flag |= 4; + flag |= 4; /* Window changed */ sk->window_seq = ack + ntohs(th->window); cli(); @@ -3030,9 +3253,16 @@ sk->packets_out= 0; } + /* + * Update the right hand window edge of the host + */ + sk->window_seq = ack + ntohs(th->window); - /* We don't want too many packets out there. */ + /* + * We don't want too many packets out there. + */ + if (sk->ip_xmit_timeout == TIME_WRITE && sk->cong_window < 2048 && after(ack, sk->rcv_ack_seq)) { @@ -3066,6 +3296,10 @@ } } + /* + * Remember the highest ack received. + */ + sk->rcv_ack_seq = ack; /* @@ -3076,10 +3310,15 @@ if (sk->ip_xmit_timeout == TIME_PROBE0) { + sk->retransmits = 0; /* Our probe was answered */ + + /* + * Was it a usable window open ? + */ + if (skb_peek(&sk->write_queue) != NULL && /* should always be non-null */ ! before (sk->window_seq, sk->write_queue.next->h.seq)) { - sk->retransmits = 0; sk->backoff = 0; /* @@ -3106,6 +3345,12 @@ if (sk->send_head->link3 && after(sk->send_head->h.seq, sk->send_head->link3->h.seq)) printk("INET: tcp.c: *** bug send_list out of order.\n"); + + /* + * If our packet is before the ack sequence we can + * discard it as its confirmed to have arrived the other end. + */ + if (before(sk->send_head->h.seq, ack+1)) { struct sk_buff *oskb; @@ -3124,7 +3369,7 @@ * retransmissions. */ - if (sk->send_head->link3) + if (sk->send_head->link3) /* Any more queued retransmits? */ sk->retransmits = 1; else sk->retransmits = 0; @@ -3154,7 +3399,7 @@ sk->write_space(sk); oskb = sk->send_head; - if (!(flag&2)) + if (!(flag&2)) /* Not retransmitting */ { long m; @@ -3187,7 +3432,8 @@ sk->rto = 20; sk->backoff = 0; } - flag |= (2|4); + flag |= (2|4); /* 2 is really more like 'don't adjust the rtt + In this case as we just set it up */ cli(); oskb = sk->send_head; IS_SKB(oskb); @@ -3235,6 +3481,9 @@ before(sk->write_queue.next->h.seq, sk->rcv_ack_seq + 1)) && sk->packets_out < sk->cong_window) { + /* + * Add more data to the send queue. + */ flag |= 1; tcp_write_xmit(sk); } @@ -3243,6 +3492,9 @@ sk->ack_backlog == 0 && sk->state != TCP_TIME_WAIT) { + /* + * Data to queue but no room. + */ reset_xmit_timer(sk, TIME_PROBE0, sk->rto); } } @@ -3276,8 +3528,8 @@ break; default: /* - * must check send_head, write_queue, and ack_backlog - * to determine which timeout to use. + * Must check send_head, write_queue, and ack_backlog + * to determine which timeout to use. */ if (sk->send_head || skb_peek(&sk->write_queue) != NULL || sk->ack_backlog) { reset_xmit_timer(sk, TIME_WRITE, sk->rto); @@ -3291,6 +3543,11 @@ } } + /* + * We have nothing queued but space to send. Send any partial + * packets immediately (end of Nagle rule application). + */ + if (sk->packets_out == 0 && sk->partial != NULL && skb_peek(&sk->write_queue) == NULL && sk->send_head == NULL) { @@ -3373,7 +3630,7 @@ sk->state_change(sk); if(sk->max_window==0) { - sk->max_window=32; + sk->max_window=32; /* Sanity check */ sk->mss=min(sk->max_window,sk->mtu); } } @@ -3563,6 +3820,8 @@ * We no longer have anyone receiving data on this connection. */ +#ifndef TCP_DONT_RST_SHUTDOWN + if(sk->shutdown & RCV_SHUTDOWN) { /* @@ -3571,7 +3830,7 @@ * BSD stacks still have broken keepalives so we want to * cope with it. */ - + if(skb->len) /* We don't care if its just an ack or a keepalive/window probe */ { @@ -3611,6 +3870,8 @@ } } +#endif + /* * Now we have to walk the chain, and figure out where this one * goes into it. This is set up so that the last packet we received @@ -3623,10 +3884,6 @@ * */ - /* - * This should start at the last one, and then go around forwards. - */ - if (skb_peek(&sk->receive_queue) == NULL) /* Empty queue is easy case */ { skb_queue_head(&sk->receive_queue,skb); @@ -4134,7 +4391,7 @@ sk->retransmit_timer.function=&retransmit_timer; sk->retransmit_timer.data = (unsigned long)sk; reset_xmit_timer(sk, TIME_WRITE, sk->rto); /* Timer for repeating the SYN until an answer */ - sk->retransmits = TCP_RETR2 - TCP_SYN_RETRIES; + sk->retransmits = TCP_SYN_RETRIES; sk->prot->queue_xmit(sk, dev, buff, 0); reset_xmit_timer(sk, TIME_WRITE, sk->rto); @@ -4290,8 +4547,8 @@ skb->sk = NULL; kfree_skb(skb,FREE_READ); /* - * We don't release the socket because it was - * never marked in use. + * We don't release the socket because it was + * never marked in use. */ return(0); } @@ -4510,9 +4767,11 @@ goto rfc_step6; } - /* BSD has a funny hack with TIME_WAIT and fast reuse of a port. There is - a more complex suggestion for fixing these reuse issues in RFC1644 - but not yet ready for general use. Also see RFC1379.*/ + /* + * BSD has a funny hack with TIME_WAIT and fast reuse of a port. There is + * a more complex suggestion for fixing these reuse issues in RFC1644 + * but not yet ready for general use. Also see RFC1379. + */ #define BSD_TIME_WAIT #ifdef BSD_TIME_WAIT @@ -4545,9 +4804,11 @@ #endif } - /* We are now in normal data flow (see the step list in the RFC) */ - /* Note most of these are inline now. I'll inline the lot when - I have time to test it hard and look at what gcc outputs */ + /* + * We are now in normal data flow (see the step list in the RFC) + * Note most of these are inline now. I'll inline the lot when + * I have time to test it hard and look at what gcc outputs + */ if(!tcp_sequence(sk,th,len,opt,saddr,dev)) { @@ -4640,6 +4901,7 @@ /* * Write data can still be transmitted/retransmitted in the * following states. If any other state is encountered, return. + * [listen/close will never occur here anyway] */ if (sk->state != TCP_ESTABLISHED && @@ -4678,9 +4940,10 @@ memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1)); /* - * Use a previous sequence. - * This should cause the other end to send an ack. + * Use a previous sequence. + * This should cause the other end to send an ack. */ + t1->seq = htonl(sk->sent_seq-1); t1->ack = 1; t1->res1= 0; @@ -4694,8 +4957,8 @@ t1->window = ntohs(tcp_select_window(sk)); t1->doff = sizeof(*t1)/4; tcp_send_check(t1, sk->saddr, sk->daddr, sizeof(*t1), sk); - - /* Send it and free it. + /* + * Send it and free it. * This will prevent the timer from automatically being restarted. */ sk->prot->queue_xmit(sk, dev, buff, 1); diff -u --recursive --new-file v1.1.82/linux/net/inet/tcp.h linux/net/inet/tcp.h --- v1.1.82/linux/net/inet/tcp.h Wed Nov 30 21:53:48 1994 +++ linux/net/inet/tcp.h Wed Jan 18 19:11:49 1995 @@ -49,6 +49,7 @@ #define TCP_TIMEOUT_LEN (15*60*HZ) /* should be about 15 mins */ #define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to successfully * close the socket, about 60 seconds */ +#define TCP_FIN_TIMEOUT (3*60*HZ) /* BSD style FIN_WAIT2 deadlock breaker */ #define TCP_ACK_TIME (3*HZ) /* time to delay before sending an ACK */ #define TCP_DONE_TIME 250 /* maximum time to wait before actually * destroying a socket */ @@ -69,9 +70,15 @@ * TCP option */ -#define TCPOPT_NOP 1 -#define TCPOPT_EOL 0 -#define TCPOPT_MSS 2 +#define TCPOPT_NOP 1 /* Padding */ +#define TCPOPT_EOL 0 /* End of options */ +#define TCPOPT_MSS 2 /* Segment size negotiating */ +/* + * We don't use these yet, but they are for PAWS and big windows + */ +#define TCPOPT_WINDOW 3 /* Window scaling */ +#define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */ + /* * The next routines deal with comparing 32 bit unsigned ints