diff -u --recursive --new-file v2.3.6/linux/CREDITS linux/CREDITS --- v2.3.6/linux/CREDITS Tue Jun 8 10:27:18 1999 +++ linux/CREDITS Wed Jun 16 19:26:27 1999 @@ -624,6 +624,13 @@ S: Oak Park, Illinois 60302 S: USA +N: Daniel J. Frasnelli +E: dfrasnel@alphalinux.org +W: http://www.alphalinux.org/ +P: 1024/3EF87611 B9 F1 44 50 D3 E8 C2 80 DA E5 55 AA 56 7C 42 DA +D: DEC Alpha hacker +D: Miscellaneous bug squisher + N: Jim Freeman E: jfree@sovereign.org W: http://www.sovereign.org/ diff -u --recursive --new-file v2.3.6/linux/Documentation/ARM-README linux/Documentation/ARM-README --- v2.3.6/linux/Documentation/ARM-README Thu Apr 29 11:53:41 1999 +++ linux/Documentation/ARM-README Wed Dec 31 16:00:00 1969 @@ -1,181 +0,0 @@ - ARM Linux 2.1.99 - ================ - - Since this is a development kernel, it will not be as stable as the 2.0 - series, and can cause very nasty problems (eg, trashing your hard disk). - When running one of these kernels, I advise you to back up the complete - contents of all your hard disks. - - -Contributors ------------- - - Here is a list of people actively working on the project (If you - wish to be added to the list, please email me): - - Name: Russell King - Mail: linux@arm.uk.linux.org - Desc: Original developer of ARM Linux, project co-ordinator. - - Name: Dave Gilbert - Mail: linux@treblig.org - Desc: A3/4/5xx floppy and hard disk code maintainer. - - Name: Philip Blundell - Mail: Philip.Blundell@pobox.com - Desc: Architecture and processor selection during make config. - - -Todo list ---------- - - This is the list of changes to be done (roughly prioritised): - - * fully test new MEMC translation code - * fully test new AcornSCSI driver. - * reply to email ;) - - - Notes - ===== - -Compilation of kernel ---------------------- - - In order to compile ARM Linux, you will need a compiler capable of - generating ARM ELF code with GNU extensions. GCC-2.7.2.2 is good. - - To build ARM Linux natively, you shouldn't have to alter the ARCH = line in - the top level Makefile. However, if you don't have the ARM Linux ELF tools - installed as default, then you should change the CROSS_COMPILE line as - detailed below. - - If you wish to cross-compile, then alter the following lines in the top - level make file: - - ARCH = - with - ARCH = arm - - and - - CROSS_COMPILE= - to - CROSS_COMPILE= - eg. - CROSS_COMPILE=/usr/bin/arm-unknown-linuxelf- - - Do a 'make config', followed by 'make dep', and finally 'make all' to - build the kernel (vmlinux). A compressed image can be built by doing - a 'make zImage' instead of 'make all'. - - -Bug reports etc. ----------------- - - Please send patches, bug reports and code for the ARM Linux project - to linux@arm.uk.linux.org. Patches will not be included into future - kernels unless they come to me (or the relevant person concerned). - - When sending bug reports, please ensure that they contain all relevant - information, eg. the kernel messages that were printed before/during - the problem, what you were doing, etc. - - For patches, please include some explanation as to what the patch does - and why (if relevant). - - -Modules -------- - - Although modularisation is supported (and required for the FP emulator), - each module on an arm2/arm250/arm3 machine when is loaded will take - memory up to the next 32k boundary due to the size of the pages. Hence is - modularisation on these machines really worth it? - - However, arm6 and up machines allow modules to take multiples of 4k, and - as such Acorn RiscPCs and other architectures using these processors can - make good use of modularisation. - - -ADFS Image files ----------------- - - You can access image files on your ADFS partitions by mounting the ADFS - partition, and then using the loopback device driver. You must have - losetup installed. - - Please note that the PCEmulator DOS partitions have a partition table at - the start, and as such, you will have to give '-o offset' to losetup. - - -Kernel initialisation abort codes ---------------------------------- - - When the kernel is unable to boot, it will if possible display a colour - at the top of the screen. The colours have the following significance - when run in a 16 colour mode with the default palette: - - Stripes of white, red, yellow, and green: - Kernel does not support the processor architecture detected. - - -Request to developers ---------------------- - - When writing device drivers which include a separate assembler file, please - include it in with the C file, and not the arch/arm/lib directory. This - allows the driver to be compiled as a loadable module without requiring - half the code to be compiled into the kernel image. - - In general, try to avoid using assembler unless it is really necessary. It - makes drivers far less easy to port to other hardware. - - -ST506 hard drives ------------------ - - The ST506 hard drive controllers seem to be working fine (if a little - slowly). At the moment they will only work off the controllers on an - A4x0's motherboard, but for it to work off a Podule just requires - someone with a podule to add the addresses for the IRQ mask and the - HDC base to the source. - - As of 31/3/96 it works with two drives (you should get the ADFS - *configure hard drive set to 2). I've got an internal 20 MB and a great - big external 5.25" FH 64 MB drive (who could ever want more :-) ). - - I've just got 240 K/s off it (a dd with bs=128k); that's about half of what - RiscOS gets, but it's a heck of a lot better than the 50 K/s I was getting - last week :-) - - Known bug: Drive data errors can cause a hang; including cases where - the controller has fixed the error using ECC. (Possibly ONLY - in that case...hmm). - - -1772 Floppy ------------ - This also seems to work OK, but hasn't been stressed much lately. It - hasn't got any code for disc change detection in there at the moment which - could be a bit of a problem! Suggestions on the correct way to do this - are welcome. - - -Kernel entry (head-armv.S) --------------------------- - The initial entry into the kernel made via head-armv.S uses architecture - independent code. The architecture is selected by the value of 'r1' on - entry, which must be kept unique. You can register a new architecture - by mailing the following details to rmk@arm.uk.linux.org. Please give - the mail a subject of 'Register new architecture': - - Name: - ARCHDIR: - Description: - - - Please follow this format - it is an automated system. You should - receive a reply the next day. ---- -Russell King (03/05/1998) diff -u --recursive --new-file v2.3.6/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.3.6/linux/Documentation/Configure.help Wed Jun 9 16:59:15 1999 +++ linux/Documentation/Configure.help Thu Jun 17 01:11:35 1999 @@ -7723,13 +7723,6 @@ want), say M here and read Documentation/modules.txt. The module will be called smbfs.o. Most people say N, however. -SMB Win95 bug work-around -CONFIG_SMB_WIN95 - If you want to connect to a share exported by Windows 95, you should - say Y here. The Windows 95 server contains a bug that makes listing - directories unreliable. This option slows down the listing of - directories. This makes the Windows 95 server a bit more stable. - Coda filesystem support CONFIG_CODA_FS Coda is an advanced network filesystem, similar to NFS in that it @@ -11355,7 +11348,8 @@ CONFIG_HOST_FOOTBRIDGE The 21285 Footbridge chip can operate in either `host mode' or `add-in' mode. Say Y if your 21285 is in host mode, and therefore - is the configuration master, otherwise say N. + is the configuration master, otherwise say N. This must not be + set to 'Y' if the card is used in 'add-in' mode. MFM harddisk support CONFIG_BLK_DEV_MFM diff -u --recursive --new-file v2.3.6/linux/Documentation/pci.txt linux/Documentation/pci.txt --- v2.3.6/linux/Documentation/pci.txt Tue Apr 28 14:22:04 1998 +++ linux/Documentation/pci.txt Thu Jun 17 12:56:11 1999 @@ -4,7 +4,7 @@ "What should you avoid when writing PCI drivers" - by Martin Mares on 13-Feb-1998 + by Martin Mares on 17-Jun-1999 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -18,6 +18,10 @@ configure_device(dev); For class-based search, use pci_find_class(CLASS_ID, dev). + + You can use the constant PCI_ANY_ID as a wildcard replacement for +VENDOR_ID or DEVICE_ID. This allows searching for any device from a +specific vendor, for example. In case you want to do some complex matching, look at pci_devices -- it's a linked list of pci_dev structures for all PCI devices in the system. diff -u --recursive --new-file v2.3.6/linux/Makefile linux/Makefile --- v2.3.6/linux/Makefile Wed Jun 2 14:40:19 1999 +++ linux/Makefile Wed Jun 16 19:26:27 1999 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 3 -SUBLEVEL = 6 +SUBLEVEL = 7 EXTRAVERSION = ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) diff -u --recursive --new-file v2.3.6/linux/arch/arm/Makefile linux/arch/arm/Makefile --- v2.3.6/linux/arch/arm/Makefile Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/Makefile Thu Jun 17 01:11:35 1999 @@ -10,21 +10,31 @@ # License. See the file "COPYING" in the main directory of this archive # for more details. # -# Copyright (C) 1995, 1996 by Russell King +# Copyright (C) 1995-1999 by Russell King CFLAGS_PROC := ASFLAGS_PROC := -# All processors get `-mshort-load-bytes' for now, to work around alignment -# problems. This is more of a hack that just happens to work than a real fix -# but it will do for now. +# GCC 2.7 uses different options to later compilers; sort out which we have +CONFIG_GCC_NEW := $(shell if $(CC) --version 2>&1 | grep '^2\.7' > /dev/null; then echo n; else echo y; fi) + +# Hack to get around RiscPC with StrongARM optimistaion +# problem - force ARM710 optimisation for now. +ifeq ($(CONFIG_GCC_NEW),y) + ifeq ($(CONFIG_ARCH_RPC),y) + ifeq ($(CONFIG_CPU_SA110),y) + CONFIG_CPU_SA110 := n + CONFIG_CPU_ARM7 := y + endif + endif +endif ifeq ($(CONFIG_CPU_26),y) PROCESSOR = armo TEXTADDR = 0x02080000 ZTEXTADDR = 0x01800000 ZRELADDR = 0x02080000 - ifeq ($(CONFIG_BINUTILS_NEW),y) + ifeq ($(CONFIG_GCC_NEW),y) CFLAGS_PROC += -mapcs-26 -mshort-load-bytes ifeq ($(CONFIG_CPU_ARM2),y) CFLAGS_PROC += -mcpu=arm2 @@ -49,7 +59,7 @@ ifeq ($(CONFIG_CPU_32),y) PROCESSOR = armv TEXTADDR = 0xC0008000 - ifeq ($(CONFIG_BINUTILS_NEW),y) + ifeq ($(CONFIG_GCC_NEW),y) CFLAGS_PROC += -mapcs-32 -mshort-load-bytes ifeq ($(CONFIG_CPU_ARM6),y) CFLAGS_PROC += -mcpu=arm6 @@ -68,10 +78,11 @@ # Processor Architecture # CFLAGS_PROC - processor dependent CFLAGS -# PROCESSOR - processor type -# TEXTADDR - Uncompressed kernel link text address -# ZTEXTADDR - Compressed kernel link text address -# ZRELADDR - Compressed kernel relocating address (point at which uncompressed kernel is loaded). +# PROCESSOR - processor type +# TEXTADDR - Uncompressed kernel link text address +# ZTEXTADDR - Compressed kernel link text address +# ZRELADDR - Compressed kernel relocating address +# (point at which uncompressed kernel is loaded). # COMPRESSED_HEAD = head.o @@ -79,19 +90,16 @@ ifeq ($(CONFIG_ARCH_A5K),y) MACHINE = a5k ARCHDIR = arc -COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o endif ifeq ($(CONFIG_ARCH_ARC),y) MACHINE = arc ARCHDIR = arc -COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o endif ifeq ($(CONFIG_ARCH_RPC),y) MACHINE = rpc ARCHDIR = rpc -COMPRESSED_EXTRA = $(TOPDIR)/arch/arm/lib/ll_char_wr.o ZTEXTADDR = 0x10008000 ZRELADDR = 0x10008000 endif @@ -103,13 +111,17 @@ ZRELADDR = 0x00008000 endif -ifeq ($(CONFIG_ARCH_EBSA285),y) -MACHINE = ebsa285 +ifeq ($(CONFIG_FOOTBRIDGE),y) +MACHINE = footbridge ARCHDIR = ebsa285 ZTEXTADDR = 0x00008000 ZRELADDR = 0x00008000 endif +ifeq ($(CONFIG_ARCH_CO285),y) +TEXTADDR = 0x60008000 +endif + ifeq ($(CONFIG_ARCH_NEXUSPCI),y) MACHINE = nexuspci ARCHDIR = nexuspci @@ -119,31 +131,13 @@ COMPRESSED_HEAD = head-nexuspci.o endif -ifeq ($(CONFIG_ARCH_VNC),y) -TEXTADDR = 0xC000C000 -MACHINE = vnc -ARCHDIR = vnc -endif - -ifeq ($(CONFIG_ARCH_TBOX),y) -MACHINE = tbox -ARCHDIR = tbox -ZTEXTADDR = 0x80008000 -ZRELDIR = 0x80008000 -endif - PERL = perl -ifeq ($(CONFIG_BINUTILS_NEW),y) -LD = $(CROSS_COMPILE)ld -m elf32arm -else -LD = $(CROSS_COMPILE)ld -m elf_arm -endif +LD = $(CROSS_COMPILE)ld OBJCOPY = $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S OBJDUMP = $(CROSS_COMPILE)objdump CPP = $(CC) -E ARCHCC := $(word 1,$(CC)) GCCLIB := `$(CC) $(CFLAGS_PROC) --print-libgcc-file-name` -#GCCARCH := -B/usr/bin/arm-linuxelf- HOSTCFLAGS := $(CFLAGS:-fomit-frame-pointer=) ifeq ($(CONFIG_FRAME_POINTER),y) CFLAGS := $(CFLAGS:-fomit-frame-pointer=) @@ -153,75 +147,40 @@ LINKFLAGS = -T $(TOPDIR)/arch/arm/vmlinux-$(PROCESSOR).lds -e stext -Ttext $(TEXTADDR) ZLINKFLAGS = -Ttext $(ZTEXTADDR) -SUBDIRS := $(SUBDIRS:drivers=arch/arm/drivers) arch/arm/lib arch/arm/kernel arch/arm/mm -HEAD := arch/arm/kernel/head-$(PROCESSOR).o arch/arm/kernel/init_task.o +# If we're intending to debug the kernel, make sure it has line number +# information. This gets stripped out when building (z)Image so it doesn't +# add anything to the footprint of the running kernel. +ifeq ($(CONFIG_DEBUG_INFO),y) +CFLAGS += -g +endif + +HEAD := arch/arm/kernel/head-$(PROCESSOR).o \ + arch/arm/kernel/init_task.o +SUBDIRS := arch/arm/special $(SUBDIRS) arch/arm/lib arch/arm/kernel \ + arch/arm/mm arch/arm/nwfpe CORE_FILES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES) LIBS := arch/arm/lib/lib.a $(LIBS) $(GCCLIB) - -BLOCK_DRIVERS := drivers/block/block.a -CDROM_DRIVERS := drivers/cdrom/cdrom.a -ifeq ($(CONFIG_FB),y) -CHAR_DRIVERS := arch/arm/drivers/char1/char1.a drivers/char/char.a arch/arm/drivers/char1/char1.a -else -ifeq ($(CONFIG_VGA_CONSOLE),y) -CHAR_DRIVERS := arch/arm/drivers/char1/char1.a drivers/char/char.a arch/arm/drivers/char1/char1.a -else -CHAR_DRIVERS := arch/arm/drivers/char/char.a -endif -endif -MISC_DRIVERS := drivers/misc/misc.a -NET_DRIVERS := drivers/net/net.a -PARIDE_DRIVERS := drivers/block/paride/paride.a -PCI_DRIVERS := drivers/pci/pci.a -SCSI_DRIVERS := drivers/scsi/scsi.a -SOUND_DRIVERS := drivers/sound/sound.a -VIDEO_DRIVERS := drivers/video/video.a -PNP_DRIVERS := drivers/pnp/pnp.a +DRIVERS += arch/arm/special/special.a ifeq ($(CONFIG_ARCH_ACORN),y) -BLOCK_DRIVERS += drivers/acorn/block/acorn-block.a -CHAR_DRIVERS += drivers/acorn/char/acorn-char.a -NET_DRIVERS += drivers/acorn/net/acorn-net.a drivers/net/net.a -SCSI_DRIVERS += drivers/acorn/scsi/acorn-scsi.a +SUBDIRS += drivers/acorn/block drivers/acorn/char drivers/acorn/net \ + drivers/acorn/scsi +DRIVERS += drivers/acorn/block/acorn-block.a \ + drivers/acorn/char/acorn-char.a \ + drivers/acorn/net/acorn-net.a \ + drivers/acorn/scsi/acorn-scsi.a endif -DRIVERS := $(BLOCK_DRIVERS) $(CHAR_DRIVERS) $(MISC_DRIVERS) $(NET_DRIVERS) - -ifeq ($(CONFIG_FB),y) -DRIVERS := $(DRIVERS) $(VIDEO_DRIVERS) -else -ifeq ($(CONFIG_VGA_CONSOLE),y) -DRIVERS := $(DRIVERS) $(VIDEO_DRIVERS) -endif -endif -ifeq ($(CONFIG_SCSI),y) -DRIVERS := $(DRIVERS) $(SCSI_DRIVERS) -endif -ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR),) -DRIVERS := $(DRIVERS) $(CDROM_DRIVERS) -endif -ifdef CONFIG_PCI -DRIVERS := $(DRIVERS) $(PCI_DRIVERS) -endif -ifeq ($(CONFIG_SOUND),y) -DRIVERS := $(DRIVERS) $(SOUND_DRIVERS) -endif -ifeq ($(CONFIG_PARIDE),y) -DRIVERS := $(DRIVERS) $(PARIDE_DRIVERS) -endif -ifdef CONFIG_PNP -DRIVERS := $(DRIVERS) $(PNP_DRIVERS) +ifeq ($(CONFIG_NWFPE),y) +DRIVERS += arch/arm/nwfpe/math-emu.a endif +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot + symlinks:: $(RM) include/asm-arm/arch include/asm-arm/proc (cd include/asm-arm; ln -sf arch-$(ARCHDIR) arch; ln -sf proc-$(PROCESSOR) proc) -# Once we've finished integrating the sources, the @$(MAKE) will disappear -archmrproper: - rm -f include/asm-arm/arch include/asm-arm/proc - @$(MAKE) -C arch/$(ARCH)/drivers mrproper - arch/arm/kernel: dummy $(MAKE) linuxsubdirs SUBDIRS=arch/arm/kernel @@ -231,19 +190,20 @@ arch/arm/lib: dummy $(MAKE) linuxsubdirs SUBDIRS=arch/arm/lib -MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot - -zImage: vmlinux - @$(MAKEBOOT) zImage +zImage zinstall Image install: vmlinux + @$(MAKEBOOT) $@ -zinstall: vmlinux - @$(MAKEBOOT) zinstall +# Once we've finished integrating the sources, the @$(MAKE) will disappear +archmrproper: + rm -f include/asm-arm/arch include/asm-arm/proc + @$(MAKE) -C arch/$(ARCH)/special mrproper -Image: vmlinux - @$(MAKEBOOT) Image +archclean: + @$(MAKEBOOT) clean + $(RM) arch/arm/lib/constants.h -install: vmlinux - @$(MAKEBOOT) install +archdep: + @$(MAKEBOOT) dep # My testing targets (that short circuit a few dependencies) zImg:; @$(MAKEBOOT) zImage @@ -251,10 +211,19 @@ i:; @$(MAKEBOOT) install zi:; @$(MAKEBOOT) zinstall -archclean: - @$(MAKEBOOT) clean - $(RM) arch/arm/lib/constants.h +a5k_config: + $(RM) arch/arm/defconfig + cp arch/arm/def-configs/a5k arch/arm/defconfig + +ebsa110_config: + $(RM) arch/arm/defconfig + cp arch/arm/def-configs/ebsa110 arch/arm/defconfig + +footbridge_config: + $(RM) arch/arm/defconfig + cp arch/arm/def-configs/footbridge arch/arm/defconfig + +rpc_config: + $(RM) arch/arm/defconfig + cp arch/arm/def-configs/rpc arch/arm/defconfig -archdep: - @$(MAKEBOOT) dep -sed -e /^MACHINE..*=/s,= .*,= rpc,;/^PROCESSOR..*=/s,= .*,= armv, linux/arch/arm/Makefile.normal diff -u --recursive --new-file v2.3.6/linux/arch/arm/boot/compressed/Makefile linux/arch/arm/boot/compressed/Makefile --- v2.3.6/linux/arch/arm/boot/compressed/Makefile Sun Apr 12 11:42:15 1998 +++ linux/arch/arm/boot/compressed/Makefile Thu Jun 17 01:11:35 1999 @@ -11,10 +11,15 @@ OBJS =$(HEAD) misc.o $(COMPRESSED_EXTRA) CFLAGS =-O2 -DSTDC_HEADERS $(CFLAGS_PROC) ARFLAGS =rc +FONTC =$(TOPDIR)/drivers/video/font_acorn_8x8.c + +ifeq ($(CONFIG_ARCH_ACORN),y) +OBJS += ll_char_wr.o font.o +endif all: vmlinux -vmlinux: piggy.o $(OBJS) +vmlinux: $(OBJS) piggy.o $(LD) $(ZLINKFLAGS) -o vmlinux $(OBJS) piggy.o $(HEAD): $(HEAD:.o=.S) @@ -28,6 +33,9 @@ echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \ $(LD) -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-arm -T $$tmppiggy.lnk; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; + +font.o: $(FONTC) + $(CC) -Dstatic= -c -o $@ $(FONTC) clean:; rm -f vmlinux core diff -u --recursive --new-file v2.3.6/linux/arch/arm/boot/compressed/ll_char_wr.S linux/arch/arm/boot/compressed/ll_char_wr.S --- v2.3.6/linux/arch/arm/boot/compressed/ll_char_wr.S Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/boot/compressed/ll_char_wr.S Thu Jun 17 01:11:35 1999 @@ -0,0 +1,158 @@ +/* + * linux/arch/arm/lib/ll_char_wr.S + * + * Copyright (C) 1995, 1996 Russell King. + * + * Speedups & 1bpp code (C) 1996 Philip Blundell & Russell King. + * + * 10-04-96 RMK Various cleanups & reduced register usage. + * 08-04-98 RMK Shifts re-ordered + */ + +@ Regs: [] = corruptible +@ {} = used +@ () = do not use +#define __ASSEMBLY__ +#include +#include + .text + +#define BOLD 0x01 +#define ITALIC 0x02 +#define UNDERLINE 0x04 +#define FLASH 0x08 +#define INVERSE 0x10 + +LC0: .word SYMBOL_NAME(bytes_per_char_h) + .word SYMBOL_NAME(video_size_row) + .word SYMBOL_NAME(acorndata_8x8) + .word SYMBOL_NAME(con_charconvtable) + +ENTRY(ll_write_char) + stmfd sp!, {r4 - r7, lr} +@ +@ Smashable regs: {r0 - r3}, [r4 - r7], (r8 - fp), [ip], (sp), [lr], (pc) +@ + eor ip, r1, #UNDERLINE << 9 +/* + * calculate colours + */ + tst r1, #INVERSE << 9 + moveq r2, r1, lsr #16 + moveq r3, r1, lsr #24 + movne r2, r1, lsr #24 + movne r3, r1, lsr #16 + and r3, r3, #255 + and r2, r2, #255 +/* + * calculate offset into character table + */ + mov r1, r1, lsl #23 + mov r1, r1, lsr #20 +/* + * calculate offset required for each row [maybe I should make this an argument to this fn. + * Have to see what the register usage is like in the calling routines. + */ + adr r4, LC0 + ldmia r4, {r4, r5, r6, lr} + ldr r4, [r4] + ldr r5, [r5] +/* + * Go to resolution-dependent routine... + */ + cmp r4, #4 + blt Lrow1bpp + eor r2, r3, r2 @ Create eor mask to change colour from bg + orr r3, r3, r3, lsl #8 @ to fg. + orr r3, r3, r3, lsl #16 + add r0, r0, r5, lsl #3 @ Move to bottom of character + add r1, r1, #7 + ldrb r7, [r6, r1] + tst ip, #UNDERLINE << 9 + eoreq r7, r7, #255 + teq r4, #8 + beq Lrow8bpplp +@ +@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc) +@ + orr r3, r3, r3, lsl #4 +Lrow4bpplp: ldr r7, [lr, r7, lsl #2] + mul r7, r2, r7 + tst r1, #7 @ avoid using r7 directly after + eor ip, r3, r7 + str ip, [r0, -r5]! + LOADREGS(eqfd, sp!, {r4 - r7, pc}) + sub r1, r1, #1 + ldrb r7, [r6, r1] + ldr r7, [lr, r7, lsl #2] + mul r7, r2, r7 + tst r1, #7 @ avoid using r7 directly after + eor ip, r3, r7 + str ip, [r0, -r5]! + subne r1, r1, #1 + ldrneb r7, [r6, r1] + bne Lrow4bpplp + LOADREGS(fd, sp!, {r4 - r7, pc}) + +@ +@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc) +@ +Lrow8bpplp: mov ip, r7, lsr #4 + ldr ip, [lr, ip, lsl #2] + mul r4, r2, ip + and ip, r7, #15 @ avoid r4 + ldr ip, [lr, ip, lsl #2] @ avoid r4 + mul ip, r2, ip @ avoid r4 + eor r4, r3, r4 @ avoid ip + tst r1, #7 @ avoid ip + sub r0, r0, r5 @ avoid ip + eor ip, r3, ip + stmia r0, {r4, ip} + LOADREGS(eqfd, sp!, {r4 - r7, pc}) + sub r1, r1, #1 + ldrb r7, [r6, r1] + mov ip, r7, lsr #4 + ldr ip, [lr, ip, lsl #2] + mul r4, r2, ip + and ip, r7, #15 @ avoid r4 + ldr ip, [lr, ip, lsl #2] @ avoid r4 + mul ip, r2, ip @ avoid r4 + eor r4, r3, r4 @ avoid ip + tst r1, #7 @ avoid ip + sub r0, r0, r5 @ avoid ip + eor ip, r3, ip + stmia r0, {r4, ip} + subne r1, r1, #1 + ldrneb r7, [r6, r1] + bne Lrow8bpplp + LOADREGS(fd, sp!, {r4 - r7, pc}) + +@ +@ Smashable regs: {r0 - r3}, [r4], {r5, r6}, [r7], (r8 - fp), [ip], (sp), [lr], (pc) +@ +Lrow1bpp: add r6, r6, r1 + ldmia r6, {r4, r7} + tst ip, #INVERSE << 9 + mvnne r4, r4 + mvnne r7, r7 + strb r4, [r0], r5 + mov r4, r4, lsr #8 + strb r4, [r0], r5 + mov r4, r4, lsr #8 + strb r4, [r0], r5 + mov r4, r4, lsr #8 + strb r4, [r0], r5 + strb r7, [r0], r5 + mov r7, r7, lsr #8 + strb r7, [r0], r5 + mov r7, r7, lsr #8 + strb r7, [r0], r5 + mov r7, r7, lsr #8 + tst ip, #UNDERLINE << 9 + mvneq r7, r7 + strb r7, [r0], r5 + LOADREGS(fd, sp!, {r4 - r7, pc}) + + .bss +ENTRY(con_charconvtable) + .space 1024 diff -u --recursive --new-file v2.3.6/linux/arch/arm/config.in linux/arch/arm/config.in --- v2.3.6/linux/arch/arm/config.in Thu Jan 14 10:29:28 1999 +++ linux/arch/arm/config.in Thu Jun 17 01:11:35 1999 @@ -14,18 +14,31 @@ A5000 CONFIG_ARCH_A5K \ RiscPC CONFIG_ARCH_RPC \ EBSA-110 CONFIG_ARCH_EBSA110 \ - EBSA-285 CONFIG_ARCH_EBSA285 \ - NexusPCI CONFIG_ARCH_NEXUSPCI \ - Corel-VNC CONFIG_ARCH_VNC \ - Tbox CONFIG_ARCH_TBOX" RiscPC + FootBridge-based CONFIG_FOOTBRIDGE" RiscPC -if [ "$CONFIG_ARCH_EBSA285" = "y" ]; then - bool ' Include support for CATS boards' CONFIG_CATS +if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then + bool 'FootBridge in HOST mode' CONFIG_HOST_FOOTBRIDGE + if [ "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then + define_bool CONFIG_ADDIN_FOOTBRIDGE n + else + define_bool CONFIG_ADDIN_FOOTBRIDGE y + fi +fi + +if [ "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then + bool ' Include support for Intel EBSA285' CONFIG_ARCH_EBSA285 + bool ' Include support for Chalice CATS boards' CONFIG_CATS + bool ' Include support for Corel NetWinder' CONFIG_ARCH_NETWINDER +fi + +if [ "$CONFIG_ADDIN_FOOTBRIDGE" = "y" ]; then + # If we get any other footbridge-based plug-in boards, then + # add your architecture options here + define_bool CONFIG_ARCH_CO285 y fi # Select various configuration options depending on the machine type # Easy check for Acorn-style architectures - if [ "$CONFIG_ARCH_ARC" = "y" -o \ "$CONFIG_ARCH_A5K" = "y" -o \ "$CONFIG_ARCH_RPC" = "y" ]; then @@ -34,23 +47,19 @@ define_bool CONFIG_ARCH_ACORN n fi -if [ "$CONFIG_ARCH_TBOX" = "y" ]; then - define_bool CONFIG_BUS_I2C y -fi +#if [ "$CONFIG_ARCH_TBOX" = "y" ]; then +# define_bool CONFIG_BUS_I2C y +#fi # These machines always have PCI - if [ "$CONFIG_ARCH_NEXUSPCI" = "y" -o \ - "$CONFIG_ARCH_VNC" = "y" ]; then + "$CONFIG_HOST_FOOTBRIDGE" = "y" ]; then define_bool CONFIG_PCI y fi -if [ "$CONFIG_ARCH_EBSA285" = "y" ]; then - bool "PCI support" CONFIG_PCI -fi # These machines have ISA-DMA if [ "$CONFIG_CATS" = "y" -o \ - "$CONFIG_ARCH_VNC" = "y" ]; then + "$CONFIG_ARCH_NETWINDER" = "y" ]; then define_bool CONFIG_ISA_DMA y else define_bool CONFIG_ISA_DMA n @@ -59,7 +68,6 @@ # Figure out whether this system uses 26-bit or 32-bit CPUs. Nobody has # ever built a machine that can take both, and now that ARM3 is obsolete # nobody is likely to either. - if [ "$CONFIG_ARCH_ARC" = "y" -o \ "$CONFIG_ARCH_A5K" = "y" ]; then define_bool CONFIG_CPU_32 n @@ -71,7 +79,6 @@ # Now allow the user to choose a more precise CPU. This is only used to set # the flags we pass to GCC, not in any code. - choice 'Optimise for CPU' \ "ARM2 CONFIG_CPU_ARM2 \ ARM3 CONFIG_CPU_ARM3 \ @@ -80,22 +87,21 @@ SA110 CONFIG_CPU_SA110" ARM6 if [ "$CONFIG_CPU_26" = "y" ]; then - # For 26-bit CPUs, the page size changes with the amount of physical RAM! # The default is 4MB but if the user has less they have to own up to it here. - choice 'Physical memory size' \ "4MB+ CONFIG_PAGESIZE_32 \ - 2MB CONFIG_PAGESIZE_16 \ - 1MB/512K CONFIG_PAGESIZE_8" 4MB+ + 2MB CONFIG_PAGESIZE_16" 4MB+ fi endmenu mainmenu_option next_comment comment 'Code maturity level options' bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL -bool 'Use new compilation options (for GCC 2.8)' CONFIG_BINUTILS_NEW -bool 'Compile kernel with frame pointer (for useful debugging)' CONFIG_FRAME_POINTER +if [ "$CONFIG_CPU_32" = "y" -a "$CONFIG_ARCH_EBSA110" != "y" ]; then + bool 'Enable kernel-mode alignment trap handler (EXPERIMENTAL)' CONFIG_ALIGNMENT_TRAP +fi +bool 'Split text into discardable sections' CONFIG_TEXT_SECTIONS endmenu mainmenu_option next_comment @@ -113,13 +119,19 @@ bool 'System V IPC' CONFIG_SYSVIPC bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT bool 'Sysctl support' CONFIG_SYSCTL +tristate 'Math emulation' CONFIG_NWFPE tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC +if [ "$CONFIG_CPU_32" = "y" ]; then + tristate 'RISC OS personality' CONFIG_ARTHUR +fi tristate 'Parallel port support' CONFIG_PARPORT if [ "$CONFIG_PARPORT" != "n" ]; then - dep_tristate ' Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT + if [ "$CONFIG_ARCH_ARC" = "y" ]; then + dep_tristate ' Archimedes hardware' CONFIG_PARPORT_ARC $CONFIG_PARPORT + fi dep_tristate ' PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT # If exactly one hardware type is selected then parport will optimise away # support for loading any others. Defeat this if the user is keen. @@ -129,13 +141,29 @@ fi fi fi -if [ "$CONFIG_ARCH_EBSA285" = "y" -o \ - "$CONFIG_ARCH_EBSA110" = "y" -o \ - "$CONFIG_ARCH_VNC" = "y" ]; then +if [ "$CONFIG_ARCH_EBSA110" = "y" -o \ + "$CONFIG_ARCH_NETWINDER" = "y" -o \ + "$CONFIG_CATS" = "y" ]; then string 'Initial kernel command string' CONFIG_CMDLINE fi +if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \ + "$CONFIG_ARCH_EBSA110" = "y" -o \ + "$CONFIG_ARCH_EBSA285" = "y" -o \ + "$CONFIG_ARCH_CO285" = "y" ]; then + bool 'Timer and CPU usage LEDs' CONFIG_LEDS + if [ "$CONFIG_LEDS" = "y" ]; then + if [ "$CONFIG_ARCH_NETWINDER" = "y" -o \ + "$CONFIG_ARCH_EBSA285" = "y" -o \ + "$CONFIG_ARCH_CO285" = "y" ]; then + bool ' Timer LED' CONFIG_LEDS_TIMER + bool ' CPU usage LED' CONFIG_LEDS_CPU + fi + fi +fi endmenu +source drivers/i2o/Config.in + source drivers/pnp/Config.in source drivers/block/Config.in @@ -144,15 +172,19 @@ source drivers/acorn/block/Config.in fi -if [ "$CONFIG_VGA_CONSOLE" = "n" -a "$CONFIG_FB" = "n" ]; then - source arch/arm/drivers/char/Config.in -else - source drivers/char/Config.in -fi +source drivers/char/Config.in if [ "$CONFIG_ARCH_ACORN" = "y" ]; then - source drivers/acorn/char/Config.in + if [ "$CONFIG_MOUSE" = "y" ]; then + if [ "$CONFIG_ARCH_RPC" != "y" ]; then + define_bool CONFIG_KBDMOUSE y + else + define_bool CONFIG_RPCMOUSE y + fi + fi fi +source drivers/usb/Config.in + if [ "$CONFIG_VT" = "y" ]; then mainmenu_option next_comment comment 'Console drivers' @@ -166,9 +198,11 @@ if [ "$CONFIG_NET" = "y" ]; then source net/Config.in -fi -if [ "$CONFIG_NET" = "y" ]; then + source net/ax25/Config.in + + source net/irda/Config.in + mainmenu_option next_comment comment 'Network device support' @@ -179,6 +213,15 @@ endmenu fi +# mainmenu_option next_comment +# comment 'ISDN subsystem' +# +# tristate 'ISDN support' CONFIG_ISDN +# if [ "$CONFIG_ISDN" != "n" ]; then +# source drivers/isdn/Config.in +# fi +# endmenu + mainmenu_option next_comment comment 'SCSI support' @@ -200,21 +243,29 @@ endmenu fi -# mainmenu_option next_comment -# comment 'ISDN subsystem' -# -# tristate 'ISDN support' CONFIG_ISDN -# if [ "$CONFIG_ISDN" != "n" ]; then -# source drivers/isdn/Config.in -# fi -# endmenu - source fs/Config.in mainmenu_option next_comment comment 'Kernel hacking' -bool 'Debug kernel errors' CONFIG_DEBUG_ERRORS +bool 'Compile kernel with frame pointer (for useful debugging)' CONFIG_FRAME_POINTER +bool 'Verbose kernel error messages' CONFIG_DEBUG_ERRORS +bool 'Verbose user fault messages' CONFIG_DEBUG_USER +bool 'Include debugging information in kernel binary' CONFIG_DEBUG_INFO #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_CPU_26" = "y" ]; then + bool 'Disable pgtable cache (EXPERIMENTAL)' CONFIG_NO_PGT_CACHE + fi + + # These options are only for real kernel hackers + # who want to get their hands dirty. + bool 'Kernel low-level debugging functions' CONFIG_DEBUG_LL + if [ "$CONFIG_DEBUG_LL" = "y" ]; then + if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then + bool 'Kernel low-level debugging messages via DC21285 port' CONFIG_DEBUG_DC21285_PORT + fi + fi +fi endmenu diff -u --recursive --new-file v2.3.6/linux/arch/arm/defconfig linux/arch/arm/defconfig --- v2.3.6/linux/arch/arm/defconfig Thu Feb 25 10:46:46 1999 +++ linux/arch/arm/defconfig Thu Jun 17 01:11:35 1999 @@ -4,47 +4,71 @@ CONFIG_ARM=y # +# System and processor type +# +# CONFIG_ARCH_ARC is not set +# CONFIG_ARCH_A5K is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_EBSA110 is not set +CONFIG_FOOTBRIDGE=y +CONFIG_HOST_FOOTBRIDGE=y +# CONFIG_ADDIN_FOOTBRIDGE is not set +CONFIG_ARCH_EBSA285=y +# CONFIG_CATS is not set +CONFIG_ARCH_NETWINDER=y +# CONFIG_ARCH_ACORN is not set +CONFIG_PCI=y +CONFIG_ISA_DMA=y +CONFIG_CPU_32=y +# CONFIG_CPU_26 is not set +# CONFIG_CPU_ARM2 is not set +# CONFIG_CPU_ARM3 is not set +# CONFIG_CPU_ARM6 is not set +# CONFIG_CPU_ARM7 is not set +CONFIG_CPU_SA110=y + +# # Code maturity level options # CONFIG_EXPERIMENTAL=y +# CONFIG_ALIGNMENT_TRAP is not set +# CONFIG_TEXT_SECTIONS is not set # # Loadable module support # CONFIG_MODULES=y -CONFIG_MODVERSIONS=y +# CONFIG_MODVERSIONS is not set CONFIG_KMOD=y # # General setup # -# CONFIG_ARCH_ARC is not set -# CONFIG_ARCH_A5K is not set -CONFIG_ARCH_RPC=y -# CONFIG_ARCH_EBSA110 is not set -# CONFIG_ARCH_NEXUSPCI is not set -CONFIG_ARCH_ACORN=y -# CONFIG_PCI is not set -# CONFIG_CPU_ARM2 is not set -# CONFIG_CPU_ARM3 is not set -# CONFIG_CPU_ARM6 is not set -CONFIG_CPU_SA110=y -CONFIG_FRAME_POINTER=y -# CONFIG_BINUTILS_NEW is not set -CONFIG_DEBUG_ERRORS=y CONFIG_NET=y CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y +CONFIG_NWFPE=y CONFIG_BINFMT_AOUT=y -CONFIG_BINFMT_ELF=m -# CONFIG_BINFMT_JAVA is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set CONFIG_PARPORT=y CONFIG_PARPORT_PC=y +CONFIG_CMDLINE="root=/dev/hda2 ro mem=32M parport=0x378,7 ide0=autotune" +CONFIG_LEDS=y +CONFIG_LEDS_TIMER=y +# CONFIG_LEDS_CPU is not set + +# +# Plug and Play support +# +# CONFIG_PNP is not set # -# Floppy, IDE, and other block devices +# Block devices # -CONFIG_BLK_DEV_FD=y +# CONFIG_BLK_DEV_FD is not set CONFIG_BLK_DEV_IDE=y # @@ -52,32 +76,165 @@ # # CONFIG_BLK_DEV_HD_IDE is not set CONFIG_BLK_DEV_IDEDISK=y -CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDECD is not set # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set # CONFIG_BLK_DEV_IDESCSI is not set -# CONFIG_BLK_DEV_IDE_PCMCIA is not set -CONFIG_BLK_DEV_IDE_CARDS=y -CONFIG_BLK_DEV_IDE_ICSIDE=y -# CONFIG_BLK_DEV_IDE_RAPIDE is not set -# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +CONFIG_BLK_DEV_IDEPCI=y +CONFIG_BLK_DEV_IDEDMA=y +CONFIG_BLK_DEV_OFFBOARD=y +CONFIG_IDEDMA_AUTO=y +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_VIA82C586 is not set +# CONFIG_BLK_DEV_CMD646 is not set +CONFIG_BLK_DEV_SL82C105=y +# CONFIG_IDE_CHIPSETS is not set # # Additional Block Devices # CONFIG_BLK_DEV_LOOP=m -# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=m +CONFIG_MD_STRIPED=m +CONFIG_MD_MIRRORING=m +CONFIG_MD_RAID5=m CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_BLK_DEV_XD is not set CONFIG_PARIDE_PARPORT=y -# CONFIG_PARIDE is not set -CONFIG_BLK_DEV_PART=y +CONFIG_PARIDE=m + +# +# Parallel IDE high-level drivers +# +CONFIG_PARIDE_PD=m +CONFIG_PARIDE_PCD=m +CONFIG_PARIDE_PF=m +CONFIG_PARIDE_PT=m +CONFIG_PARIDE_PG=m + +# +# Parallel IDE protocol modules +# +CONFIG_PARIDE_ATEN=m +CONFIG_PARIDE_BPCK=m +CONFIG_PARIDE_COMM=m +CONFIG_PARIDE_DSTR=m +CONFIG_PARIDE_FIT2=m +CONFIG_PARIDE_FIT3=m +CONFIG_PARIDE_EPAT=m +CONFIG_PARIDE_EPIA=m +CONFIG_PARIDE_FRIQ=m +CONFIG_PARIDE_FRPW=m +CONFIG_PARIDE_KBIC=m +CONFIG_PARIDE_KTTI=m +CONFIG_PARIDE_ON20=m +CONFIG_PARIDE_ON26=m # CONFIG_BLK_DEV_HD is not set # +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_UNIX98_PTYS is not set +CONFIG_PRINTER=m +CONFIG_PRINTER_READBACK=y +CONFIG_MOUSE=y + +# +# Mice +# +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_BUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set +# CONFIG_QIC02_TAPE is not set +CONFIG_WATCHDOG=y + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG_NOWAYOUT is not set +# CONFIG_WDT is not set +CONFIG_SOFT_WATCHDOG=y +# CONFIG_PCWATCHDOG is not set +# CONFIG_ACQUIRE_WDT is not set +CONFIG_DS1620=y +CONFIG_NWBUTTON=y +CONFIG_NWBUTTON_REBOOT=y +CONFIG_NWFLASH=m +# CONFIG_NVRAM is not set +CONFIG_RTC=y + +# +# Video For Linux +# +# CONFIG_VIDEO_DEV is not set + +# +# Joystick support +# +# CONFIG_JOYSTICK is not set +# CONFIG_DTLK is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set + +# +# Console drivers +# +CONFIG_VGA_CONSOLE=y +CONFIG_FB=y +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FB_PM2 is not set +CONFIG_FB_CYBER2000=y +# CONFIG_FB_MATROX is not set +# CONFIG_FB_ATY is not set +# CONFIG_FB_VIRTUAL is not set +CONFIG_FBCON_ADVANCED=y +# CONFIG_FBCON_MFB is not set +# CONFIG_FBCON_CFB2 is not set +# CONFIG_FBCON_CFB4 is not set +CONFIG_FBCON_CFB8=y +CONFIG_FBCON_CFB16=y +CONFIG_FBCON_CFB24=y +# CONFIG_FBCON_CFB32 is not set +# CONFIG_FBCON_AFB is not set +# CONFIG_FBCON_ILBM is not set +# CONFIG_FBCON_IPLAN2P2 is not set +# CONFIG_FBCON_IPLAN2P4 is not set +# CONFIG_FBCON_IPLAN2P8 is not set +# CONFIG_FBCON_MAC is not set +CONFIG_FBCON_VGA=y +# CONFIG_FBCON_FONTWIDTH8_ONLY is not set +CONFIG_FBCON_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_PEARL_8x8 is not set +CONFIG_FONT_ACORN_8x8=y + +# # Networking options # -# CONFIG_PACKET is not set +CONFIG_PACKET=y # CONFIG_NETLINK is not set # CONFIG_FIREWALL is not set # CONFIG_FILTER is not set @@ -85,21 +242,20 @@ CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set -# CONFIG_IP_PNP is not set -# CONFIG_IP_ACCT is not set -# CONFIG_IP_MASQUERADE is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set # CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set -# CONFIG_IP_ALIAS is not set +CONFIG_IP_ALIAS=y # CONFIG_SYN_COOKIES is not set # # (it is safe to leave these untouched) # # CONFIG_INET_RARP is not set -CONFIG_IP_NOSR=y -# CONFIG_SKB_LARGE is not set +CONFIG_SKB_LARGE=y # CONFIG_IPV6 is not set # @@ -111,107 +267,198 @@ # CONFIG_LAPB is not set # CONFIG_BRIDGE is not set # CONFIG_LLC is not set +# CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set # CONFIG_NET_FASTROUTE is not set # CONFIG_NET_HW_FLOWCONTROL is not set # CONFIG_CPU_IS_SLOW is not set + +# +# QoS and/or fair queueing +# # CONFIG_NET_SCHED is not set -# CONFIG_NET_PROFILE is not set # -# SCSI support +# Amateur Radio support # -CONFIG_SCSI=y +# CONFIG_HAMRADIO is not set # -# SCSI support type (disk, tape, CD-ROM) +# IrDA subsystem support # -CONFIG_BLK_DEV_SD=y -# CONFIG_CHR_DEV_ST is not set -CONFIG_BLK_DEV_SR=y -# CONFIG_BLK_DEV_SR_VENDOR is not set -# CONFIG_CHR_DEV_SG is not set +# CONFIG_IRDA is not set # -# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# Network device support # -# CONFIG_SCSI_MULTI_LUN is not set -CONFIG_SCSI_CONSTANTS=y -CONFIG_SCSI_LOGGING=y +CONFIG_NETDEVICES=y +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_NET_ETHERNET=y +# CONFIG_ARM_AM79C961A is not set +CONFIG_NET_VENDOR_3COM=y +# CONFIG_EL1 is not set +# CONFIG_EL2 is not set +# CONFIG_ELPLUS is not set +# CONFIG_EL16 is not set +# CONFIG_EL3 is not set +# CONFIG_3C515 is not set +CONFIG_VORTEX=y +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_RTL8139 is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_ACENIC is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_EISA=y +# CONFIG_PCNET32 is not set +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +# CONFIG_DE4X5 is not set +CONFIG_DEC_ELCP=m +# CONFIG_DGRS is not set +# CONFIG_EEXPRESS_PRO100 is not set +# CONFIG_LNE390 is not set +# CONFIG_NE3210 is not set +CONFIG_NE2K_PCI=y +# CONFIG_TLAN is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_ES3210 is not set +# CONFIG_EPIC100 is not set +# CONFIG_ZNET is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_DLCI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=m # -# SCSI low-level drivers +# CCP compressors for PPP are only built as modules. # -CONFIG_SCSI_ACORNSCSI_3=m -CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE=y -CONFIG_SCSI_ACORNSCSI_SYNC=y -CONFIG_SCSI_CUMANA_2=m -CONFIG_SCSI_POWERTECSCSI=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +CONFIG_SLIP_MODE_SLIP6=y +# CONFIG_NET_RADIO is not set +# CONFIG_TR is not set +# CONFIG_SHAPER is not set +# CONFIG_HOSTESS_SV11 is not set +# CONFIG_COSA is not set +# CONFIG_RCPCI is not set # -# The following drives are not fully supported +# SCSI support # -CONFIG_SCSI_CUMANA_1=m -CONFIG_SCSI_OAK1=m -CONFIG_SCSI_PPA=m -CONFIG_SCSI_PPA_HAVE_PEDANTIC=2 +# CONFIG_SCSI is not set # -# Network device support +# Sound # -CONFIG_NETDEVICES=y -# CONFIG_DUMMY is not set -# CONFIG_EQUALIZER is not set -CONFIG_PPP=m +CONFIG_SOUND=m +# CONFIG_SOUND_ES1370 is not set +# CONFIG_SOUND_ES1371 is not set +# CONFIG_SOUND_SONICVIBES is not set +# CONFIG_SOUND_MSNDCLAS is not set +# CONFIG_SOUND_MSNDPIN is not set +CONFIG_SOUND_OSS=m +# CONFIG_SOUND_PAS is not set +CONFIG_SOUND_SB=m +CONFIG_SOUND_ADLIB=m +# CONFIG_SOUND_GUS is not set +# CONFIG_SOUND_MPU401 is not set +# CONFIG_SOUND_PSS is not set +# CONFIG_SOUND_MSS is not set +# CONFIG_SOUND_SSCAPE is not set +# CONFIG_SOUND_TRIX is not set +# CONFIG_SOUND_MAD16 is not set +# CONFIG_SOUND_WAVEFRONT is not set +# CONFIG_SOUND_CS4232 is not set +# CONFIG_SOUND_OPL3SA2 is not set +# CONFIG_SOUND_MAUI is not set +# CONFIG_SOUND_SGALAXY is not set +# CONFIG_SOUND_AD1816 is not set +# CONFIG_SOUND_OPL3SA1 is not set +# CONFIG_SOUND_SOFTOSS is not set +# CONFIG_SOUND_YM3812 is not set +# CONFIG_SOUND_VMIDI is not set +# CONFIG_SOUND_UART6850 is not set +# CONFIG_SOUND_VIDC is not set +CONFIG_SOUND_WAVEARTIST=m +CONFIG_WAVEARTIST_BASE=250 +CONFIG_WAVEARTIST_IRQ=12 +CONFIG_WAVEARTIST_DMA=3 +CONFIG_WAVEARTIST_DMA2=7 # -# CCP compressors for PPP are only built as modules. +# Additional low level sound drivers # -# CONFIG_SLIP is not set -CONFIG_ETHER1=m -CONFIG_ETHER3=m -CONFIG_ETHERH=m +# CONFIG_LOWLEVEL_SOUND is not set # # Filesystems # # CONFIG_QUOTA is not set -# CONFIG_MINIX_FS is not set -CONFIG_EXT2_FS=y -CONFIG_ISO9660_FS=y -CONFIG_JOLIET=y -CONFIG_FAT_FS=y -CONFIG_MSDOS_FS=y +# CONFIG_AUTOFS_FS is not set +CONFIG_ADFS_FS=y +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m # CONFIG_UMSDOS_FS is not set -CONFIG_VFAT_FS=y +CONFIG_VFAT_FS=m +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set CONFIG_NFS_FS=y -CONFIG_NFSD=y +CONFIG_ROOT_NFS=y +CONFIG_NFSD=m +# CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y -# CONFIG_CODA_FS is not set # CONFIG_SMB_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_NTFS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_HFS_FS is not set -# CONFIG_ROMFS_FS is not set -# CONFIG_AUTOFS_FS is not set -# CONFIG_UFS_FS is not set -CONFIG_ADFS_FS=y -CONFIG_ADFS_FS=y +# CONFIG_NCP_FS is not set + +# +# Partition Types +# +# CONFIG_OSF_PARTITION is not set # CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SGI_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +CONFIG_ACORN_PARTITION=y +CONFIG_ACORN_PARTITION_ADFS=y +# CONFIG_ACORN_PARTITION_ICS is not set +# CONFIG_ACORN_PARTITION_POWERTEC is not set +# CONFIG_ACORN_PARTITION_RISCIX is not set CONFIG_NLS=y # # Native Language Support # -# CONFIG_NLS_CODEPAGE_437 is not set +CONFIG_NLS_CODEPAGE_437=m # CONFIG_NLS_CODEPAGE_737 is not set # CONFIG_NLS_CODEPAGE_775 is not set -# CONFIG_NLS_CODEPAGE_850 is not set -# CONFIG_NLS_CODEPAGE_852 is not set +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m # CONFIG_NLS_CODEPAGE_855 is not set # CONFIG_NLS_CODEPAGE_857 is not set # CONFIG_NLS_CODEPAGE_860 is not set @@ -223,8 +470,8 @@ # CONFIG_NLS_CODEPAGE_866 is not set # CONFIG_NLS_CODEPAGE_869 is not set # CONFIG_NLS_CODEPAGE_874 is not set -# CONFIG_NLS_ISO8859_1 is not set -# CONFIG_NLS_ISO8859_2 is not set +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_2=m # CONFIG_NLS_ISO8859_3 is not set # CONFIG_NLS_ISO8859_4 is not set # CONFIG_NLS_ISO8859_5 is not set @@ -232,34 +479,15 @@ # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set +CONFIG_NLS_ISO8859_15=m # CONFIG_NLS_KOI8_R is not set # -# Character devices -# -CONFIG_VT=y -CONFIG_VT_CONSOLE=y -CONFIG_SERIAL=y -# CONFIG_SERIAL_CONSOLE is not set -# CONFIG_SERIAL_EXTENDED is not set -CONFIG_ATOMWIDE_SERIAL=y -CONFIG_DUALSP_SERIAL=y -CONFIG_MOUSE=y -CONFIG_PRINTER=m -CONFIG_PRINTER_READBACK=y -# CONFIG_UMISC is not set -# CONFIG_WATCHDOG is not set -CONFIG_RPCMOUSE=y - -# -# Sound -# -CONFIG_SOUND=m -CONFIG_VIDC=y -CONFIG_AUDIO=y -DSP_BUFFSIZE=65536 - -# # Kernel hacking # +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_INFO is not set CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_LL is not set diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/Makefile linux/arch/arm/kernel/Makefile --- v2.3.6/linux/arch/arm/kernel/Makefile Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/Makefile Thu Jun 17 01:11:35 1999 @@ -9,31 +9,37 @@ ENTRY_OBJ = entry-$(PROCESSOR).o O_TARGET := kernel.o -O_OBJS := $(ENTRY_OBJ) ioport.o irq.o process.o ptrace.o setup.o \ +O_OBJS := $(ENTRY_OBJ) irq.o process.o ptrace.o setup.o \ signal.o sys_arm.o time.o traps.o -DMA_OBJS_arc = dma-arc.o -DMA_OBJS_a5k = dma-a5k.o -DMA_OBJS_rpc = dma-rpc.o -DMA_OBJS_ebsa110 = dma-dummy.o -DMA_OBJS_ebsa285 = dma-ebsa285.o -DMA_OBJS_nexuspci = -DMA_OBJS_vnc = dma-vnc.o - -O_OBJS_arc = ecard.o iic.o fiq.o oldlatches.o -O_OBJS_a5k = ecard.o iic.o fiq.o -O_OBJS_rpc = ecard.o iic.o fiq.o -O_OBJS_ebsa110 = leds-ebsa110.o -O_OBJS_ebsa285 = leds-ebsa285.o hw-ebsa285.o -O_OBJS_nexuspci = -O_OBJS_vnc = leds-ebsa285.o hw-vnc.o +ifeq ($(CONFIG_ISA_DMA),y) + ISA_DMA_OBJS += dma-isa.o +endif + +O_OBJS_arc = dma-arc.o iic.o fiq.o oldlatches.o +O_OBJS_a5k = dma-a5k.o iic.o fiq.o +O_OBJS_rpc = dma-rpc.o iic.o fiq.o +O_OBJS_ebsa110 = dma-dummy.o +O_OBJS_footbridge = dma-footbridge.o $(ISA_DMA_OBJS) +O_OBJS_nexuspci = dma-dummy.o + +OX_OBJS_arc = dma.o +OX_OBJS_a5k = dma.o +OX_OBJS_rpc = dma.o +OX_OBJS_ebsa110 = +OX_OBJS_footbridge= dma.o hw-footbridge.o +OX_OBJS_nexuspci = all: lib kernel.o $(HEAD_OBJ) init_task.o +O_OBJS += $(O_OBJS_$(MACHINE)) + ifeq ($(CONFIG_MODULES),y) OX_OBJS = armksyms.o -else - O_OBJS += armksyms.o +endif + +ifeq ($(CONFIG_ARCH_ACORN),y) + OX_OBJS += ecard.o endif ifeq ($(MACHINE),nexuspci) @@ -46,17 +52,23 @@ endif endif -ifneq ($(DMA_OBJS_$(MACHINE)),) - OX_OBJS += dma.o - O_OBJS += $(DMA_OBJS_$(MACHINE)) - ifeq ($(CONFIG_ISA_DMA),y) - O_OBJS += dma-isa.o - endif +ifdef CONFIG_LEDS + OX_OBJS += leds-$(MACHINE).o +endif + +ifeq ($(CONFIG_MODULES),y) + OX_OBJS += $(OX_OBJS_$(MACHINE)) else - O_OBJS += dma-dummy.o + O_OBJS += $(OX_OBJS_$(MACHINE)) endif -O_OBJS += $(O_OBJS_$(MACHINE)) +ifeq ($(CONFIG_ARTHUR),y) + O_OBJS += arthur.o +else + ifeq ($(CONFIG_ARTHUR),m) + M_OBJS += arthur.o + endif +endif $(HEAD_OBJ): $(HEAD_OBJ:.o=.S) $(CC) -D__ASSEMBLY__ -DTEXTADDR=$(TEXTADDR) -traditional -c $(HEAD_OBJ:.o=.S) -o $@ @@ -72,3 +84,7 @@ lib: $(MAKE) -C ../lib constants.h + +# Spell out some dependencies that `make dep' doesn't spot +entry-armv.o: calls.S +entry-armo.o: calls.S diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/armksyms.c linux/arch/arm/kernel/armksyms.c --- v2.3.6/linux/arch/arm/kernel/armksyms.c Sun Sep 6 10:44:47 1998 +++ linux/arch/arm/kernel/armksyms.c Thu Jun 17 01:11:35 1999 @@ -6,20 +6,40 @@ #include #include #include +#include -#include #include #include #include #include +#include #include #include +#include extern void dump_thread(struct pt_regs *, struct user *); extern int dump_fpu(struct pt_regs *, struct user_fp_struct *); extern void inswb(unsigned int port, void *to, int len); extern void outswb(unsigned int port, const void *to, int len); +extern unsigned int local_bh_count[NR_CPUS]; +extern unsigned int local_irq_count[NR_CPUS]; + +extern void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags); +extern void iounmap(void *addr); + +extern pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); + +/* + * syscalls + */ +extern int sys_write(int, const char *, int); +extern int sys_read(int, char *, int); +extern int sys_lseek(int, off_t, int); +extern int sys_open(const char *, int, int); +extern int sys_exit(int); +extern int sys_wait4(int, int *, int, struct rusage *); + /* * libgcc functions - functions that are used internally by the * compiler... (prototypes are not correct though, but that @@ -43,6 +63,8 @@ extern void __umoddi3(void); extern void __umodsi3(void); +extern void ret_from_exception(void); +extern void fpundefinstr(void); extern void fp_enter(void); #define EXPORT_SYMBOL_ALIAS(sym,orig) \ const char __kstrtab_##sym##[] __attribute__((section(".kstrtab"))) = \ @@ -57,32 +79,46 @@ EXPORT_SYMBOL_ALIAS(fp_printk,printk); EXPORT_SYMBOL_ALIAS(fp_send_sig,send_sig); +#ifdef CONFIG_CPU_26 +EXPORT_SYMBOL(fpundefinstr); +EXPORT_SYMBOL(ret_from_exception); +#endif + /* platform dependent support */ EXPORT_SYMBOL(dump_thread); EXPORT_SYMBOL(dump_fpu); EXPORT_SYMBOL(udelay); EXPORT_SYMBOL(xchg_str); - - /* expansion card support */ -#ifdef CONFIG_ARCH_ACORN -EXPORT_SYMBOL(ecard_startfind); -EXPORT_SYMBOL(ecard_find); -EXPORT_SYMBOL(ecard_readchunk); -EXPORT_SYMBOL(ecard_address); +EXPORT_SYMBOL(local_bh_count); +EXPORT_SYMBOL(local_irq_count); +#ifdef CONFIG_CPU_32 +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); #endif +EXPORT_SYMBOL(kernel_thread); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); /* processor dependencies */ EXPORT_SYMBOL(processor); -EXPORT_SYMBOL(machine_type); +EXPORT_SYMBOL(__machine_arch_type); + + /* networking */ +EXPORT_SYMBOL(csum_partial_copy); +EXPORT_SYMBOL(__csum_ipv6_magic); /* io */ -EXPORT_SYMBOL(outswb); +EXPORT_SYMBOL(outsb); EXPORT_SYMBOL(outsw); -EXPORT_SYMBOL(inswb); +EXPORT_SYMBOL(outsl); +EXPORT_SYMBOL(insb); EXPORT_SYMBOL(insw); +EXPORT_SYMBOL(insl); + +EXPORT_SYMBOL(_memcpy_fromio); +EXPORT_SYMBOL(_memcpy_toio); +EXPORT_SYMBOL(_memset_io); /* address translation */ #ifndef __virt_to_phys__is_a_macro @@ -98,7 +134,9 @@ EXPORT_SYMBOL(__bus_to_virt); #endif +#ifndef CONFIG_NO_PGT_CACHE EXPORT_SYMBOL(quicklists); +#endif EXPORT_SYMBOL(__bad_pmd); EXPORT_SYMBOL(__bad_pmd_kernel); @@ -167,3 +205,17 @@ EXPORT_SYMBOL(armidlist); EXPORT_SYMBOL(armidindex); EXPORT_SYMBOL(elf_platform); + + /* syscalls */ +EXPORT_SYMBOL(sys_write); +EXPORT_SYMBOL(sys_read); +EXPORT_SYMBOL(sys_lseek); +EXPORT_SYMBOL(sys_open); +EXPORT_SYMBOL(sys_exit); +EXPORT_SYMBOL(sys_wait4); + + /* semaphores */ +EXPORT_SYMBOL_NOVERS(__down_failed); +EXPORT_SYMBOL_NOVERS(__down_interruptible_failed); +EXPORT_SYMBOL_NOVERS(__up_wakeup); + diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/arthur.c linux/arch/arm/kernel/arthur.c --- v2.3.6/linux/arch/arm/kernel/arthur.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/kernel/arthur.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,88 @@ +/* + * Arthur personality + * Copyright (C) 1998 Philip Blundell + */ + +#include +#include +#include +#include +#include + +#include + +/* RISC OS doesn't have many signals, and a lot of those that it does + have don't map easily to any Linux equivalent. Never mind. */ + +#define RISCOS_SIGABRT 1 +#define RISCOS_SIGFPE 2 +#define RISCOS_SIGILL 3 +#define RISCOS_SIGINT 4 +#define RISCOS_SIGSEGV 5 +#define RISCOS_SIGTERM 6 +#define RISCOS_SIGSTAK 7 +#define RISCOS_SIGUSR1 8 +#define RISCOS_SIGUSR2 9 +#define RISCOS_SIGOSERROR 10 + +static unsigned long riscos_to_linux_signals[32] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31 +}; + +static unsigned long linux_to_riscos_signals[32] = { + 0, -1, RISCOS_SIGINT, -1, + RISCOS_SIGILL, 5, RISCOS_SIGABRT, 7, + RISCOS_SIGFPE, 9, RISCOS_SIGUSR1, RISCOS_SIGSEGV, + RISCOS_SIGUSR2, 13, 14, RISCOS_SIGTERM, + 16, 17, 18, 19, + 20, 21, 22, 23, + 24, 25, 26, 27, + 28, 29, 30, 31 +}; + +static void arthur_lcall7(int nr, struct pt_regs *regs) +{ + struct siginfo info; + info.si_signo = SIGSWI; + info.si_code = nr; + /* Bounce it to the emulator */ + send_sig_info(SIGSWI, &info, current); +} + +static struct exec_domain riscos_exec_domain = { + "Arthur", /* name */ + (lcall7_func)arthur_lcall7, + PER_RISCOS, PER_RISCOS, + riscos_to_linux_signals, + linux_to_riscos_signals, +#ifdef MODULE + &__this_module, /* No usage counter. */ +#else + NULL, +#endif + NULL /* Nothing after this in the list. */ +}; + +/* + * We could do with some locking to stop Arthur being removed while + * processes are using it. + */ + +#ifdef MODULE +int init_module(void) +#else +int initialise_arthur(void) +#endif +{ + return register_exec_domain(&riscos_exec_domain); +} + +#ifdef MODULE +void cleanup_module(void) +{ + unregister_exec_domain(&riscos_exec_domain); +} +#endif diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/calls.S linux/arch/arm/kernel/calls.S --- v2.3.6/linux/arch/arm/kernel/calls.S Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/calls.S Thu Jun 17 01:11:35 1999 @@ -31,7 +31,7 @@ .long SYMBOL_NAME(sys_lseek) /* 20 */ .long SYMBOL_NAME(sys_getpid) .long SYMBOL_NAME(sys_mount_wrapper) - .long SYMBOL_NAME(sys_umount) + .long SYMBOL_NAME(sys_oldumount) .long SYMBOL_NAME(sys_setuid) .long SYMBOL_NAME(sys_getuid) /* 25 */ .long SYMBOL_NAME(sys_stime) @@ -61,7 +61,7 @@ .long SYMBOL_NAME(sys_geteuid) /* 50 */ .long SYMBOL_NAME(sys_getegid) .long SYMBOL_NAME(sys_acct) - .long SYMBOL_NAME(sys_ni_syscall) /* was sys_phys */ + .long SYMBOL_NAME(sys_umount) .long SYMBOL_NAME(sys_ni_syscall) /* was sys_lock */ .long SYMBOL_NAME(sys_ioctl) /* 55 */ .long SYMBOL_NAME(sys_fcntl) @@ -110,7 +110,7 @@ .long SYMBOL_NAME(sys_ni_syscall) /* was sys_profil */ .long SYMBOL_NAME(sys_statfs) /* 100 */ .long SYMBOL_NAME(sys_fstatfs) - .long SYMBOL_NAME(sys_ni_syscall) /* .long _sys_ioperm */ + .long SYMBOL_NAME(sys_ni_syscall) .long SYMBOL_NAME(sys_socketcall) .long SYMBOL_NAME(sys_syslog) .long SYMBOL_NAME(sys_setitimer) @@ -119,7 +119,7 @@ .long SYMBOL_NAME(sys_newlstat) .long SYMBOL_NAME(sys_newfstat) .long SYMBOL_NAME(sys_uname) -/* 110 */ .long SYMBOL_NAME(sys_iopl) +/* 110 */ .long SYMBOL_NAME(sys_ni_syscall) .long SYMBOL_NAME(sys_vhangup) .long SYMBOL_NAME(sys_idle) .long SYMBOL_NAME(sys_syscall) /* call a syscall */ @@ -196,6 +196,10 @@ .long SYMBOL_NAME(sys_capget) /* 185 */ .long SYMBOL_NAME(sys_capset) .long SYMBOL_NAME(sys_sigaltstack_wrapper) + .long SYMBOL_NAME(sys_sendfile) + .long SYMBOL_NAME(sys_ni_syscall) + .long SYMBOL_NAME(sys_ni_syscall) +/* 190 */ .long SYMBOL_NAME(sys_vfork_wrapper) .rept NR_syscalls-186 .long SYMBOL_NAME(sys_ni_syscall) diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dec21285.c linux/arch/arm/kernel/dec21285.c --- v2.3.6/linux/arch/arm/kernel/dec21285.c Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/dec21285.c Thu Jun 17 01:11:35 1999 @@ -8,17 +8,17 @@ #include #include #include +#include #include #include #include +#include -#define MAX_SLOTS 20 +#define MAX_SLOTS 21 extern void pcibios_fixup_ebsa285(struct pci_dev *dev); extern void pcibios_init_ebsa285(void); -extern void pcibios_fixup_vnc(struct pci_dev *dev); -extern void pcibios_init_vnc(void); int pcibios_present(void) @@ -33,11 +33,12 @@ int slot = PCI_SLOT(dev_fn); if (slot < MAX_SLOTS) - return 0xf8c00000 + (slot << 11) + (PCI_FUNC(dev_fn) << 8); + return PCICFG0_BASE + 0xc00000 + + (slot << 11) + (PCI_FUNC(dev_fn) << 8); else return 0; } else - return 0xf9000000 | (bus << 16) | (dev_fn << 8); + return PCICFG1_BASE | (bus << 16) | (dev_fn << 8); } int @@ -151,10 +152,7 @@ struct pci_dev *dev; for (dev = pci_devices; dev; dev = dev->next) { - if (machine_is_ebsa285() || machine_is_cats()) - pcibios_fixup_ebsa285(dev); - if (machine_is_netwinder()) - pcibios_fixup_vnc(dev); + pcibios_fixup_ebsa285(dev); pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_LINE, dev->irq); @@ -164,18 +162,83 @@ dev->bus->number, dev->devfn, dev->vendor, dev->device, dev->irq); } - if (machine_is_netwinder()) - hw_init(); + + hw_init(); } __initfunc(void pcibios_init(void)) { - if (machine_is_ebsa285() || machine_is_cats()) - pcibios_init_ebsa285(); - if (machine_is_netwinder()) - pcibios_init_vnc(); + unsigned int mem_size = (unsigned int)high_memory - PAGE_OFFSET; + unsigned long cntl; + + *CSR_SDRAMBASEMASK = (mem_size - 1) & 0x0ffc0000; + *CSR_SDRAMBASEOFFSET = 0; + *CSR_ROMBASEMASK = 0x80000000; + *CSR_CSRBASEMASK = 0; + *CSR_CSRBASEOFFSET = 0; + *CSR_PCIADDR_EXTN = 0; + +#ifdef CONFIG_HOST_FOOTBRIDGE + /* + * Against my better judgement, Philip Blundell still seems + * to be saying that we should initialise the PCI stuff here + * when the PCI_CFN bit is not set, dispite my comment below, + * which he decided to remove. If it is not set, then + * the card is in add-in mode, and we're in a machine where + * the bus is set up by 'others'. + * + * We should therefore not mess about with the mapping in + * anyway, and we should not be using the virt_to_bus functions + * that exist in the HOST architecture mode (since they assume + * a fixed mapping). + * + * Instead, you should be using ADDIN mode, which allows for + * this situation. This does assume that you have correctly + * initialised the PCI bus, which you must have done to get + * your PC booted. + * + * Unfortunately, he seems to be blind to this. I guess he'll + * also remove all this. + * + * And THIS COMMENT STAYS, even if this gets patched, thank + * you. + */ + + /* + * Map our SDRAM at a known address in PCI space, just in case + * the firmware had other ideas. Using a nonzero base is + * necessary, since some VGA cards forcefully use PCI addresses + * in the range 0x000a0000 to 0x000c0000. (eg, S3 cards). + * + * NOTE! If you need to chec the PCI_CFN bit in the SA110 + * control register then you've configured the kernel wrong. + * If you're not using host mode, then DO NOT set + * CONFIG_HOST_FOOTBRIDGE, but use CONFIG_ADDIN_FOOTBRIDGE + * instead. In this case, you MUST supply some firmware + * to allow your PC to boot, plus we should not modify the + * mappings that the PC BIOS has set up for us. + */ + *CSR_PCICACHELINESIZE = 0x00002008; + *CSR_PCICSRBASE = 0; + *CSR_PCICSRIOBASE = 0; + *CSR_PCISDRAMBASE = virt_to_bus((void *)PAGE_OFFSET); + *CSR_PCIROMBASE = 0; + *CSR_PCICMD = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | PCI_COMMAND_FAST_BACK | + PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | + (1 << 31) | (1 << 29) | (1 << 28) | (1 << 24); +#endif + + /* + * Clear any existing errors - we aren't + * interested in historical data... + */ + cntl = *CSR_SA110_CNTL & 0xffffde07; + *CSR_SA110_CNTL = cntl | SA110_CNTL_RXSERR; + + pcibios_init_ebsa285(); - printk("DEC21285 PCI revision %02X\n", *(unsigned char *)0xfe000008); + printk(KERN_DEBUG"PCI: DEC21285 revision %02lX\n", *CSR_CLASSREV & 0xff); } __initfunc(void pcibios_fixup_bus(struct pci_bus *bus)) diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-a5k.c linux/arch/arm/kernel/dma-a5k.c --- v2.3.6/linux/arch/arm/kernel/dma-a5k.c Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/dma-a5k.c Thu Jun 17 01:11:35 1999 @@ -12,7 +12,6 @@ #include #include #include -#include #include "dma.h" @@ -37,8 +36,9 @@ if (channel != DMA_VIRTUAL_FLOPPY) printk("arch_dma_count: invalid channel %d\n", channel); else { - extern int floppy_fiqresidual(void); - return floppy_fiqresidual(); + struct pt_regs regs; + get_fiq_regs(®s); + return regs.ARM_r9; } return 0; } @@ -48,6 +48,7 @@ if (channel != DMA_VIRTUAL_FLOPPY) printk("arch_enable_dma: invalid channel %d\n", channel); else { + struct pt_regs regs; void *fiqhandler_start; unsigned int fiqhandler_length; extern void floppy_fiqsetup(unsigned long len, unsigned long addr, @@ -67,8 +68,10 @@ return; } memcpy((void *)0x1c, fiqhandler_start, fiqhandler_length); - flush_page_to_ram(0); - floppy_fiqsetup(dma->buf.length, __bus_to_virt(dma->buf.address), (int)PCIO_FLOPPYDMABASE); + regs.ARM_r9 = dma->buf.length; + regs.ARM_r10 = __bus_to_virt(dma->buf.address); + regs.ARM_fp = (int)PCIO_FLOPPYDMABASE; + set_fiq_regs(®s); enable_irq(dma->dma_irq); } } @@ -81,6 +84,11 @@ disable_irq(dma->dma_irq); release_fiq(&fh); } +} + +int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns) +{ + return 0; } __initfunc(void arch_dma_init(dma_t *dma)) diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-arc.c linux/arch/arm/kernel/dma-arc.c --- v2.3.6/linux/arch/arm/kernel/dma-arc.c Sun Apr 12 11:42:15 1998 +++ linux/arch/arm/kernel/dma-arc.c Thu Jun 17 01:11:35 1999 @@ -1,10 +1,11 @@ /* * arch/arm/kernel/dma-arc.c * - * Copyright (C) 1998 Dave Gilbert / Russell King + * Copyright (C) 1998-1999 Dave Gilbert / Russell King * * DMA functions specific to Archimedes architecture */ +#include #include #include @@ -14,7 +15,7 @@ #include "dma.h" -int arch_request_dma(dmach_t channel, dma_t *dma) +int arch_request_dma(dmach_t channel, dma_t *dma, const char * dev_id) { if (channel == DMA_VIRTUAL_FLOPPY0 || channel == DMA_VIRTUAL_FLOPPY1) @@ -25,16 +26,12 @@ void arch_free_dma(dmach_t channel, dma_t *dma) { - if (channel != DMA_VIRTUAL_FLOPPY0 && - channel != DMA_VIRTUAL_FLOPPY1) - return 0; - else - return -EINVAL; } void arch_enable_dma(dmach_t channel, dma_t *dma) { switch (channel) { +#ifdef CONFIG_BLK_DEV_FD case DMA_VIRTUAL_FLOPPY0: { /* Data DMA */ switch (dma->dma_mode) { case DMA_MODE_READ: /* read */ @@ -96,9 +93,38 @@ restore_flags(flags); } break; +#endif } } +int arch_get_dma_residue(dmach_t channel, dma_t *dma) +{ + switch (channel) { +#ifdef CONFIG_BLK_DEV_FD + case DMA_VIRTUAL_FLOPPY0: { /* Data DMA */ + extern unsigned int fdc1772_bytestogo; + + /* 10/1/1999 DAG - I presume its the number of bytes left? */ + return fdc1772_bytestogo; + }; + break; + + case DMA_VIRTUAL_FLOPPY1: { /* Command completed */ + /* 10/1/1999 DAG - Presume whether there is an outstanding command? */ + extern unsigned int fdc1772_fdc_int_done; + + return (fdc1772_fdc_int_done==0)?1:0; /* Explicit! If the int done is 0 then 1 int to go */ + }; + break; + +#endif + + default: + printk("dma-arc.c:arch_get_dma_residue called with unknown/unconfigured DMA channel\n"); + return 0; + }; +} + void arch_disable_dma(dmach_t channel, dma_t *dma) { if (channel != DMA_VIRTUAL_FLOPPY0 && @@ -106,6 +132,11 @@ printk("arch_disable_dma: invalid channel %d\n", channel); else disable_irq(dma->dma_irq); +} + +int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns) +{ + return 0; } __initfunc(void arch_dma_init(dma_t *dma)) diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-dummy.c linux/arch/arm/kernel/dma-dummy.c --- v2.3.6/linux/arch/arm/kernel/dma-dummy.c Fri May 8 00:42:38 1998 +++ linux/arch/arm/kernel/dma-dummy.c Thu Jun 17 01:11:35 1999 @@ -9,6 +9,10 @@ #include #include +#include + +spinlock_t dma_spin_lock = SPIN_LOCK_UNLOCKED; + int request_dma(int channel, const char *device_id) { return -EINVAL; diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-ebsa285.c linux/arch/arm/kernel/dma-ebsa285.c --- v2.3.6/linux/arch/arm/kernel/dma-ebsa285.c Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/dma-ebsa285.c Wed Dec 31 16:00:00 1969 @@ -1,101 +0,0 @@ -/* - * arch/arm/kernel/dma-ebsa285.c - * - * Copyright (C) 1998 Phil Blundell - * - * DMA functions specific to EBSA-285/CATS architectures - * - * Changelog: - * 09/11/1998 RMK Split out ISA DMA functions to dma-isa.c - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "dma.h" -#include "dma-isa.h" - -int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name) -{ - switch (channel) { - case 0: - case 1: /* 21285 internal channels */ - return 0; - - case 2 ... 9: - if (machine_is_cats()) - return isa_request_dma(channel - 2, dma, dev_name); - } - - return -EINVAL; -} - -void arch_free_dma(dmach_t channel, dma_t *dma) -{ - /* nothing to do */ -} - -int arch_get_dma_residue(dmach_t channel, dma_t *dma) -{ - int residue = 0; - - switch (channel) { - case 0: - case 1: - break; -#ifdef CONFIG_CATS - case 2 ... 9: - if (machine_is_cats()) - residue = isa_get_dma_residue(channel - 2); -#endif - } - return residue; -} - -void arch_enable_dma(dmach_t channel, dma_t *dma) -{ - switch (channel) { - case 0: - case 1: - /* - * Not yet implemented - */ - break; -#ifdef CONFIG_CATS - case 2 ... 9: - if (machine_is_cats()) - isa_enable_dma(channel - 2, dma); -#endif - } -} - -void arch_disable_dma(dmach_t channel, dma_t *dma) -{ - switch (channel) { - case 0: - case 1: - /* - * Not yet implemented - */ - break; -#ifdef CONFIG_CATS - case 2 ... 9: - if (machine_is_cats()) - isa_disable_dma(channel - 2, dma); -#endif - } -} - -__initfunc(void arch_dma_init(dma_t *dma)) -{ - /* Nothing to do */ -} diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-footbridge.c linux/arch/arm/kernel/dma-footbridge.c --- v2.3.6/linux/arch/arm/kernel/dma-footbridge.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/kernel/dma-footbridge.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,112 @@ +/* + * arch/arm/kernel/dma-ebsa285.c + * + * Copyright (C) 1998 Phil Blundell + * + * DMA functions specific to EBSA-285/CATS architectures + * + * Changelog: + * 09-Nov-1998 RMK Split out ISA DMA functions to dma-isa.c + * 17-Mar-1999 RMK Allow any EBSA285-like architecture to have + * ISA DMA controllers. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dma.h" +#include "dma-isa.h" + +#ifdef CONFIG_ISA_DMA +static int has_isa_dma; +#else +#define has_isa_dma 0 +#endif + +int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name) +{ + switch (channel) { + case _DC21285_DMA(0): + case _DC21285_DMA(1): /* 21285 internal channels */ + return 0; + + case _ISA_DMA(0) ... _ISA_DMA(7): + if (has_isa_dma) + return isa_request_dma(channel - _ISA_DMA(0), dma, dev_name); + } + + return -EINVAL; +} + +void arch_free_dma(dmach_t channel, dma_t *dma) +{ + /* nothing to do */ +} + +int arch_get_dma_residue(dmach_t channel, dma_t *dma) +{ + int residue = 0; + + switch (channel) { + case _DC21285_DMA(0): + case _DC21285_DMA(1): + break; + + case _ISA_DMA(0) ... _ISA_DMA(7): + if (has_isa_dma) + residue = isa_get_dma_residue(channel - _ISA_DMA(0), dma); + } + return residue; +} + +void arch_enable_dma(dmach_t channel, dma_t *dma) +{ + switch (channel) { + case _DC21285_DMA(0): + case _DC21285_DMA(1): + /* + * Not yet implemented + */ + break; + + case _ISA_DMA(0) ... _ISA_DMA(7): + if (has_isa_dma) + isa_enable_dma(channel - _ISA_DMA(0), dma); + } +} + +void arch_disable_dma(dmach_t channel, dma_t *dma) +{ + switch (channel) { + case _DC21285_DMA(0): + case _DC21285_DMA(1): + /* + * Not yet implemented + */ + break; + + case _ISA_DMA(0) ... _ISA_DMA(7): + if (has_isa_dma) + isa_disable_dma(channel - _ISA_DMA(0), dma); + } +} + +int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle_ns) +{ + return 0; +} + +__initfunc(void arch_dma_init(dma_t *dma)) +{ +#ifdef CONFIG_ISA_DMA + has_isa_dma = isa_init_dma(); +#endif +} diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-isa.c linux/arch/arm/kernel/dma-isa.c --- v2.3.6/linux/arch/arm/kernel/dma-isa.c Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/dma-isa.c Thu Jun 17 01:11:35 1999 @@ -11,6 +11,7 @@ * Copyright (C) 1998 Phil Blundell */ #include +#include #include #include @@ -18,6 +19,11 @@ #include "dma.h" #include "dma-isa.h" +#define ISA_DMA_MODE_READ 0x44 +#define ISA_DMA_MODE_WRITE 0x48 +#define ISA_DMA_MODE_CASCADE 0xc0 +#define ISA_DMA_AUTOINIT 0x10 + #define ISA_DMA_MASK 0 #define ISA_DMA_MODE 1 #define ISA_DMA_CLRFF 2 @@ -40,10 +46,7 @@ int isa_request_dma(int channel, dma_t *dma, const char *dev_name) { - if (channel != 4) - return 0; - - return -EINVAL; + return 0; } void isa_free_dma(int channel, dma_t *dma) @@ -56,25 +59,27 @@ unsigned int io_port = isa_dma_port[channel][ISA_DMA_COUNT]; int count; - count = 1 + inb(io_port) + (inb(io_port) << 8); + count = 1 + inb(io_port); + count |= inb(io_port) << 8; return channel < 4 ? count : (count << 1); } void isa_enable_dma(int channel, dma_t *dma) { - unsigned long address, length; - if (dma->invalid) { + unsigned long address, length; + unsigned int mode; + address = dma->buf.address; length = dma->buf.length - 1; - outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]); outb(address >> 16, isa_dma_port[channel][ISA_DMA_PGLO]); + outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]); if (channel >= 4) { address >>= 1; - length = (length >> 1) & 0xfe; /* why &0xfe? */ + length >>= 1; } outb(0, isa_dma_port[channel][ISA_DMA_CLRFF]); @@ -85,17 +90,31 @@ outb(length, isa_dma_port[channel][ISA_DMA_COUNT]); outb(length >> 8, isa_dma_port[channel][ISA_DMA_COUNT]); - outb(dma->dma_mode | (channel & 3), isa_dma_port[channel][ISA_DMA_MODE]); + mode = channel & 3; - switch (dma->dma_mode) { + switch (dma->dma_mode & DMA_MODE_MASK) { case DMA_MODE_READ: + mode |= ISA_DMA_MODE_READ; dma_cache_inv(__bus_to_virt(dma->buf.address), dma->buf.length); break; case DMA_MODE_WRITE: + mode |= ISA_DMA_MODE_WRITE; dma_cache_wback(__bus_to_virt(dma->buf.address), dma->buf.length); break; + + case DMA_MODE_CASCADE: + mode |= ISA_DMA_MODE_CASCADE; + break; + + default: + break; } + + if (dma->dma_mode & DMA_AUTOINIT) + mode |= ISA_DMA_AUTOINIT; + + outb(mode, isa_dma_port[channel][ISA_DMA_MODE]); dma->invalid = 0; } outb(channel & 3, isa_dma_port[channel][ISA_DMA_MASK]); @@ -104,4 +123,57 @@ void isa_disable_dma(int channel, dma_t *dma) { outb(channel | 4, isa_dma_port[channel][ISA_DMA_MASK]); +} + +__initfunc(int isa_init_dma(void)) +{ + int dmac_found; + + outb(0xff, 0x0d); + outb(0xff, 0xda); + + outb(0x55, 0x00); + outb(0xaa, 0x00); + + dmac_found = inb(0x00) == 0x55 && inb(0x00) == 0xaa; + + if (dmac_found) { + int channel; + + for (channel = 0; channel < 8; channel++) + isa_disable_dma(channel, NULL); + + outb(0x40, 0x0b); + outb(0x41, 0x0b); + outb(0x42, 0x0b); + outb(0x43, 0x0b); + + outb(0xc0, 0xd6); + outb(0x41, 0xd6); + outb(0x42, 0xd6); + outb(0x43, 0xd6); + + outb(0, 0xd4); + + outb(0x10, 0x08); + outb(0x10, 0xd0); + + /* + * Is this correct? According to + * my documentation, it doesn't + * appear to be. It should be + * outb(0x3f, 0x40b); outb(0x3f, 0x4d6); + */ + outb(0x30, 0x40b); + outb(0x31, 0x40b); + outb(0x32, 0x40b); + outb(0x33, 0x40b); + outb(0x31, 0x4d6); + outb(0x32, 0x4d6); + outb(0x33, 0x4d6); + + request_dma(DMA_ISA_CASCADE, "cascade"); + } + + return dmac_found; } diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-isa.h linux/arch/arm/kernel/dma-isa.h --- v2.3.6/linux/arch/arm/kernel/dma-isa.h Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/dma-isa.h Thu Jun 17 01:11:35 1999 @@ -23,3 +23,7 @@ */ void isa_disable_dma(int channel, dma_t *dma); +/* + * Initialise DMA + */ +int isa_init_dma(void); diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-rpc.c linux/arch/arm/kernel/dma-rpc.c --- v2.3.6/linux/arch/arm/kernel/dma-rpc.c Sun Sep 6 10:44:47 1998 +++ linux/arch/arm/kernel/dma-rpc.c Thu Jun 17 01:11:35 1999 @@ -11,10 +11,10 @@ #include #include -#include #include #include #include +#include #include #include @@ -223,8 +223,9 @@ break; case DMA_VIRTUAL_FLOPPY: { - extern int floppy_fiqresidual(void); - residue = floppy_fiqresidual(); + struct pt_regs regs; + get_fiq_regs(®s); + return regs.ARM_r9; } break; } @@ -286,7 +287,6 @@ set_fiq_handler(fiqhandler_start, fiqhandler_length); set_fiq_regs(®s); enable_irq(dma->dma_irq); - } break; @@ -319,6 +319,46 @@ } } +int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle) +{ + int tcr, speed; + + if (cycle < 188) + speed = 3; + else if (cycle <= 250) + speed = 2; + else if (cycle < 438) + speed = 1; + else + speed = 0; + + tcr = inb(IOMD_DMATCR); + speed &= 3; + + switch (channel) { + case DMA_0: + tcr = (tcr & ~0x03) | speed; + break; + + case DMA_1: + tcr = (tcr & ~0x0c) | (speed << 2); + break; + + case DMA_2: + tcr = (tcr & ~0x30) | (speed << 4); + break; + + case DMA_3: + tcr = (tcr & ~0xc0) | (speed << 6); + break; + + default: + break; + } + + outb(tcr, IOMD_DMATCR); +} + __initfunc(void arch_dma_init(dma_t *dma)) { outb(0, IOMD_IO0CR); @@ -326,7 +366,7 @@ outb(0, IOMD_IO2CR); outb(0, IOMD_IO3CR); -// outb(0xf0, IOMD_DMATCR); + outb(0xa0, IOMD_DMATCR); dma[0].dma_base = ioaddr(IOMD_IO0CURA); dma[0].dma_irq = IRQ_DMA0; diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma-vnc.c linux/arch/arm/kernel/dma-vnc.c --- v2.3.6/linux/arch/arm/kernel/dma-vnc.c Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/dma-vnc.c Wed Dec 31 16:00:00 1969 @@ -1,51 +0,0 @@ -/* - * arch/arm/kernel/dma-vnc.c - * - * Copyright (C) 1998 Russell King - */ -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "dma.h" -#include "dma-isa.h" - -int arch_request_dma(dmach_t channel, dma_t *dma, const char *dev_name) -{ - if (channel < 8) - return isa_request_dma(channel, dma, dev_name); - return -EINVAL; -} - -void arch_free_dma(dmach_t channel, dma_t *dma) -{ - isa_free_dma(channel, dma); -} - -int arch_get_dma_residue(dmach_t channel, dma_t *dma) -{ - return isa_get_dma_residue(channel, dma); -} - -void arch_enable_dma(dmach_t channel, dma_t *dma) -{ - isa_enable_dma(channel, dma); -} - -void arch_disable_dma(dmach_t channel, dma_t *dma) -{ - isa_disable_dma(channel, dma); -} - -__initfunc(void arch_dma_init(dma_t *dma)) -{ - /* Nothing to do */ -} - diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma.c linux/arch/arm/kernel/dma.c --- v2.3.6/linux/arch/arm/kernel/dma.c Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/dma.c Thu Jun 17 01:11:35 1999 @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -201,6 +200,12 @@ printk (KERN_ERR "Trying to disable free DMA%d\n", channel); } +void set_dma_speed(dmach_t channel, int cycle_ns) +{ + dma_chan[channel].speed = + arch_set_dma_speed(channel, &dma_chan[channel], cycle_ns); +} + int get_dma_residue(dmach_t channel) { return arch_get_dma_residue(channel, &dma_chan[channel]); @@ -214,6 +219,7 @@ EXPORT_SYMBOL(set_dma_mode); EXPORT_SYMBOL(get_dma_residue); EXPORT_SYMBOL(set_dma_sg); +EXPORT_SYMBOL(set_dma_speed); __initfunc(void init_dma(void)) { diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/dma.h linux/arch/arm/kernel/dma.h --- v2.3.6/linux/arch/arm/kernel/dma.h Sun Apr 12 11:42:15 1998 +++ linux/arch/arm/kernel/dma.h Thu Jun 17 01:11:35 1999 @@ -15,6 +15,7 @@ unsigned int active:1; /* Transfer active */ unsigned int invalid:1; /* Address/Count changed */ dmamode_t dma_mode; /* DMA mode */ + int speed; /* DMA speed */ unsigned int lock; /* Device is allocated */ const char *device_id; /* Device name */ @@ -62,6 +63,15 @@ * Returns : Number of bytes left to DMA */ int arch_get_dma_residue(dmach_t channel, dma_t *dma); + +/* Prototype: int arch_set_dma_speed(channel, dma, cycle) + * Purpose : Convert a cycle time to a register setting + * Params : channel - DMA channel number + * : dma - DMA structure for channel + * : cycle - cycle time in NS + * Returns : setting for 'dma->speed' + */ +int arch_set_dma_speed(dmach_t channel, dma_t *dma, int cycle); /* Prototype: void arch_dma_init(dma) * Purpose : Initialise architecture specific DMA diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/ecard.c linux/arch/arm/kernel/ecard.c --- v2.3.6/linux/arch/arm/kernel/ecard.c Mon Dec 28 11:04:21 1998 +++ linux/arch/arm/kernel/ecard.c Thu Jun 17 01:11:35 1999 @@ -7,32 +7,43 @@ * * Created from information from Acorns RiscOS3 PRMs * - * 08-Dec-1996 RMK Added code for the 9'th expansion card - the ether podule slot. + * 08-Dec-1996 RMK Added code for the 9'th expansion card - the ether + * podule slot. * 06-May-1997 RMK Added blacklist for cards whose loader doesn't work. - * 12-Sep-1997 RMK Created new handling of interrupt enables/disables - cards can - * now register their own routine to control interrupts (recommended). - * 29-Sep-1997 RMK Expansion card interrupt hardware not being re-enabled on reset from - * Linux. (Caused cards not to respond under RiscOS without hard reset). + * 12-Sep-1997 RMK Created new handling of interrupt enables/disables + * - cards can now register their own routine to control + * interrupts (recommended). + * 29-Sep-1997 RMK Expansion card interrupt hardware not being re-enabled + * on reset from Linux. (Caused cards not to respond + * under RiscOS without hard reset). * 15-Feb-1998 RMK Added DMA support * 12-Sep-1998 RMK Added EASI support + * 10-Jan-1999 RMK Run loaders in a simulated RISC OS environment. + * 17-Apr-1999 RMK Support for EASI Type C cycles. */ #define ECARD_C +#define __KERNEL_SYSCALLS__ #include +#include #include #include #include #include #include #include +#include +#include +#include #include -#include -#include +#include #include +#include +#include #include -#include +#include #ifdef CONFIG_ARCH_ARC #include @@ -40,45 +51,420 @@ #define oldlatch_init() #endif -#define BLACKLIST_NAME(m,p,s) { m, p, NULL, s } -#define BLACKLIST_LOADER(m,p,l) { m, p, l, NULL } -#define BLACKLIST_NOLOADER(m,p) { m, p, noloader, blacklisted_str } -#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE) +enum req { + req_readbytes, + req_reset +}; -extern unsigned long atomwide_serial_loader[], oak_scsi_loader[], noloader[]; -static const char blacklisted_str[] = "*loader s/w is not 32-bit compliant*"; +struct ecard_request { + enum req req; + ecard_t *ec; + unsigned int address; + unsigned int length; + unsigned int use_loader; + void *buffer; +}; -static const struct expcard_blacklist { +struct expcard_blacklist { unsigned short manufacturer; unsigned short product; - const loader_t loader; const char *type; -} blacklist[] = { -/* Cards without names */ - BLACKLIST_NAME(MANU_ACORN, PROD_ACORN_ETHER1, "Acorn Ether1"), - -/* Cards with corrected loader */ - BLACKLIST_LOADER(MANU_ATOMWIDE, PROD_ATOMWIDE_3PSERIAL, atomwide_serial_loader), - BLACKLIST_LOADER(MANU_OAK, PROD_OAK_SCSI, oak_scsi_loader), +}; -/* Supported cards with broken loader */ - { MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI, noloader, "AlSystems PowerTec SCSI" }, +static ecard_t *cards; +static ecard_t *slot_to_expcard[MAX_ECARDS]; +static unsigned int ectcr; +#ifdef HAS_EXPMASK +static unsigned int have_expmask; +#endif -/* Unsupported cards with no loader */ - BLACKLIST_NOLOADER(MANU_MCS, PROD_MCS_CONNECT32) +/* List of descriptions of cards which don't have an extended + * identification, or chunk directories containing a description. + */ +static const struct expcard_blacklist __init blacklist[] = { + { MANU_ACORN, PROD_ACORN_ETHER1, "Acorn Ether1" } }; +asmlinkage extern int +ecard_loader_reset(volatile unsigned char *pa, loader_t loader); +asmlinkage extern int +ecard_loader_read(int off, volatile unsigned char *pa, loader_t loader); extern int setup_arm_irq(int, struct irqaction *); +extern void do_ecard_IRQ(int, struct pt_regs *); + + +static void +ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs); + +static struct irqaction irqexpansioncard = { + ecard_irq_noexpmask, SA_INTERRUPT, 0, "expansion cards", NULL, NULL +}; + +static inline unsigned short +ecard_getu16(unsigned char *v) +{ + return v[0] | v[1] << 8; +} + +static inline signed long +ecard_gets24(unsigned char *v) +{ + return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0); +} + +static inline ecard_t * +slot_to_ecard(unsigned int slot) +{ + return slot < MAX_ECARDS ? slot_to_expcard[slot] : NULL; +} +/* ===================== Expansion card daemon ======================== */ /* - * from linux/arch/arm/kernel/irq.c + * Since the loader programs on the expansion cards need to be run + * in a specific environment, create a separate task with this + * environment up, and pass requests to this task as and when we + * need to. + * + * This should allow 99% of loaders to be called from Linux. + * + * From a security standpoint, we trust the card vendors. This + * may be a misplaced trust. */ -extern void do_ecard_IRQ(int irq, struct pt_regs *); +#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE) +#define POD_INT_ADDR(x) ((volatile unsigned char *)\ + ((BUS_ADDR((x)) - IO_BASE) + IO_START)) -static ecard_t expcard[MAX_ECARDS]; -static signed char irqno_to_expcard[16]; -static unsigned int ecard_numcards, ecard_numirqcards; -static unsigned int have_expmask; +static void +ecard_task_reset(struct ecard_request *req) +{ + if (req->ec == NULL) { + ecard_t *ec; + + for (ec = cards; ec; ec = ec->next) { + printk(KERN_DEBUG "Resetting card %d\n", + ec->slot_no); + + if (ec->loader) + ecard_loader_reset(POD_INT_ADDR(ec->podaddr), + ec->loader); + } + printk(KERN_DEBUG "All cards reset\n"); + } else if (req->ec->loader) + ecard_loader_reset(POD_INT_ADDR(req->ec->podaddr), + req->ec->loader); +} + +static void +ecard_task_readbytes(struct ecard_request *req) +{ + unsigned char *buf = (unsigned char *)req->buffer; + volatile unsigned char *base_addr = + (volatile unsigned char *)POD_INT_ADDR(req->ec->podaddr); + unsigned int len = req->length; + + if (req->ec->slot_no == 8) { + /* + * The card maintains an index which + * increments the address into a 4096-byte + * page on each access. We need to keep + * track of the counter. + */ + static unsigned int index; + unsigned int offset, page; + unsigned char byte = 0; /* keep gcc quiet */ + + offset = req->address & 4095; + page = req->address >> 12; + + if (page > 256) + return; + + page *= 4; + + if (offset == 0 || index > offset) { + /* + * We need to reset the index counter. + */ + *base_addr = 0; + index = 0; + } + + while (index <= offset) { + byte = base_addr[page]; + index += 1; + } + + while (len--) { + *buf++ = byte; + if (len) { + byte = base_addr[page]; + index += 1; + } + } + } else { + unsigned int off = req->address; + + if (!req->use_loader || !req->ec->loader) { + off *= 4; + while (len--) { + *buf++ = base_addr[off]; + off += 4; + } + } else { + while(len--) { + /* + * The following is required by some + * expansion card loader programs. + */ + *(unsigned long *)0x108 = 0; + *buf++ = ecard_loader_read(off++, base_addr, + req->ec->loader); + } + } + } + +} + +#ifdef CONFIG_CPU_32 +static pid_t ecard_pid; +static wait_queue_head_t ecard_wait; +static wait_queue_head_t ecard_done; +static struct ecard_request *ecard_req; + +/* + * Set up the expansion card daemon's environment. + */ +static void +ecard_init_task(void) +{ + /* We want to set up the page tables for the following mapping: + * Virtual Physical + * 0x03000000 0x03000000 + * 0x03010000 unmapped + * 0x03210000 0x03210000 + * 0x03400000 unmapped + * 0x08000000 0x08000000 + * 0x10000000 unmapped + * + * FIXME: we don't follow this 100% yet. + */ + pgd_t *src_pgd, *dst_pgd; + unsigned int dst_addr = IO_START; + + src_pgd = pgd_offset(current->mm, IO_BASE); + dst_pgd = pgd_offset(current->mm, dst_addr); + + while (dst_addr < IO_START + IO_SIZE) { + *dst_pgd++ = *src_pgd++; + dst_addr += PGDIR_SIZE; + } + + flush_tlb_range(current->mm, IO_START, IO_START + IO_SIZE); + + dst_addr = EASI_START; + src_pgd = pgd_offset(current->mm, EASI_BASE); + dst_pgd = pgd_offset(current->mm, dst_addr); + + while (dst_addr < EASI_START + EASI_SIZE) { + *dst_pgd++ = *src_pgd++; + dst_addr += PGDIR_SIZE; + } + + flush_tlb_range(current->mm, EASI_START, EASI_START + EASI_SIZE); +} + +static int +ecard_task(void * unused) +{ + current->session = 1; + current->pgrp = 1; + + /* + * We don't want /any/ signals, not even SIGKILL + */ + sigfillset(¤t->blocked); + sigemptyset(¤t->signal); + + strcpy(current->comm, "kecardd"); + + /* + * Set up the environment + */ + ecard_init_task(); + + while (1) { + struct ecard_request *req; + + do { + req = xchg(&ecard_req, NULL); + + if (req == NULL) { + sigemptyset(¤t->signal); + interruptible_sleep_on(&ecard_wait); + } + } while (req == NULL); + + switch (req->req) { + case req_readbytes: + ecard_task_readbytes(req); + break; + + case req_reset: + ecard_task_reset(req); + break; + } + wake_up(&ecard_done); + } +} + +/* + * Wake the expansion card daemon to action our request. + * + * FIXME: The test here is not sufficient to detect if the + * kcardd is running. + */ +static inline void +ecard_call(struct ecard_request *req) +{ + /* + * If we're called from task 0, or from an + * interrupt (will be keyboard interrupt), + * we forcefully set up the memory map, and + * call the loader. We can't schedule, or + * sleep for this call. + */ + if ((current == task[0] || in_interrupt()) && + req->req == req_reset && req->ec == NULL) { + ecard_init_task(); + ecard_task_reset(req); + } else { + if (ecard_pid <= 0) + ecard_pid = kernel_thread(ecard_task, NULL, 0); + + ecard_req = req; + + wake_up(&ecard_wait); + + sleep_on(&ecard_done); + } +} +#else +/* + * On 26-bit processors, we don't need the kcardd thread to access the + * expansion card loaders. We do it directly. + */ +static inline void +ecard_call(struct ecard_request *req) +{ + if (req->req == req_reset) + ecard_task_reset(req); + else + ecard_task_readbytes(req); +} +#endif + +/* ======================= Mid-level card control ===================== */ +/* + * This is called to reset the loaders for each expansion card on reboot. + * + * This is required to make sure that the card is in the correct state + * that RiscOS expects it to be. + */ +void +ecard_reset(int slot) +{ + struct ecard_request req; + + req.req = req_reset; + + if (slot < 0) + req.ec = NULL; + else + req.ec = slot_to_ecard(slot); + + ecard_call(&req); + +#ifdef HAS_EXPMASK + if (have_expmask && slot < 0) { + have_expmask |= ~0; + EXPMASK_ENABLE = have_expmask; + } +#endif +} + +static void +ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld) +{ + struct ecard_request req; + + req.req = req_readbytes; + req.ec = ec; + req.address = off; + req.length = len; + req.use_loader = useld; + req.buffer = addr; + + ecard_call(&req); +} + +int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num) +{ + struct ex_chunk_dir excd; + int index = 16; + int useld = 0; + + if (!ec->cid.cd) + return 0; + + while(1) { + ecard_readbytes(&excd, ec, index, 8, useld); + index += 8; + if (c_id(&excd) == 0) { + if (!useld && ec->loader) { + useld = 1; + index = 0; + continue; + } + return 0; + } + if (c_id(&excd) == 0xf0) { /* link */ + index = c_start(&excd); + continue; + } + if (c_id(&excd) == 0x80) { /* loader */ + if (!ec->loader) { + ec->loader = (loader_t)kmalloc(c_len(&excd), + GFP_KERNEL); + if (ec->loader) + ecard_readbytes(ec->loader, ec, + (int)c_start(&excd), + c_len(&excd), useld); + else + return 0; + } + continue; + } + if (c_id(&excd) == id && num-- == 0) + break; + } + + if (c_id(&excd) & 0x80) { + switch (c_id(&excd) & 0x70) { + case 0x70: + ecard_readbytes((unsigned char *)excd.d.string, ec, + (int)c_start(&excd), c_len(&excd), + useld); + break; + case 0x00: + break; + } + } + cd->start_offset = c_start(&excd); + memcpy(cd->d.string, excd.d.string, 256); + return 1; +} + +/* ======================= Interrupt control ============================ */ static void ecard_def_irq_enable(ecard_t *ec, int irqnr) { @@ -100,6 +486,11 @@ #endif } +static int ecard_def_irq_pending(ecard_t *ec) +{ + return !ec->irqmask || ec->irqaddr[0] & ec->irqmask; +} + static void ecard_def_fiq_enable(ecard_t *ec, int fiqnr) { panic("ecard_def_fiq_enable called - impossible"); @@ -110,11 +501,18 @@ panic("ecard_def_fiq_disable called - impossible"); } +static int ecard_def_fiq_pending(ecard_t *ec) +{ + return !ec->fiqmask || ec->fiqaddr[0] & ec->fiqmask; +} + static expansioncard_ops_t ecard_default_ops = { ecard_def_irq_enable, ecard_def_irq_disable, + ecard_def_irq_pending, ecard_def_fiq_enable, - ecard_def_fiq_disable + ecard_def_fiq_disable, + ecard_def_fiq_pending }; /* @@ -125,10 +523,9 @@ */ void ecard_enableirq(unsigned int irqnr) { - irqnr &= 7; - if (irqnr < MAX_ECARDS && irqno_to_expcard[irqnr] != -1) { - ecard_t *ec = expcard + irqno_to_expcard[irqnr]; + ecard_t *ec = slot_to_ecard(irqnr - 32); + if (ec) { if (!ec->ops) ec->ops = &ecard_default_ops; @@ -142,10 +539,9 @@ void ecard_disableirq(unsigned int irqnr) { - irqnr &= 7; - if (irqnr < MAX_ECARDS && irqno_to_expcard[irqnr] != -1) { - ecard_t *ec = expcard + irqno_to_expcard[irqnr]; + ecard_t *ec = slot_to_ecard(irqnr - 32); + if (ec) { if (!ec->ops) ec->ops = &ecard_default_ops; @@ -156,10 +552,9 @@ void ecard_enablefiq(unsigned int fiqnr) { - fiqnr &= 7; - if (fiqnr < MAX_ECARDS && irqno_to_expcard[fiqnr] != -1) { - ecard_t *ec = expcard + irqno_to_expcard[fiqnr]; + ecard_t *ec = slot_to_ecard(fiqnr); + if (ec) { if (!ec->ops) ec->ops = &ecard_default_ops; @@ -173,10 +568,9 @@ void ecard_disablefiq(unsigned int fiqnr) { - fiqnr &= 7; - if (fiqnr < MAX_ECARDS && irqno_to_expcard[fiqnr] != -1) { - ecard_t *ec = expcard + irqno_to_expcard[fiqnr]; + ecard_t *ec = slot_to_ecard(fiqnr); + if (ec) { if (!ec->ops) ec->ops = &ecard_default_ops; @@ -185,41 +579,89 @@ } } -static void ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs) +static void +ecard_dump_irq_state(ecard_t *ec) { - const int num_cards = ecard_numirqcards; - int i, called = 0; + printk(" %d: %sclaimed, ", + ec->slot_no, + ec->claimed ? "" : "not "); + + if (ec->ops && ec->ops->irqpending && + ec->ops != &ecard_default_ops) + printk("irq %spending\n", + ec->ops->irqpending(ec) ? "" : "not "); + else + printk("irqaddr %p, mask = %02X, status = %02X\n", + ec->irqaddr, ec->irqmask, *ec->irqaddr); +} - for (i = 0; i < num_cards; i++) { - if (expcard[i].claimed && expcard[i].irq && - (!expcard[i].irqmask || - expcard[i].irqaddr[0] & expcard[i].irqmask)) { - do_ecard_IRQ(expcard[i].irq, regs); - called ++; +static void +ecard_check_lockup(void) +{ + static int last, lockup; + ecard_t *ec; + + /* + * If the timer interrupt has not run since the last million + * unrecognised expansion card interrupts, then there is + * something seriously wrong. Disable the expansion card + * interrupts so at least we can continue. + * + * Maybe we ought to start a timer to re-enable them some time + * later? + */ + if (last == jiffies) { + lockup += 1; + if (lockup > 1000000) { + printk(KERN_ERR "\nInterrupt lockup detected - " + "disabling all expansion card interrupts\n"); + + disable_irq(IRQ_EXPANSIONCARD); + + printk("Expansion card IRQ state:\n"); + + for (ec = cards; ec; ec = ec->next) + ecard_dump_irq_state(ec); } + } else + lockup = 0; + + /* + * If we did not recognise the source of this interrupt, + * warn the user, but don't flood the user with these messages. + */ + if (!last || time_after(jiffies, last + 5*HZ)) { + last = jiffies; + printk(KERN_WARNING "Unrecognised interrupt from backplane\n"); } - cli(); - if (called == 0) { - static int last, lockup; +} - if (last == jiffies) { - lockup += 1; - if (lockup > 1000000) { - printk(KERN_ERR "\nInterrupt lockup detected - disabling expansion card IRQs\n"); - disable_irq(intr_no); - printk("Expansion card IRQ state:\n"); - for (i = 0; i < num_cards; i++) - printk(" %d: %sclaimed, irqaddr = %p, irqmask = %X, status=%X\n", expcard[i].irq - 32, - expcard[i].claimed ? "" : "not", expcard[i].irqaddr, expcard[i].irqmask, *expcard[i].irqaddr); - } - } else - lockup = 0; +static void +ecard_irq_noexpmask(int intr_no, void *dev_id, struct pt_regs *regs) +{ + ecard_t *ec; + int called = 0; + + for (ec = cards; ec; ec = ec->next) { + int pending; + + if (!ec->claimed || ec->irq == NO_IRQ || ec->slot_no == 8) + continue; + + if (ec->ops && ec->ops->irqpending) + pending = ec->ops->irqpending(ec); + else + pending = ecard_default_ops.irqpending(ec); - if (!last || time_after(jiffies, last + 5*HZ)) { - last = jiffies; - printk(KERN_ERR "\nUnrecognised interrupt from backplane\n"); + if (pending) { + do_ecard_IRQ(ec->irq, regs); + called ++; } } + cli(); + + if (called == 0) + ecard_check_lockup(); } #ifdef HAS_EXPMASK @@ -234,31 +676,35 @@ 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00 }; -static void ecard_irq_expmask(int intr_no, void *dev_id, struct pt_regs *regs) +static void +ecard_irq_expmask(int intr_no, void *dev_id, struct pt_regs *regs) { const unsigned int statusmask = 15; unsigned int status; status = EXPMASK_STATUS & statusmask; if (status) { - unsigned int irqno; + unsigned int slot; ecard_t *ec; again: - irqno = first_set[status]; - ec = expcard + irqno_to_expcard[irqno]; + slot = first_set[status]; + ec = slot_to_ecard(slot); if (ec->claimed) { unsigned int oldexpmask; /* - * this ugly code is so that we can operate a prioritorising system. + * this ugly code is so that we can operate a + * prioritorising system: + * * Card 0 highest priority * Card 1 * Card 2 * Card 3 lowest priority + * * Serial cards should go in 0/1, ethernet/scsi in 2/3 * otherwise you will lose serial data at high speeds! */ oldexpmask = have_expmask; - EXPMASK_ENABLE = (have_expmask &= priority_masks[irqno]); + EXPMASK_ENABLE = (have_expmask &= priority_masks[slot]); sti(); do_ecard_IRQ(ec->irq, regs); cli(); @@ -267,15 +713,18 @@ if (status) goto again; } else { - printk(KERN_WARNING "card%d: interrupt from unclaimed card???\n", irqno); - EXPMASK_ENABLE = (have_expmask &= ~(1 << irqno)); + printk(KERN_WARNING "card%d: interrupt from unclaimed " + "card???\n", slot); + EXPMASK_ENABLE = (have_expmask &= ~(1 << slot)); } } else printk(KERN_WARNING "Wild interrupt from backplane (masks)\n"); } -static int ecard_checkirqhw(void) +__initfunc(static void +ecard_probeirqhw(void)) { + ecard_t *ec; int found; EXPMASK_ENABLE = 0x00; @@ -283,62 +732,80 @@ found = ((EXPMASK_STATUS & 15) == 0); EXPMASK_ENABLE = 0xff; - return found; + if (!found) + return; + + printk(KERN_DEBUG "Expansion card interrupt " + "management hardware found\n"); + + irqexpansioncard.handler = ecard_irq_expmask; + + /* for each card present, set a bit to '1' */ + have_expmask = 0x80000000; + + for (ec = cards; ec; ec = ec->next) + have_expmask |= 1 << ec->slot_no; + + EXPMASK_ENABLE = have_expmask; } +#else +#define ecard_probeirqhw() #endif -static void ecard_readbytes(void *addr, ecard_t *ec, int off, int len, int useld) +#ifndef IO_EC_MEMC8_BASE +#define IO_EC_MEMC8_BASE 0 +#endif + +unsigned int ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed) { - extern int ecard_loader_read(int off, volatile unsigned int pa, loader_t loader); - unsigned char *a = (unsigned char *)addr; + unsigned long address = 0; + int slot = ec->slot_no; - if (ec->slot_no == 8) { - static unsigned int lowaddress; - unsigned int laddr, haddr; - unsigned char byte = 0; /* keep gcc quiet */ + if (ec->slot_no == 8) + return IO_EC_MEMC8_BASE; - laddr = off & 4095; /* number of bytes to read from offset + base addr */ - haddr = off >> 12; /* offset into card from base addr */ + ectcr &= ~(1 << slot); - if (haddr > 256) - return; + switch (type) { + case ECARD_MEMC: + if (slot < 4) + address = IO_EC_MEMC_BASE + (slot << 12); + break; - /* - * If we require a low address or address 0, then reset, and start again... - */ - if (!off || lowaddress > laddr) { - outb(0, ec->podaddr); - lowaddress = 0; - } - while (lowaddress <= laddr) { - byte = inb(ec->podaddr + haddr); - lowaddress += 1; - } - while (len--) { - *a++ = byte; - if (len) { - byte = inb(ec->podaddr + haddr); - lowaddress += 1; - } - } - } else { - if (!useld || !ec->loader) { - while(len--) - *a++ = inb(ec->podaddr + (off++)); - } else { - while(len--) { - *(unsigned long *)0x108 = 0; /* hack for some loaders!!! */ - *a++ = ecard_loader_read(off++, BUS_ADDR(ec->podaddr), ec->loader); - } - } + case ECARD_IOC: + if (slot < 4) + address = IO_EC_IOC_BASE + (slot << 12); +#ifdef IO_EC_IOC4_BASE + else + address = IO_EC_IOC4_BASE + ((slot - 4) << 12); +#endif + if (address) + address += speed << 17; + break; + +#ifdef IO_EC_EASI_BASE + case ECARD_EASI: + address = IO_EC_EASI_BASE + (slot << 22); + if (speed == ECARD_FAST) + ectcr |= 1 << slot; + break; +#endif } + +#ifdef IOMD_ECTCR + outb(ectcr, IOMD_ECTCR); +#endif + return address; } +static const char *unknown = "*unknown*"; + static int ecard_prints(char *buffer, ecard_t *ec) { char *start = buffer; - buffer += sprintf(buffer, "\n %d: ", ec->slot_no); + buffer += sprintf(buffer, " %d: %s ", ec->slot_no, + ec->type == ECARD_EASI ? "EASI" : " "); if (ec->cid.id == 0) { struct in_chunk_dir incd; @@ -346,28 +813,57 @@ buffer += sprintf(buffer, "[%04X:%04X] ", ec->cid.manufacturer, ec->cid.product); - if (!ec->card_desc && ec->cid.is && ec->cid.cd && - ecard_readchunk(&incd, ec, 0xf5, 0)) - ec->card_desc = incd.d.string; + if (!ec->card_desc && ec->cid.cd && + ecard_readchunk(&incd, ec, 0xf5, 0)) { + ec->card_desc = kmalloc(strlen(incd.d.string)+1, GFP_KERNEL); - if (!ec->card_desc) - ec->card_desc = "*unknown*"; + if (ec->card_desc) + strcpy(ec->card_desc, incd.d.string); + } - buffer += sprintf(buffer, "%s", ec->card_desc); + buffer += sprintf(buffer, "%s\n", ec->card_desc ? ec->card_desc : "*unknown*"); } else - buffer += sprintf(buffer, "Simple card %d", ec->cid.id); + buffer += sprintf(buffer, "Simple card %d\n", ec->cid.id); return buffer - start; } -static inline unsigned short ecard_getu16(unsigned char *v) +int get_ecard_dev_info(char *buf, char **start, off_t pos, int count, int wr) { - return v[0] | v[1] << 8; + ecard_t *ec = cards; + off_t at = 0; + int len, cnt; + + cnt = 0; + while (ec && count > cnt) { + len = ecard_prints(buf, ec); + at += len; + if (at >= pos) { + if (!*start) { + *start = buf + (pos - (at - len)); + cnt = at - pos; + } else + cnt += len; + buf += len; + } + ec = ec->next; + } + return (count > cnt) ? cnt : count; } -static inline signed long ecard_gets24(unsigned char *v) +static struct proc_dir_entry proc_ecard_devices = { + PROC_BUS_ECARD_DEVICES, 7, "devices", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_array_inode_operations, + get_ecard_dev_info +}; + +static struct proc_dir_entry *proc_bus_ecard_dir; + +static void ecard_proc_init(void) { - return v[0] | v[1] << 8 | v[2] << 16 | ((v[2] & 0x80) ? 0xff000000 : 0); + proc_bus_ecard_dir = create_proc_entry("ecard", S_IFDIR, proc_bus); + proc_register(proc_bus_ecard_dir, &proc_ecard_devices); } /* @@ -376,33 +872,39 @@ * If bit 1 of the first byte of the card is set, then the * card does not exist. */ -__initfunc(static int ecard_probe(int card, int freeslot, card_type_t type)) +__initfunc(static int +ecard_probe(int slot, card_type_t type)) { - ecard_t *ec = expcard + freeslot; + ecard_t **ecp; + ecard_t *ec; struct ex_ecid cid; char buffer[200]; - int i; + int i, rc = -ENOMEM; + + ec = kmalloc(sizeof(ecard_t), GFP_KERNEL); - irqno_to_expcard[card] = -1; + if (!ec) + goto nodev; - ec->slot_no = card; + memset(ec, 0, sizeof(ecard_t)); + + ec->slot_no = slot; + ec->type = type; ec->irq = NO_IRQ; ec->fiq = NO_IRQ; ec->dma = NO_DMA; ec->card_desc = NULL; ec->ops = &ecard_default_ops; + rc = -ENODEV; if ((ec->podaddr = ecard_address(ec, type, ECARD_SYNC)) == 0) - return 0; + goto nodev; cid.r_zero = 1; ecard_readbytes(&cid, ec, 0, 16, 0); if (cid.r_zero) - return 0; - - irqno_to_expcard[card] = freeslot; + goto nodev; - ec->type = type; ec->cid.id = cid.r_id; ec->cid.cd = cid.r_cd; ec->cid.is = cid.r_is; @@ -415,9 +917,9 @@ ec->cid.fiqmask = cid.r_fiqmask; ec->cid.fiqoff = ecard_gets24(cid.r_fiqoff); ec->fiqaddr = - ec->irqaddr = (unsigned char *)BUS_ADDR(ec->podaddr); + ec->irqaddr = (unsigned char *)ioaddr(ec->podaddr); - if (ec->cid.cd && ec->cid.is) { + if (ec->cid.is) { ec->irqmask = ec->cid.irqmask; ec->irqaddr += ec->cid.irqoff; ec->fiqmask = ec->cid.fiqmask; @@ -430,88 +932,69 @@ for (i = 0; i < sizeof(blacklist) / sizeof(*blacklist); i++) if (blacklist[i].manufacturer == ec->cid.manufacturer && blacklist[i].product == ec->cid.product) { - ec->loader = blacklist[i].loader; ec->card_desc = blacklist[i].type; break; } - ecard_prints(buffer, ec); - printk("%s", buffer); - - ec->irq = 32 + card; + ec->irq = 32 + slot; #ifdef IO_EC_MEMC8_BASE - if (card == 8) + if (slot == 8) ec->irq = 11; #endif #ifdef CONFIG_ARCH_RPC /* On RiscPC, only first two slots have DMA capability */ - if (card < 2) - ec->dma = 2 + card; + if (slot < 2) + ec->dma = 2 + slot; #endif #if 0 /* We don't support FIQs on expansion cards at the moment */ - ec->fiq = 96 + card; + ec->fiq = 96 + slot; #endif - return 1; -} + rc = 0; -/* - * This is called to reset the loaders for each expansion card on reboot. - * - * This is required to make sure that the card is in the correct state - * that RiscOS expects it to be. - */ -void ecard_reset(int card) -{ - extern int ecard_loader_reset(volatile unsigned int pa, loader_t loader); + for (ecp = &cards; *ecp; ecp = &(*ecp)->next); - if (card >= ecard_numcards) - return; - - if (card < 0) { - for (card = 0; card < ecard_numcards; card++) - if (expcard[card].loader) - ecard_loader_reset(BUS_ADDR(expcard[card].podaddr), - expcard[card].loader); - } else - if (expcard[card].loader) - ecard_loader_reset(BUS_ADDR(expcard[card].podaddr), - expcard[card].loader); + *ecp = ec; -#ifdef HAS_EXPMASK - if (have_expmask) { - have_expmask |= ~0; - EXPMASK_ENABLE = have_expmask; +nodev: + if (rc && ec) + kfree(ec); + else { + slot_to_expcard[slot] = ec; + + ecard_prints(buffer, ec); + printk("%s", buffer); } -#endif + return rc; } -static unsigned int ecard_startcard; +static ecard_t *finding_pos; void ecard_startfind(void) { - ecard_startcard = 0; + finding_pos = NULL; } ecard_t *ecard_find(int cid, const card_ids *cids) { - int card; + if (!finding_pos) + finding_pos = cards; + else + finding_pos = finding_pos->next; - if (!cids) { - for (card = ecard_startcard; card < ecard_numcards; card++) - if (!expcard[card].claimed && - (expcard[card].cid.id ^ cid) == 0) + for (; finding_pos; finding_pos = finding_pos->next) { + if (finding_pos->claimed) + continue; + + if (!cids) { + if ((finding_pos->cid.id ^ cid) == 0) break; - } else { - for (card = ecard_startcard; card < ecard_numcards; card++) { + } else { unsigned int manufacturer, product; int i; - if (expcard[card].claimed) - continue; - - manufacturer = expcard[card].cid.manufacturer; - product = expcard[card].cid.product; + manufacturer = finding_pos->cid.manufacturer; + product = finding_pos->cid.product; for (i = 0; cids[i].manufacturer != 65535; i++) if (manufacturer == cids[i].manufacturer && @@ -523,111 +1006,24 @@ } } - ecard_startcard = card + 1; - - return card < ecard_numcards ? &expcard[card] : NULL; + return finding_pos; } -int ecard_readchunk(struct in_chunk_dir *cd, ecard_t *ec, int id, int num) +__initfunc(static void ecard_free_all(void)) { - struct ex_chunk_dir excd; - int index = 16; - int useld = 0; + ecard_t *ec, *ecn; - if (!ec->cid.is || !ec->cid.cd) - return 0; + for (ec = cards; ec; ec = ecn) { + ecn = ec->next; - while(1) { - ecard_readbytes(&excd, ec, index, 8, useld); - index += 8; - if (c_id(&excd) == 0) { - if (!useld && ec->loader) { - useld = 1; - index = 0; - continue; - } - return 0; - } - if (c_id(&excd) == 0xf0) { /* link */ - index = c_start(&excd); - continue; - } - if (c_id(&excd) == 0x80) { /* loader */ - if (!ec->loader) { - ec->loader = (loader_t)kmalloc(c_len(&excd), GFP_KERNEL); - ecard_readbytes(ec->loader, ec, (int)c_start(&excd), c_len(&excd), useld); - } - continue; - } - if (c_id(&excd) == id && num-- == 0) - break; + kfree(ec); } - if (c_id(&excd) & 0x80) { - switch (c_id(&excd) & 0x70) { - case 0x70: - ecard_readbytes((unsigned char *)excd.d.string, ec, - (int)c_start(&excd), c_len(&excd), useld); - break; - case 0x00: - break; - } - } - cd->start_offset = c_start(&excd); - memcpy(cd->d.string, excd.d.string, 256); - return 1; -} - -unsigned int ecard_address(ecard_t *ec, card_type_t type, card_speed_t speed) -{ - switch (ec->slot_no) { - case 0 ... 3: - switch (type) { - case ECARD_MEMC: - return IO_EC_MEMC_BASE + (ec->slot_no << 12); - - case ECARD_IOC: - return IO_EC_IOC_BASE + (speed << 17) + (ec->slot_no << 12); + cards = NULL; -#ifdef IO_EC_EASI_BASE - case ECARD_EASI: - return IO_EC_EASI_BASE + (ec->slot_no << 22); -#endif - } - break; - - case 4 ... 7: - switch (type) { -#ifdef IO_EC_IOC4_BASE - case ECARD_IOC: - return IO_EC_IOC4_BASE + (speed << 17) + ((ec->slot_no - 4) << 12); -#endif -#ifdef IO_EC_EASI_BASE - case ECARD_EASI: - return IO_EC_EASI_BASE + (ec->slot_no << 22); -#endif - default: - break; - } - break; - -#ifdef IO_EC_MEMC8_BASE - case 8: - return IO_EC_MEMC8_BASE; -#endif - } - return 0; + memset(slot_to_expcard, 0, sizeof(slot_to_expcard)); } -static struct irqaction irqexpansioncard = { - ecard_irq_noexpmask, - SA_INTERRUPT, - 0, - "expansion cards", - NULL, - NULL -}; - /* * Initialise the expansion card system. * Locate all hardware - interrupt management and @@ -635,51 +1031,38 @@ */ __initfunc(void ecard_init(void)) { - int i, nc = 0; + int slot; - memset(expcard, 0, sizeof(expcard)); + oldlatch_init(); -#ifdef HAS_EXPMASK - if (ecard_checkirqhw()) { - printk(KERN_DEBUG "Expansion card interrupt management hardware found\n"); - irqexpansioncard.handler = ecard_irq_expmask; - irqexpansioncard.flags |= SA_IRQNOMASK; - have_expmask = -1; - } +#ifdef CONFIG_CPU_32 + init_waitqueue_head(&ecard_wait); + init_waitqueue_head(&ecard_done); #endif - printk("Installed expansion cards:"); + printk("Probing expansion cards: (does not imply support)\n"); - /* - * First of all, probe all cards on the expansion card interrupt line - */ - for (i = 0; i < 8; i++) - if (ecard_probe(i, nc, ECARD_IOC) || ecard_probe(i, nc, ECARD_EASI)) - nc += 1; - else - have_expmask &= ~(1< + .equ ioc_base_high, IOC_BASE & 0xff000000 .equ ioc_base_low, IOC_BASE & 0x00ff0000 .macro disable_fiq @@ -186,113 +190,109 @@ .byte 6, 6, 6, 6, 2, 2, 2, 2, 3, 3, 6, 6, 2, 2, 2, 2 .endm -#elif defined(CONFIG_ARCH_EBSA285) +#elif defined(CONFIG_HOST_FOOTBRIDGE) || defined(CONFIG_ADDIN_FOOTBRIDGE) +#include .macro disable_fiq .endm + .equ irq_mask_pci_err_high, IRQ_MASK_PCI_ERR & 0xff000000 + .equ irq_mask_pci_err_low, IRQ_MASK_PCI_ERR & 0x00ffffff + .equ dc21285_high, ARMCSR_BASE & 0xff000000 + .equ dc21285_low, ARMCSR_BASE & 0x00ffffff + .macro get_irqnr_and_base, irqnr, irqstat, base - mov r4, #0xfe000000 + mov r4, #dc21285_high + .if dc21285_low + orr r4, r4, #dc21285_low + .endif ldr \irqstat, [r4, #0x180] @ get interrupts - mov \irqnr, #0 -1001: tst \irqstat, #1 - addeq \irqnr, \irqnr, #1 - moveq \irqstat, \irqstat, lsr #1 - tsteq \irqnr, #32 - beq 1001b - teq \irqnr, #32 - .endm - .macro irq_prio_table - .endm + mov \irqnr, #IRQ_SDRAMPARITY + tst \irqstat, #IRQ_MASK_SDRAMPARITY + bne 1001f -#elif defined(CONFIG_ARCH_NEXUSPCI) + tst \irqstat, #IRQ_MASK_UART_RX + movne \irqnr, #IRQ_CONRX + bne 1001f - .macro disable_fiq - .endm + tst \irqstat, #IRQ_MASK_DMA1 + movne \irqnr, #IRQ_DMA1 + bne 1001f - .macro get_irqnr_and_base, irqnr, irqstat, base - ldr r4, =0xffe00000 - ldr \irqstat, [r4, #0x180] @ get interrupts - mov \irqnr, #0 -1001: tst \irqstat, #1 - addeq \irqnr, \irqnr, #1 - moveq \irqstat, \irqstat, lsr #1 - tsteq \irqnr, #32 - beq 1001b - teq \irqnr, #32 - .endm + tst \irqstat, #IRQ_MASK_DMA2 + movne \irqnr, #IRQ_DMA2 + bne 1001f - .macro irq_prio_table - .endm + tst \irqstat, #IRQ_MASK_IN0 + movne \irqnr, #IRQ_IN0 + bne 1001f -#elif defined(CONFIG_ARCH_VNC) + tst \irqstat, #IRQ_MASK_IN1 + movne \irqnr, #IRQ_IN1 + bne 1001f - .macro disable_fiq - .endm + tst \irqstat, #IRQ_MASK_IN2 + movne \irqnr, #IRQ_IN2 + bne 1001f - .equ pci_iack_high, PCI_IACK & 0xff000000 - .equ pci_iack_low, PCI_IACK & 0x00ff0000 + tst \irqstat, #IRQ_MASK_IN3 + movne \irqnr, #IRQ_IN3 + bne 1001f - .macro get_irqnr_and_base, irqnr, irqstat, base - mov r4, #IO_BASE_ARM_CSR - ldr \irqstat, [r4, #CSR_IRQ_STATUS] @ just show us the unmasked ones + tst \irqstat, #IRQ_MASK_PCI + movne \irqnr, #IRQ_PCI + bne 1001f - @ run through hard priorities - @ timer - tst \irqstat, #IRQ_MASK_TIMER0 - movne \irqnr, #IRQ_TIMER0 + tst \irqstat, #IRQ_MASK_I2OINPOST + movne \irqnr, #IRQ_I2OINPOST bne 1001f - @ ether10 - tst \irqstat, #IRQ_MASK_ETHER10 - movne \irqnr, #IRQ_ETHER10 + tst \irqstat, #IRQ_MASK_TIMER1 + movne \irqnr, #IRQ_TIMER1 bne 1001f - @ ether100 - tst \irqstat, #IRQ_MASK_ETHER100 - movne \irqnr, #IRQ_ETHER100 + tst \irqstat, #IRQ_MASK_TIMER2 + movne \irqnr, #IRQ_TIMER2 bne 1001f - @ video compressor - tst \irqstat, #IRQ_MASK_VIDCOMP - movne \irqnr, #IRQ_VIDCOMP + tst \irqstat, #IRQ_MASK_TIMER3 + movne \irqnr, #IRQ_TIMER3 bne 1001f - @ now try all the PIC sources - @ determine whether we have an irq - tst \irqstat, #IRQ_MASK_EXTERN_IRQ - beq 1002f - mov r4, #pci_iack_high - orr r4, r4, #pci_iack_low - ldrb \irqnr, [r4] @ get the IACK byte - b 1001f + tst \irqstat, #IRQ_MASK_UART_TX + movne \irqnr, #IRQ_CONTX + bne 1001f -1002: @ PCI errors - tst \irqstat, #IRQ_MASK_PCI_ERR + tst \irqstat, #irq_mask_pci_err_high + tsteq \irqstat, #irq_mask_pci_err_low movne \irqnr, #IRQ_PCI_ERR bne 1001f +1001: + .endm - @ softint - tst \irqstat, #IRQ_MASK_SOFTIRQ - movne \irqnr, #IRQ_SOFTIRQ - bne 1001f + .macro irq_prio_table + .endm - @ debug uart - tst \irqstat, #IRQ_MASK_UART_DEBUG - movne \irqnr, #IRQ_CONRX - bne 1001f +#elif defined(CONFIG_ARCH_NEXUSPCI) - @ watchdog - tst \irqstat, #IRQ_MASK_WATCHDOG - movne \irqnr, #IRQ_WATCHDOG + .macro disable_fiq + .endm -1001: @ If Z is set, then we will not enter an interrupt + .macro get_irqnr_and_base, irqnr, irqstat, base + ldr r4, =0xffe00000 + ldr \irqstat, [r4, #0x180] @ get interrupts + mov \irqnr, #0 +1001: tst \irqstat, #1 + addeq \irqnr, \irqnr, #1 + moveq \irqstat, \irqstat, lsr #1 + tsteq \irqnr, #32 + beq 1001b + teq \irqnr, #32 .endm .macro irq_prio_table .endm - #else #error Unknown architecture #endif @@ -306,22 +306,22 @@ stmia sp, {r0 - r12} @ Calling r0 - r12 add r8, sp, #S_PC stmdb r8, {sp, lr}^ @ Calling sp, lr - mov r7, r0 + str lr, [r8, #0] @ Save calling PC mrs r6, spsr - mov r5, lr - stmia r8, {r5, r6, r7} @ Save calling PC, CPSR, OLD_R0 + str r6, [r8, #4] @ Save CPSR + str r0, [r8, #8] @ Save OLD_R0 .endm .macro restore_user_regs - mrs r0, cpsr @ disable IRQs - orr r0, r0, #I_BIT - msr cpsr, r0 + mrs r1, cpsr @ disable IRQs + orr r1, r1, #I_BIT ldr r0, [sp, #S_PSR] @ Get calling cpsr + msr cpsr, r1 msr spsr, r0 @ save in spsr_svc ldmia sp, {r0 - lr}^ @ Get calling r0 - lr mov r0, r0 - add sp, sp, #S_PC - ldr lr, [sp], #S_FRAME_SIZE - S_PC @ Get PC and jump over PC, PSR, OLD_R0 + ldr lr, [sp, #S_PC] @ Get PC + add sp, sp, #S_FRAME_SIZE movs pc, lr @ return & move spsr_svc into cpsr .endm @@ -348,25 +348,6 @@ msr cpsr, \temp .endm - .macro initialise_traps_extra - mrs r0, cpsr - bic r0, r0, #31 - orr r0, r0, #0xd3 - msr cpsr, r0 - .endm - - -#ifndef __ARM_ARCH_4__ -.Larm700bug: str lr, [r8] - ldr r0, [sp, #S_PSR] @ Get calling cpsr - msr spsr, r0 - ldmia sp, {r0 - lr}^ @ Get calling r0 - lr - mov r0, r0 - add sp, sp, #S_PC - ldr lr, [sp], #S_FRAME_SIZE - S_PC @ Get PC and jump over PC, PSR, OLD_R0 - movs pc, lr -#endif - .macro get_current_task, rd mov \rd, sp, lsr #13 mov \rd, \rd, lsl #13 @@ -379,231 +360,89 @@ adr\cond \reg, \label .endm -/*============================================================================= - * Address exception handler - *----------------------------------------------------------------------------- - * These aren't too critical. - * (they're not supposed to happen, and won't happen in 32-bit mode). - */ - -vector_addrexcptn: - b vector_addrexcptn - -/*============================================================================= - * Undefined FIQs - *----------------------------------------------------------------------------- - * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC - * MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg. - * Basically to switch modes, we *HAVE* to clobber one register... brain - * damage alert! I don't think that we can execute any code in here in any - * other mode than FIQ... Ok you can switch to another mode, but you can't - * get out of that mode without clobbering one register. - */ -_unexp_fiq: disable_fiq - subs pc, lr, #4 - -/*============================================================================= - * Interrupt entry dispatcher - *----------------------------------------------------------------------------- - * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC - */ -vector_IRQ: @ - @ save mode specific registers - @ - ldr r13, .LCirq - sub lr, lr, #4 - str lr, [r13] @ save lr_IRQ - mrs lr, spsr - str lr, [r13, #4] @ save spsr_IRQ - @ - @ now branch to the relevent MODE handling routine - @ - mrs sp, cpsr @ switch to SVC mode - bic sp, sp, #31 - orr sp, sp, #0x13 - msr spsr, sp - and lr, lr, #15 - cmp lr, #4 - addlts pc, pc, lr, lsl #2 @ Changes mode and branches - b __irq_invalid @ 4 - 15 - b __irq_usr @ 0 (USR_26 / USR_32) - b __irq_invalid @ 1 (FIQ_26 / FIQ_32) - b __irq_invalid @ 2 (IRQ_26 / IRQ_32) - b __irq_svc @ 3 (SVC_26 / SVC_32) -/* - *------------------------------------------------------------------------------------------------ - * Undef instr entry dispatcher - dispatches it to the correct handler for the processor mode - *------------------------------------------------------------------------------------------------ - * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC - */ -.LCirq: .word __temp_irq -.LCund: .word __temp_und -.LCabt: .word __temp_abt - -vector_undefinstr: - @ - @ save mode specific registers - @ - ldr r13, [pc, #.LCund - . - 8] - str lr, [r13] - mrs lr, spsr - str lr, [r13, #4] - @ - @ now branch to the relevent MODE handling routine - @ - mrs sp, cpsr - bic sp, sp, #31 - orr sp, sp, #0x13 - msr spsr, sp - and lr, lr, #15 - cmp lr, #4 - addlts pc, pc, lr, lsl #2 @ Changes mode and branches - b __und_invalid @ 4 - 15 - b __und_usr @ 0 (USR_26 / USR_32) - b __und_invalid @ 1 (FIQ_26 / FIQ_32) - b __und_invalid @ 2 (IRQ_26 / IRQ_32) - b __und_svc @ 3 (SVC_26 / SVC_32) -/* - *------------------------------------------------------------------------------------------------ - * Prefetch abort dispatcher - dispatches it to the correct handler for the processor mode - *------------------------------------------------------------------------------------------------ - * Enter in ABT mode, spsr = USR CPSR, lr = USR PC - */ -vector_prefetch: - @ - @ save mode specific registers - @ - sub lr, lr, #4 - ldr r13, .LCabt - str lr, [r13] - mrs lr, spsr - str lr, [r13, #4] - @ - @ now branch to the relevent MODE handling routine - @ - mrs sp, cpsr - bic sp, sp, #31 - orr sp, sp, #0x13 - msr spsr, sp - and lr, lr, #15 - adds pc, pc, lr, lsl #2 @ Changes mode and branches - b __pabt_invalid @ 4 - 15 - b __pabt_usr @ 0 (USR_26 / USR_32) - b __pabt_invalid @ 1 (FIQ_26 / FIQ_32) - b __pabt_invalid @ 2 (IRQ_26 / IRQ_32) - b __pabt_invalid @ 3 (SVC_26 / SVC_32) /* - *------------------------------------------------------------------------------------------------ - * Data abort dispatcher - dispatches it to the correct handler for the processor mode - *------------------------------------------------------------------------------------------------ - * Enter in ABT mode, spsr = USR CPSR, lr = USR PC + * Invalid mode handlers */ -vector_data: @ - @ save mode specific registers - @ - sub lr, lr, #8 - ldr r13, .LCabt - str lr, [r13] - mrs lr, spsr - str lr, [r13, #4] - @ - @ now branch to the relevent MODE handling routine - @ - mrs sp, cpsr - bic sp, sp, #31 - orr sp, sp, #0x13 - msr spsr, sp - and lr, lr, #15 - cmp lr, #4 - addlts pc, pc, lr, lsl #2 @ Changes mode & branches - b __dabt_invalid @ 4 - 15 - b __dabt_usr @ 0 (USR_26 / USR_32) - b __dabt_invalid @ 1 (FIQ_26 / FIQ_32) - b __dabt_invalid @ 2 (IRQ_26 / IRQ_32) - b __dabt_svc @ 3 (SVC_26 / SVC_32) +__pabt_invalid: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go + stmia sp, {r0 - lr} @ Save XXX r0 - lr + ldr r4, .LCabt + mov r1, #BAD_PREFETCH + b 1f -/*============================================================================= - * Prefetch abort handler - *----------------------------------------------------------------------------- - */ -pabtmsg: .ascii "Pabt: %08lX\n\0" - .align -__pabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go - stmia sp, {r0 - r12} @ Save r0 - r12 - add r8, sp, #S_PC - stmdb r8, {sp, lr}^ @ Save sp_usr lr_usr +__dabt_invalid: sub sp, sp, #S_FRAME_SIZE + stmia sp, {r0 - lr} @ Save SVC r0 - lr [lr *should* be intact] ldr r4, .LCabt - ldmia r4, {r5 - r7} @ Get USR pc, cpsr - stmia r8, {r5 - r7} @ Save USR pc, cpsr, old_r0 + mov r1, #BAD_DATA + b 1f - mrs r7, cpsr @ Enable interrupts if they were - bic r7, r7, #I_BIT @ previously - msr cpsr, r7 - mov r0, r5 @ address (pc) - mov r1, sp @ regs - bl SYMBOL_NAME(do_PrefetchAbort) @ call abort handler - teq r0, #0 @ Does this still apply??? - bne ret_from_exception @ Return from exception -#ifdef DEBUG_UNDEF - adr r0, t - bl SYMBOL_NAME(printk) -#endif - mov r0, r5 - mov r1, sp - and r2, r6, #31 - bl SYMBOL_NAME(do_undefinstr) - ldr lr, [sp, #S_PSR] @ Get USR cpsr - msr spsr, lr - ldmia sp, {r0 - pc}^ @ Restore USR registers +__irq_invalid: sub sp, sp, #S_FRAME_SIZE @ Allocate space on stack for frame + stmfd sp, {r0 - lr} @ Save r0 - lr + ldr r4, .LCirq + mov r1, #BAD_IRQ + b 1f -__pabt_invalid: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go - stmia sp, {r0 - lr} @ Save XXX r0 - lr - mov r7, r0 @ OLD R0 - ldr r4, .LCabt - ldmia r4, {r5 - r7} @ Get XXX pc, cpsr +__und_invalid: sub sp, sp, #S_FRAME_SIZE + stmia sp, {r0 - lr} + ldr r4, .LCund + mov r1, #BAD_UNDEFINSTR @ int reason + +1: mov fp, #0 + ldmia r4, {r5 - r7} @ Get XXX pc, cpsr, old_r0 add r4, sp, #S_PC stmia r4, {r5 - r7} @ Save XXX pc, cpsr, old_r0 - mov r0, sp @ Prefetch aborts are definitely *not* - mov r1, #BAD_PREFETCH @ allowed in non-user modes. We cant - and r2, r6, #31 @ recover from this problem. + mov r0, sp + and r2, r6, #31 @ int mode b SYMBOL_NAME(bad_mode) -#ifdef DEBUG_UNDEF -t: .ascii "*** undef ***\r\n\0" - .align -#endif -/*============================================================================= - * Data abort handler code - *----------------------------------------------------------------------------- +wfs_mask_data: .word 0x0e200110 @ WFS/RFS + .word 0x0fef0fff + .word 0x0d0d0100 @ LDF [sp]/STF [sp] + .word 0x0d0b0100 @ LDF [fp]/STF [fp] + .word 0x0f0f0f00 + +/* We get here if an undefined instruction happens and the floating + * point emulator is not present. If the offending instruction was + * a WFS, we just perform a normal return as if we had emulated the + * operation. This is a hack to allow some basic userland binaries + * to run so that the emulator module proper can be loaded. --philb */ -.LCprocfns: .word SYMBOL_NAME(processor) +fpe_not_present: + adr r10, wfs_mask_data + ldmia r10, {r4, r5, r6, r7, r8} + ldr r10, [sp, #S_PC] @ Load PC + sub r10, r10, #-4 + mask_pc r10, r10 + ldrt r10, [r10] @ get instruction + and r5, r10, r5 + teq r5, r4 @ Is it WFS? + moveq pc, r9 + and r5, r10, r8 + teq r5, r6 @ Is it LDF/STF on sp or fp? + teqne r5, r7 + movne pc, lr + tst r10, #0x00200000 @ Does it have WB + moveq pc, r9 + and r4, r10, #255 @ get offset + and r6, r10, #0x000f0000 + tst r10, #0x00800000 @ +/- + ldr r5, [sp, r6, lsr #14] @ Load reg + rsbeq r4, r4, #0 + add r5, r5, r4, lsl #2 + str r5, [sp, r6, lsr #14] @ Save reg + mov pc, r9 -__dabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go - stmia sp, {r0 - r12} @ save r0 - r12 - add r3, sp, #S_PC - stmdb r3, {sp, lr}^ - ldr r0, .LCabt - ldmia r0, {r0 - r2} @ Get USR pc, cpsr - stmia r3, {r0 - r2} @ Save USR pc, cpsr, old_r0 - mov fp, #0 - mrs r2, cpsr @ Enable interrupts if they were - bic r2, r2, #I_BIT @ previously - msr cpsr, r2 - ldr r2, .LCprocfns - mov lr, pc - ldr pc, [r2, #8] @ call processor specific code - mov r3, sp - bl SYMBOL_NAME(do_DataAbort) - b ret_from_sys_call - -__dabt_svc: sub sp, sp, #S_FRAME_SIZE +/* + * SVC mode handlers + */ + .align 5 +__dabt_svc: sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ save r0 - r12 ldr r2, .LCabt add r0, sp, #S_FRAME_SIZE + ldmia r2, {r2 - r4} @ get pc, cpsr add r5, sp, #S_SP mov r1, lr - ldmia r2, {r2 - r4} @ get pc, cpsr stmia r5, {r0 - r4} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro tst r3, #I_BIT mrseq r0, cpsr @ Enable interrupts if they were @@ -619,29 +458,15 @@ msr spsr, r0 ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr -__dabt_invalid: sub sp, sp, #S_FRAME_SIZE - stmia sp, {r0 - lr} @ Save SVC r0 - lr [lr *should* be intact] - mov r7, r0 - ldr r4, .LCabt - ldmia r4, {r5, r6} @ Get SVC pc, cpsr - add r4, sp, #S_PC - stmia r4, {r5, r6, r7} @ Save SVC pc, cpsr, old_r0 - mov r0, sp - mov r1, #BAD_DATA - and r2, r6, #31 - b SYMBOL_NAME(bad_mode) - -/*============================================================================= - * Interrupt (IRQ) handler - *----------------------------------------------------------------------------- - */ -__irq_usr: sub sp, sp, #S_FRAME_SIZE + .align 5 +__irq_svc: sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ save r0 - r12 - add r8, sp, #S_PC - stmdb r8, {sp, lr}^ - ldr r4, .LCirq - ldmia r4, {r5 - r7} @ get saved PC, SPSR - stmia r8, {r5 - r7} @ save pc, psr, old_r0 + ldr r7, .LCirq + add r5, sp, #S_FRAME_SIZE + ldmia r7, {r7 - r9} + add r4, sp, #S_SP + mov r6, lr + stmia r4, {r5, r6, r7, r8, r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro 1: get_irqnr_and_base r0, r6, r5 movne r1, sp @ @@ -649,148 +474,414 @@ @ adrsvc ne, lr, 1b bne do_IRQ - b ret_with_reschedule - - irq_prio_table + ldr r0, [sp, #S_PSR] + msr spsr, r0 + ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr -__irq_svc: sub sp, sp, #S_FRAME_SIZE + .align 5 +__und_svc: sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ save r0 - r12 + ldr r7, .LCund mov r6, lr - ldr r7, .LCirq ldmia r7, {r7 - r9} add r5, sp, #S_FRAME_SIZE add r4, sp, #S_SP - stmia r4, {r5, r6, r7, r8, r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro + stmia r4, {r5 - r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro + + adrsvc al, r9, 1f @ r9 = normal FP return + bl call_fpe @ lr = undefined instr return + + mov r0, r5 @ unsigned long pc + mov r1, sp @ struct pt_regs *regs + bl SYMBOL_NAME(do_undefinstr) + +1: ldr lr, [sp, #S_PSR] @ Get SVC cpsr + msr spsr, lr + ldmia sp, {r0 - pc}^ @ Restore SVC registers + + .align 5 +.LCirq: .word __temp_irq +.LCund: .word __temp_und +.LCabt: .word __temp_abt +.LCprocfns: .word SYMBOL_NAME(processor) +.LCfp: .word SYMBOL_NAME(fp_enter) +#ifdef CONFIG_ALIGNMENT_TRAP +.LCswi: .word SYMBOL_NAME(cr_alignment) +#endif + + irq_prio_table + +/* + * User mode handlers + */ +#ifdef DEBUG_UNDEF +t: .ascii "Prefetch -> undefined instruction\n\0" + .align +#endif + .align 5 +__dabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go + stmia sp, {r0 - r12} @ save r0 - r12 + ldr r4, .LCabt + add r3, sp, #S_PC + ldmia r4, {r0 - r2} @ Get USR pc, cpsr + stmia r3, {r0 - r2} @ Save USR pc, cpsr, old_r0 + stmdb r3, {sp, lr}^ + +#ifdef CONFIG_ALIGNMENT_TRAP + ldr r7, [r4, #OFF_CR_ALIGNMENT(__temp_abt)] + mcr p15, 0, r7, c1, c0 +#endif + + mov fp, #0 + mrs r2, cpsr @ Enable interrupts if they were + bic r2, r2, #I_BIT @ previously + msr cpsr, r2 + ldr r2, .LCprocfns + mov lr, pc + ldr pc, [r2, #8] @ call processor specific code + mov r3, sp + adrsvc al, lr, ret_from_sys_call + b SYMBOL_NAME(do_DataAbort) + + .align 5 +__irq_usr: sub sp, sp, #S_FRAME_SIZE + stmia sp, {r0 - r12} @ save r0 - r12 + ldr r4, .LCirq + add r8, sp, #S_PC + ldmia r4, {r5 - r7} @ get saved PC, SPSR + stmia r8, {r5 - r7} @ save pc, psr, old_r0 + stmdb r8, {sp, lr}^ + +#ifdef CONFIG_ALIGNMENT_TRAP + ldr r7, [r4, #OFF_CR_ALIGNMENT(__temp_irq)] + mcr p15, 0, r7, c1, c0 +#endif + + mov fp, #0 1: get_irqnr_and_base r0, r6, r5 movne r1, sp + adrsvc ne, lr, 1b @ @ routine called with r0 = irq number, r1 = struct pt_regs * @ - adrsvc ne, lr, 1b bne do_IRQ - ldr r0, [sp, #S_PSR] - msr spsr, r0 - ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr - -__irq_invalid: sub sp, sp, #S_FRAME_SIZE @ Allocate space on stack for frame - stmfd sp, {r0 - lr} @ Save r0 - lr - mov r7, #-1 - ldr r4, .LCirq - ldmia r4, {r5, r6} @ get saved pc, psr - add r4, sp, #S_PC - stmia r4, {r5, r6, r7} - mov fp, #0 - mov r0, sp - mov r1, #BAD_IRQ - b SYMBOL_NAME(bad_mode) - -/*============================================================================= - * Undefined instruction handler - *----------------------------------------------------------------------------- - * Handles floating point instructions - */ -.LC2: .word SYMBOL_NAME(fp_enter) + mov r4, #0 + b ret_with_reschedule + .align 5 __und_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go stmia sp, {r0 - r12} @ Save r0 - r12 - add r8, sp, #S_PC - stmdb r8, {sp, lr}^ @ Save user r0 - r12 ldr r4, .LCund + add r8, sp, #S_PC ldmia r4, {r5 - r7} stmia r8, {r5 - r7} @ Save USR pc, cpsr, old_r0 - mov fp, #0 + stmdb r8, {sp, lr}^ @ Save user r0 - r12 + +#ifdef CONFIG_ALIGNMENT_TRAP + ldr r7, [r4, #OFF_CR_ALIGNMENT(__temp_und)] + mcr p15, 0, r7, c1, c0 +#endif - adrsvc al, r9, ret_from_exception @ r9 = normal FP return + mov fp, #0 + adrsvc al, r9, ret_from_sys_call @ r9 = normal FP return adrsvc al, lr, fpundefinstr @ lr = undefined instr return -1: get_current_task r10 +call_fpe: get_current_task r10 mov r8, #1 strb r8, [r10, #TSK_USED_MATH] @ set current->used_math + ldr r4, .LCfp add r10, r10, #TSS_FPESAVE @ r10 = workspace - ldr r4, .LC2 ldr pc, [r4] @ Call FP module USR entry point -__und_svc: sub sp, sp, #S_FRAME_SIZE - stmia sp, {r0 - r12} @ save r0 - r12 - mov r6, lr - ldr r7, .LCund - ldmia r7, {r7 - r9} - add r5, sp, #S_FRAME_SIZE - add r4, sp, #S_SP - stmia r4, {r5 - r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro - - adrsvc al, r9, 3f @ r9 = normal FP return - bl 1b @ lr = undefined instr return - - mov r0, r5 @ unsigned long pc - mov r1, sp @ struct pt_regs *regs - bl SYMBOL_NAME(do_undefinstr) - -3: ldr lr, [sp, #S_PSR] @ Get SVC cpsr - msr spsr, lr - ldmia sp, {r0 - pc}^ @ Restore SVC registers - fpundefinstr: mov r0, lr mov r1, sp mrs r4, cpsr @ Enable interrupts bic r4, r4, #I_BIT msr cpsr, r4 - adrsvc al, lr, ret_from_exception + adrsvc al, lr, ret_from_sys_call b SYMBOL_NAME(do_undefinstr) -__und_invalid: sub sp, sp, #S_FRAME_SIZE - stmia sp, {r0 - lr} - mov r7, r0 - ldr r4, .LCund - ldmia r4, {r5, r6} @ Get UND/IRQ/FIQ/ABT pc, cpsr - add r4, sp, #S_PC - stmia r4, {r5, r6, r7} @ Save UND/IRQ/FIQ/ABT pc, cpsr, old_r0 - mov r0, sp @ struct pt_regs *regs - mov r1, #BAD_UNDEFINSTR @ int reason - and r2, r6, #31 @ int mode - b SYMBOL_NAME(bad_mode) @ Does not ever return... + .align 5 +__pabt_usr: sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go + stmia sp, {r0 - r12} @ Save r0 - r12 + ldr r4, .LCabt + add r8, sp, #S_PC + ldmia r4, {r5 - r7} @ Get USR pc, cpsr + stmia r8, {r5 - r7} @ Save USR pc, cpsr, old_r0 + stmdb r8, {sp, lr}^ @ Save sp_usr lr_usr -/* We get here if an undefined instruction happens and the floating - * point emulator is not present. If the offending instruction was - * a WFS, we just perform a normal return as if we had emulated the - * operation. This is a hack to allow some basic userland binaries - * to run so that the emulator module proper can be loaded. --philb - */ -fpe_not_present: - adr r10, wfs_mask_data - ldmia r10, {r4, r5, r6, r7, r8} - ldr r10, [sp, #S_PC] @ Load PC - sub r10, r10, #4 - mask_pc r10, r10 - ldrt r10, [r10] @ get instruction - and r5, r10, r5 - teq r5, r4 @ Is it WFS? - moveq pc, r9 - and r5, r10, r8 - teq r5, r6 @ Is it LDF/STF on sp or fp? - teqne r5, r7 - movne pc, lr - tst r10, #0x00200000 @ Does it have WB - moveq pc, r9 - and r4, r10, #255 @ get offset - and r6, r10, #0x000f0000 - tst r10, #0x00800000 @ +/- - rsbeq r4, r4, #0 - ldr r5, [sp, r6, lsr #14] @ Load reg - add r5, r5, r4, lsl #2 - str r5, [sp, r6, lsr #14] @ Save reg - mov pc, r9 +#ifdef CONFIG_ALIGNMENT_TRAP + ldr r7, [r4, #OFF_CR_ALIGNMENT(__temp_abt)] + mcr p15, 0, r7, c1, c0 +#endif -wfs_mask_data: .word 0x0e200110 @ WFS - .word 0x0fff0fff - .word 0x0d0d0100 @ LDF [sp]/STF [sp] - .word 0x0d0b0100 @ LDF [fp]/STF [fp] - .word 0x0f0f0f00 + mov fp, #0 + mrs r7, cpsr @ Enable interrupts if they were + bic r7, r7, #I_BIT @ previously + msr cpsr, r7 + mov r0, r5 @ address (pc) + mov r1, sp @ regs + bl SYMBOL_NAME(do_PrefetchAbort) @ call abort handler + teq r0, #0 @ Does this still apply??? + bne ret_from_sys_call @ Return from exception +#ifdef DEBUG_UNDEF + adr r0, t + bl SYMBOL_NAME(printk) +#endif + mov r0, r5 + mov r1, sp + and r2, r6, #31 + bl SYMBOL_NAME(do_undefinstr) + ldr lr, [sp, #S_PSR] @ Get USR cpsr + msr spsr, lr + ldmia sp, {r0 - pc}^ @ Restore USR registers #include "entry-common.S" + .text + +#ifndef __ARM_ARCH_4__ +.Larm700bug: ldr r0, [sp, #S_PSR] @ Get calling cpsr + str lr, [r8] + msr spsr, r0 + ldmia sp, {r0 - lr}^ @ Get calling r0 - lr + mov r0, r0 + ldr lr, [sp, #S_PC] @ Get PC + add sp, sp, #S_FRAME_SIZE + movs pc, lr +#endif + + .section ".text.init",#alloc,#execinstr +/* + * Vector stubs. NOTE that we only align 'vector_IRQ' to a cache line boundary, + * and we rely on each stub being exactly 48 (1.5 cache lines) in size. This + * means that we only ever load two cache lines for this code, or one if we're + * lucky. We also copy this code to 0x200 so that we can use branches in the + * vectors, rather than ldr's. + */ + .align 5 +__stubs_start: +/* + * Interrupt dispatcher + * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC + */ +vector_IRQ: @ + @ save mode specific registers + @ + ldr r13, .LCsirq + sub lr, lr, #4 + str lr, [r13] @ save lr_IRQ + mrs lr, spsr + str lr, [r13, #4] @ save spsr_IRQ + @ + @ now branch to the relevent MODE handling routine + @ + bic r13, lr, #63 + orr r13, r13, #0x93 + msr spsr, r13 @ switch to SVC_32 mode + + and lr, lr, #15 + adr r13, .LCtab_irq + ldr lr, [r13, lr, lsl #2] + movs pc, lr @ Changes mode and branches +/* + * Data abort dispatcher - dispatches it to the correct handler for the processor mode + * Enter in ABT mode, spsr = USR CPSR, lr = USR PC + */ +vector_data: @ + @ save mode specific registers + @ + ldr r13, .LCsabt + sub lr, lr, #8 + str lr, [r13] + mrs lr, spsr + str lr, [r13, #4] + @ + @ now branch to the relevent MODE handling routine + @ + bic r13, lr, #63 + orr r13, r13, #0x93 + msr spsr, r13 @ switch to SVC_32 mode + + and lr, lr, #15 + adr r13, .LCtab_dabt + ldr lr, [r13, lr, lsl #2] + movs pc, lr @ Changes mode and branches + +/* + * Prefetch abort dispatcher - dispatches it to the correct handler for the processor mode + * Enter in ABT mode, spsr = USR CPSR, lr = USR PC + */ +vector_prefetch: + @ + @ save mode specific registers + @ + ldr r13, .LCsabt + sub lr, lr, #4 + str lr, [r13] @ save lr_ABT + mrs lr, spsr + str lr, [r13, #4] @ save spsr_ABT + @ + @ now branch to the relevent MODE handling routine + @ + bic r13, lr, #63 + orr r13, r13, #0x93 + msr spsr, r13 @ switch to SVC_32 mode + + ands lr, lr, #15 + ldreq lr, .LCtab_pabt + ldrne lr, .LCtab_pabt + 4 + movs pc, lr + +/* + * Undef instr entry dispatcher - dispatches it to the correct handler for the processor mode + * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC + */ +vector_undefinstr: + @ + @ save mode specific registers + @ + ldr r13, .LCsund + str lr, [r13] @ save lr_UND + mrs lr, spsr + str lr, [r13, #4] @ save spsr_UND + @ + @ now branch to the relevent MODE handling routine + @ + bic r13, lr, #63 + orr r13, r13, #0x93 + msr spsr, r13 @ switch to SVC_32 mode + + and lr, lr, #15 + adr r13, .LCtab_und + ldr lr, [r13, lr, lsl #2] + movs pc, lr @ Changes mode and branches + +/*============================================================================= + * Undefined FIQs + *----------------------------------------------------------------------------- + * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC + * MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg. + * Basically to switch modes, we *HAVE* to clobber one register... brain + * damage alert! I don't think that we can execute any code in here in any + * other mode than FIQ... Ok you can switch to another mode, but you can't + * get out of that mode without clobbering one register. + */ +vector_FIQ: disable_fiq + subs pc, lr, #4 + +/*============================================================================= + * Address exception handler + *----------------------------------------------------------------------------- + * These aren't too critical. + * (they're not supposed to happen, and won't happen in 32-bit data mode). + */ + +vector_addrexcptn: + b vector_addrexcptn + +/* + * We group all the following data together to optimise + * for CPUs with separate I & D caches. + */ + .align 5 + +.LCtab_irq: .word __irq_usr @ 0 (USR_26 / USR_32) + .word __irq_invalid @ 1 (FIQ_26 / FIQ_32) + .word __irq_invalid @ 2 (IRQ_26 / IRQ_32) + .word __irq_svc @ 3 (SVC_26 / SVC_32) + .word __irq_invalid @ 4 + .word __irq_invalid @ 5 + .word __irq_invalid @ 6 + .word __irq_invalid @ 7 + .word __irq_invalid @ 8 + .word __irq_invalid @ 9 + .word __irq_invalid @ a + .word __irq_invalid @ b + .word __irq_invalid @ c + .word __irq_invalid @ d + .word __irq_invalid @ e + .word __irq_invalid @ f + +.LCtab_und: .word __und_usr @ 0 (USR_26 / USR_32) + .word __und_invalid @ 1 (FIQ_26 / FIQ_32) + .word __und_invalid @ 2 (IRQ_26 / IRQ_32) + .word __und_svc @ 3 (SVC_26 / SVC_32) + .word __und_invalid @ 4 + .word __und_invalid @ 5 + .word __und_invalid @ 6 + .word __und_invalid @ 7 + .word __und_invalid @ 8 + .word __und_invalid @ 9 + .word __und_invalid @ a + .word __und_invalid @ b + .word __und_invalid @ c + .word __und_invalid @ d + .word __und_invalid @ e + .word __und_invalid @ f + +.LCtab_dabt: .word __dabt_usr @ 0 (USR_26 / USR_32) + .word __dabt_invalid @ 1 (FIQ_26 / FIQ_32) + .word __dabt_invalid @ 2 (IRQ_26 / IRQ_32) + .word __dabt_svc @ 3 (SVC_26 / SVC_32) + .word __dabt_invalid @ 4 + .word __dabt_invalid @ 5 + .word __dabt_invalid @ 6 + .word __dabt_invalid @ 7 + .word __dabt_invalid @ 8 + .word __dabt_invalid @ 9 + .word __dabt_invalid @ a + .word __dabt_invalid @ b + .word __dabt_invalid @ c + .word __dabt_invalid @ d + .word __dabt_invalid @ e + .word __dabt_invalid @ f + +.LCtab_pabt: .word __pabt_usr + .word __pabt_invalid + +.LCvswi: .word vector_swi + +.LCsirq: .word __temp_irq +.LCsund: .word __temp_und +.LCsabt: .word __temp_abt + +__stubs_end: + + .equ __real_stubs_start, .LCvectors + 0x200 + +.LCvectors: swi SYS_ERROR0 + b __real_stubs_start + (vector_undefinstr - __stubs_start) + ldr pc, __real_stubs_start + (.LCvswi - __stubs_start) + b __real_stubs_start + (vector_prefetch - __stubs_start) + b __real_stubs_start + (vector_data - __stubs_start) + b __real_stubs_start + (vector_addrexcptn - __stubs_start) + b __real_stubs_start + (vector_IRQ - __stubs_start) + b __real_stubs_start + (vector_FIQ - __stubs_start) + +ENTRY(trap_init) + stmfd sp!, {r4 - r6, lr} + + adr r1, .LCvectors @ set up the vectors + mov r0, #0 + ldmia r1, {r1, r2, r3, r4, r5, r6, ip, lr} + stmia r0, {r1, r2, r3, r4, r5, r6, ip, lr} + + add r2, r0, #0x200 + adr r0, __stubs_start @ copy stubs to 0x200 + adr r1, __stubs_end +1: ldr r3, [r0], #4 + str r3, [r2], #4 + cmp r0, r1 + blt 1b + LOADREGS(fd, sp!, {r4 - r6, pc}) + .data +/* + * Do not reorder these, and do not insert extra data between... + */ + __temp_irq: .word 0 @ saved lr_irq .word 0 @ saved spsr_irq .word -1 @ old_r0 @@ -800,3 +891,10 @@ __temp_abt: .word 0 @ Saved lr_abt .word 0 @ Saved spsr_abt .word -1 @ old_r0 + + .globl SYMBOL_NAME(cr_alignment) + .globl SYMBOL_NAME(cr_no_alignment) +SYMBOL_NAME(cr_alignment): + .space 4 +SYMBOL_NAME(cr_no_alignment): + .space 4 diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/entry-common.S linux/arch/arm/kernel/entry-common.S --- v2.3.6/linux/arch/arm/kernel/entry-common.S Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/entry-common.S Thu Jun 17 01:11:35 1999 @@ -1,51 +1,54 @@ /*============================================================================ * All exits to user mode from the kernel go through this code. */ - -#include - .globl ret_from_sys_call -ret_from_exception: - adr r0, 1f - ldmia r0, {r0, r1} + .align 5 +fast_syscall_return: + str r0, [sp, #S_R0 + 4] @ returned r0 +slow_syscall_return: + add sp, sp, #4 +ret_from_sys_call: + adr r0, bh_data + ldmia r0, {r0, r4} ldr r0, [r0] - ldr r1, [r1] + ldr r1, [r4] tst r0, r1 blne SYMBOL_NAME(do_bottom_half) -ret_from_intr: ldr r0, [sp, #S_PSR] - tst r0, #3 - beq ret_with_reschedule - b ret_from_all +ret_with_reschedule: + get_current_task r1 @ check for scheduling + ldr r0, [r1, #TSK_NEED_RESCHED] + teq r0, #0 + bne ret_reschedule + ldr r1, [r1, #TSK_SIGPENDING] + teq r1, #0 @ check for signals + bne ret_signal + +ret_from_all: restore_user_regs ret_signal: mov r1, sp adrsvc al, lr, ret_from_all + mov r2, r4 b SYMBOL_NAME(do_signal) -2: bl SYMBOL_NAME(schedule) +ret_reschedule: adrsvc al, lr, ret_with_reschedule + b SYMBOL_NAME(schedule) -ret_from_sys_call: - adr r0, 1f + .globl ret_from_exception +ret_from_exception: + adr r0, bh_data ldmia r0, {r0, r1} ldr r0, [r0] ldr r1, [r1] + mov r4, #0 tst r0, r1 - adrsvc ne, lr, ret_from_intr - bne SYMBOL_NAME(do_bottom_half) - -ret_with_reschedule: - get_current_task r1 - ldr r0, [r1, #TSK_NEED_RESCHED] - teq r0, #0 - bne 2b - ldr r1, [r1, #TSK_SIGPENDING] - teq r1, #0 - bne ret_signal - -ret_from_all: restore_user_regs + blne SYMBOL_NAME(do_bottom_half) + ldr r0, [sp, #S_PSR] + tst r0, #3 @ returning to user mode? + beq ret_with_reschedule + b ret_from_all -1: .word SYMBOL_NAME(bh_mask) - .word SYMBOL_NAME(bh_active) +#include "calls.S" /*============================================================================= * SWI handler @@ -57,84 +60,65 @@ * too worried. */ -#include "calls.S" - + .align 5 vector_swi: save_user_regs - mov fp, #0 mask_pc lr, lr - ldr r6, [lr, #-4]! @ get SWI instruction + mov fp, #0 + ldr r6, [lr, #-4] @ get SWI instruction arm700_bug_check r6, r7 +#ifdef CONFIG_ALIGNMENT_TRAP + ldr r7, .LCswi + ldr r7, [r7] + mcr p15, 0, r7, c1, c0 +#endif enable_irqs r7 - + + str r4, [sp, #-4]! @ new style: (r0 = arg1, r4 = arg5) + adrsvc al, lr, fast_syscall_return + bic r6, r6, #0xff000000 @ mask off SWI op-code eor r6, r6, #OS_NUMBER<<20 @ check OS number cmp r6, #NR_syscalls @ check upper syscall limit bcs 2f - get_current_task r5 - ldr ip, [r5, #TSK_FLAGS] @ check for syscall tracing + get_current_task r7 + ldr ip, [r7, #TSK_FLAGS] @ check for syscall tracing + adr r5, SYMBOL_NAME(sys_call_table) tst ip, #PF_TRACESYS - bne 1f + ldreq pc, [r5, r6, lsl #2] @ call sys routine - adr ip, SYMBOL_NAME(sys_call_table) - str r4, [sp, #-4]! @ new style: (r0 = arg1, r5 = arg5) - mov lr, pc - ldr pc, [ip, r6, lsl #2] @ call sys routine - add sp, sp, #4 - str r0, [sp, #S_R0] @ returned r0 - b ret_from_sys_call - -1: ldr r7, [sp, #S_IP] @ save old IP + ldr r7, [sp, #S_IP + 4] @ save old IP mov r0, #0 - str r0, [sp, #S_IP] @ trace entry [IP = 0] + str r0, [sp, #S_IP + 4] @ trace entry [IP = 0] bl SYMBOL_NAME(syscall_trace) - str r7, [sp, #S_IP] - ldmia sp, {r0 - r3} @ have to reload r0 - r3 - adr ip, SYMBOL_NAME(sys_call_table) - str r4, [sp, #-4]! @ new style: (r0 = arg1, r5 = arg5) + str r7, [sp, #S_IP + 4] + + ldmib sp, {r0 - r3} @ have to reload r0 - r3 mov lr, pc - ldr pc, [ip, r6, lsl #2] @ call sys routine - add sp, sp, #4 - str r0, [sp, #S_R0] @ returned r0 + ldr pc, [r5, r6, lsl #2] @ call sys routine + str r0, [sp, #S_R0 + 4] @ returned r0 + mov r0, #1 - str r0, [sp, #S_IP] @ trace exit [IP = 1] + str r0, [sp, #S_IP + 4] @ trace exit [IP = 1] bl SYMBOL_NAME(syscall_trace) - str r7, [sp, #S_IP] - b ret_from_sys_call + str r7, [sp, #S_IP + 4] + b slow_syscall_return -2: tst r6, #0x00f00000 @ is it a Unix SWI? +2: add r1, sp, #4 + tst r6, #0x00f00000 @ is it a Unix SWI? bne 3f - cmp r6, #(KSWI_SYS_BASE - KSWI_BASE) - bcc 4f @ not private func - bic r0, r6, #0x000f0000 - mov r1, sp - bl SYMBOL_NAME(arm_syscall) - b ret_from_sys_call - -3: eor r0, r6, #OS_NUMBER<<20 @ Put OS number back - mov r1, sp - bl SYMBOL_NAME(deferred) - ldmfd sp, {r0 - r3} - b ret_from_sys_call - -4: bl SYMBOL_NAME(sys_ni_syscall) - str r0, [sp, #0] @ returned r0 - b ret_from_sys_call + subs r0, r6, #(KSWI_SYS_BASE - KSWI_BASE) + bcs SYMBOL_NAME(arm_syscall) + b SYMBOL_NAME(sys_ni_syscall) @ not private func -@ r0 = syscall number -@ r1 = syscall r0 -@ r5 = syscall r4 -@ ip = syscall table -SYMBOL_NAME(sys_syscall): - mov r6, r0 - eor r6, r6, #OS_NUMBER << 20 - cmp r6, #NR_syscalls @ check range - movgt r0, #-ENOSYS - movgt pc, lr - add sp, sp, #4 @ take of the save of our r4 - ldmib sp, {r0 - r4} @ get our args - str r4, [sp, #-4]! @ Put our arg on the stack - ldr pc, [ip, r6, lsl #2] +3: eor r0, r6, #OS_NUMBER <<20 @ Put OS number back + adrsvc al, lr, slow_syscall_return + b SYMBOL_NAME(deferred) + + .align 5 + +bh_data: .word SYMBOL_NAME(bh_mask) + .word SYMBOL_NAME(bh_active) ENTRY(sys_call_table) #include "calls.S" @@ -142,10 +126,25 @@ /*============================================================================ * Special system call wrappers */ +@ r0 = syscall number +@ r5 = syscall table +SYMBOL_NAME(sys_syscall): + eor r6, r0, #OS_NUMBER << 20 + cmp r6, #NR_syscalls @ check range + ldmleib sp, {r0 - r4} @ get our args + strle r4, [sp] @ Put our arg on the stack + ldrle pc, [r5, r6, lsl #2] + mov r0, #-ENOSYS + mov pc, lr + sys_fork_wrapper: add r0, sp, #4 b SYMBOL_NAME(sys_fork) +sys_vfork_wrapper: + add r0, sp, #4 + b SYMBOL_NAME(sys_vfork) + sys_execve_wrapper: add r3, sp, #4 b SYMBOL_NAME(sys_execve) @@ -191,99 +190,6 @@ sys_sigaltstack_wrapper: ldr r2, [sp, #4 + S_SP] b do_sigaltstack - -/* - *============================================================================= - * Low-level interface code - *----------------------------------------------------------------------------- - * Trap initialisation - *----------------------------------------------------------------------------- - * - * Note - FIQ code has changed. The default is a couple of words in 0x1c, 0x20 - * that call _unexp_fiq. Nowever, we now copy the FIQ routine to 0x1c (removes - * some excess cycles). - * - * What we need to put into 0-0x1c are ldrs to branch to 0xC0000000 - * (the kernel). - * 0x1c onwards is reserved for FIQ, so I think that I will allocate 0xe0 onwards for - * the actual address to jump to. - */ - - .section ".text.init",#alloc,#execinstr - -#if defined(CONFIG_CPU_32) -/* - * these go into 0x00 - */ -.Lbranches: swi SYS_ERROR0 - ldr pc, .Lbranches + 0xe4 - ldr pc, .Lbranches + 0xe8 - ldr pc, .Lbranches + 0xec - ldr pc, .Lbranches + 0xf0 - ldr pc, .Lbranches + 0xf4 - ldr pc, .Lbranches + 0xf8 - ldr pc, .Lbranches + 0xfc -/* - * this is put into 0xe4 and above - */ -.Ljump_addresses: - .word vector_undefinstr @ 0xe4 - .word vector_swi @ 0xe8 - .word vector_prefetch @ 0xec - .word vector_data @ 0xf0 - .word vector_addrexcptn @ 0xf4 - .word vector_IRQ @ 0xf8 - .word _unexp_fiq @ 0xfc -/* - * initialise the trap system - */ -ENTRY(trap_init) - stmfd sp!, {r4 - r7, lr} - initialise_traps_extra - mov r0, #0xe4 - adr r1, .Ljump_addresses - ldmia r1, {r1 - r7} - stmia r0, {r1 - r7} - mov r0, #0 - adr r1, .Lbranches - ldmia r1, {r1 - r7} - stmia r0, {r1 - r7} - LOADREGS(fd, sp!, {r4 - r7, pc}) -#elif defined(CONFIG_CPU_26) -.Ljump_addresses: - swi SYS_ERROR0 - .word vector_undefinstr - 12 - .word vector_swi - 16 - .word vector_prefetch - 20 - .word vector_data - 24 - .word vector_addrexcptn - 28 - .word vector_IRQ - 32 - .word _unexp_fiq - 36 - b . + 8 -/* - * initialise the trap system - */ -ENTRY(trap_init) - stmfd sp!, {r4 - r7, lr} - adr r1, .Ljump_addresses - ldmia r1, {r1 - r7, ip, lr} - orr r2, lr, r2, lsr #2 - orr r3, lr, r3, lsr #2 - orr r4, lr, r4, lsr #2 - orr r5, lr, r5, lsr #2 - orr r6, lr, r6, lsr #2 - orr r7, lr, r7, lsr #2 - orr ip, lr, ip, lsr #2 - mov r0, #0 - stmia r0, {r1 - r7, ip} - ldmfd sp!, {r4 - r7, pc}^ -#endif - - .previous - -/*============================================================================ - * FP support - */ .data diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/fiq.c linux/arch/arm/kernel/fiq.c --- v2.3.6/linux/arch/arm/kernel/fiq.c Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/fiq.c Thu Jun 17 01:11:35 1999 @@ -2,6 +2,8 @@ * linux/arch/arm/kernel/fiq.c * * Copyright (C) 1998 Russell King + * Copyright (C) 1998, 1999 Phil Blundell + * * FIQ support written by Philip Blundell , 1998. * * FIQ support re-written by Russell King to be more generic @@ -78,7 +80,7 @@ unprotect_page_0(); *(unsigned long *)FIQ_VECTOR = no_fiq_insn; protect_page_0(); - __flush_entry_to_ram(FIQ_VECTOR); + flush_icache_range(FIQ_VECTOR, FIQ_VECTOR + 4); } return 0; @@ -106,28 +108,77 @@ memcpy((void *)FIQ_VECTOR, start, length); protect_page_0(); -#ifdef CONFIG_CPU_32 - processor.u.armv3v4._flush_cache_area(FIQ_VECTOR, FIQ_VECTOR + length, 1); -#endif + flush_icache_range(FIQ_VECTOR, FIQ_VECTOR + length); } +/* + * Taking an interrupt in FIQ mode is death, so both these functions + * disable irqs for the duration. + */ void set_fiq_regs(struct pt_regs *regs) { - /* not yet - - * this is temporary to get the floppy working - * again on RiscPC. It *will* become more - * generic. - */ -#ifdef CONFIG_ARCH_ACORN - extern void floppy_fiqsetup(unsigned long len, unsigned long addr, - unsigned long port); - floppy_fiqsetup(regs->ARM_r9, regs->ARM_r10, regs->ARM_fp); + register unsigned long tmp, tmp2; + __asm__ volatile ( +#ifdef CONFIG_CPU_26 + "mov %0, pc + bic %1, %0, #0x3 + orr %1, %1, #0x0c000001 + teqp %1, #0 @ select FIQ mode + mov r0, r0 + ldmia %2, {r8 - r14} + teqp %0, #0 @ return to SVC mode + mov r0, r0" #endif +#ifdef CONFIG_CPU_32 + "mrs %0, cpsr + bic %1, %0, #0xf + orr %1, %1, #0xc1 + msr cpsr, %1 @ select FIQ mode + mov r0, r0 + ldmia %2, {r8 - r14} + msr cpsr, %0 @ return to SVC mode + mov r0, r0" +#endif + : "=r" (tmp), "=r" (tmp2) + : "r" (®s->ARM_r8) + /* These registers aren't modified by the above code in a way + visible to the compiler, but we mark them as clobbers anyway + so that GCC won't put any of the input or output operands in + them. */ + : "r8", "r9", "r10", "r11", "r12", "r13", "r14"); } void get_fiq_regs(struct pt_regs *regs) { - /* not yet */ + register unsigned long tmp, tmp2; + __asm__ volatile ( +#ifdef CONFIG_CPU_26 + "mov %0, pc + bic %1, %0, #0x3 + orr %1, %1, #0x0c000001 + teqp %1, #0 @ select FIQ mode + mov r0, r0 + stmia %2, {r8 - r14} + teqp %0, #0 @ return to SVC mode + mov r0, r0" +#endif +#ifdef CONFIG_CPU_32 + "mrs %0, cpsr + bic %1, %0, #0xf + orr %1, %1, #0xc1 + msr cpsr, %1 @ select FIQ mode + mov r0, r0 + stmia %2, {r8 - r14} + msr cpsr, %0 @ return to SVC mode + mov r0, r0" +#endif + : "=r" (tmp), "=r" (tmp2) + : "r" (®s->ARM_r8) + /* These registers aren't modified by the above code in a way + visible to the compiler, but we mark them as clobbers anyway + so that GCC won't put any of the input or output operands in + them. */ + : "r8", "r9", "r10", "r11", "r12", "r13", "r14"); } int claim_fiq(struct fiq_handler *f) diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/head-armv.S linux/arch/arm/kernel/head-armv.S --- v2.3.6/linux/arch/arm/kernel/head-armv.S Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/head-armv.S Thu Jun 17 01:11:35 1999 @@ -7,13 +7,21 @@ */ #include #include +#include +#include + + .globl SYMBOL_NAME(swapper_pg_dir) + .equ SYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x4000 + + .section ".text.init",#alloc,#execinstr +ENTRY(stext) +ENTRY(_stext) -#ifndef CONFIG_ARCH_VNC #if (TEXTADDR & 0xffff) != 0x8000 #error TEXTADDR must start at 0xXXXX8000 #endif -#else - .text + +#ifdef CONFIG_ARCH_NETWINDER mov r0, r0 mov r0, r0 mov r0, r0 @@ -22,16 +30,34 @@ mov r0, r0 mov r0, r0 mov r0, r0 + + adr r2, 1f + ldmdb r2, {r7, r8} + and r3, r2, #0x0000c000 + teq r3, #0x00008000 + beq __entry + bic r3, r2, #0xc000 + orr r3, r3, #0x8000 + mov r0, r3 + mov r4, #32 + sub r5, r8, r7 + b 1f + + .word _stext + .word _end + +1: ldmia r2!, {r6, r7, r8, r9} + stmia r3!, {r6, r7, r8, r9} + subs r4, r4, #16 + bcs 1b + movs r4, r5 + mov r5, #0 + movne pc, r0 + mov r0, #0 mov r1, #5 #endif -#define DEBUG - - .globl SYMBOL_NAME(swapper_pg_dir) - .equ SYMBOL_NAME(swapper_pg_dir), TEXTADDR - 0x4000 - - .text /* * Entry point and restart point. Entry *must* be called with r0 == 0, * MMU off. Note! These should be unique!!! Please read Documentation/ARM-README @@ -45,16 +71,15 @@ * r1 = 5 -> Corel Netwinder * r1 = 6 -> CATS * r1 = 7 -> tbox + * r1 = 8 -> SA110/21285 as co-processor */ -ENTRY(stext) -ENTRY(_stext) __entry: teq r0, #0 @ check for illegal entry... bne .Lerror @ loop indefinitely - cmp r1, #8 @ Unknown machine architecture + cmp r1, #9 @ Unknown machine architecture bge .Lerror -/* First thing to do is to get the page tables set up so that we can call the kernel - * in the correct place. This is relocatable code... +/* First thing to do is to get the page tables set up so that we can call + * the kernel in the correct place. This is relocatable code... * - Read processor ID register (CP#15, CR0). */ mrc p15, 0, r9, c0, c0 @ get Processor ID @@ -74,7 +99,7 @@ adr r4, .LCMachTypes add r4, r4, r1, lsl #4 - ldmia r4, {r4, r5, r6} + ldmia r4, {r4, r5, r6, r7} /* * r4 = page dir in physical ram * r5 = physical address of start of RAM @@ -99,26 +124,28 @@ add r3, r3, #1 << 20 str r3, [r0], #4 add r3, r3, #1 << 20 -#ifdef DEBUG +#ifdef CONFIG_DEBUG_LL /* Map in IO space * This allows debug messages to be output via a serial * before/while paging_init. */ - add r0, r4, #0x3800 + add r0, r4, r7 orr r3, r6, r8 add r2, r0, #0x0800 1: str r3, [r0], #4 add r3, r3, #1 << 20 teq r0, r2 bne 1b -#ifdef CONFIG_ARCH_VNC - add r0, r4, #0x3f00 - add r0, r0, #0x00f8 +#ifdef CONFIG_ARCH_NETWINDER + teq r1, #5 + bne 1f + add r0, r4, #0x3fc0 mov r3, #0x7c000000 orr r3, r3, r8 str r3, [r0], #4 add r3, r3, #1 << 20 str r3, [r0], #4 +1: #endif #endif #ifdef CONFIG_ARCH_RPC @@ -168,49 +195,55 @@ .LCMachTypes: .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical) .long 0 @ Address of RAM .long 0xe0000000 @ I/O address - .long 0 + .long 0x3800 @ Acorn RiscPC .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x10000000 .long 0x10000000 .long 0x03000000 - .long 0 + .long 0x3800 @ EBSIT ??? .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 .long 0 .long 0xe0000000 - .long 0 + .long 0x3800 @ NexusPCI .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x40000000 .long 0x40000000 .long 0x10000000 - .long 0 + .long 0x3800 @ DEC EBSA285 .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical) .long 0 @ Address of RAM .long 0x24000000 @ I/O base address (0x42000000 -> 0xFE000000) - .long 0 + .long 0x3800 @ Corel VNC .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical) .long 0 @ Address of RAM .long 0x24000000 @ I/O base address (0x42000000 -> 0xfe000000) - .long 0 + .long 0x3800 @ CATS .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 @ Address of page tables (physical) .long 0 @ Address of RAM .long 0x24000000 @ I/O base address (0x42000000 -> 0xfe000000) - .long 0 + .long 0x3800 @ tbox .long SYMBOL_NAME(swapper_pg_dir) - 0xc0000000 + 0x80000000 .long 0x80000000 @ Address of RAM .long 0x00400000 @ Uart - .long 0 + .long 0x3800 + + @ DEC EBSA285 as co-processor + .long 0x4000 @ Address of page tables (physical) + .long 0 @ Address of RAM + .long DC21285_ARMCSR_BASE @ Physical I/O base address + .long 0x7cf00000 >> 18 @ Virtual I/O base address .LCProcTypes: @ ARM6 / 610 .long 0x41560600 @@ -250,7 +283,11 @@ mcr p15, 0, r4, c2, c0 @ load page table pointer mov r0, #0x1f @ Domains 0, 1 = client mcr p15, 0, r0, c3, c0 @ load domain access register +#ifdef CONFIG_ALIGNMENT_TRAP + mov r0, #0x3f @ ....S..DPWCAM +#else mov r0, #0x3d @ ....S..DPWC.M +#endif orr r0, r0, #0x100 mov pc, lr @@ -261,7 +298,11 @@ mcr p15, 0, r4, c2, c0 @ load page table pointer mov r0, #0x1f @ Domains 0, 1 = client mcr p15, 0, r0, c3, c0 @ load domain access register +#ifdef CONFIG_ALIGNMENT_TRAP + mov r0, #0x7f @ ....S.LDPWCAM +#else mov r0, #0x7d @ ....S.LDPWC.M +#endif orr r0, r0, #0x100 mov pc, lr @@ -276,32 +317,38 @@ mrc p15, 0, r0, c1, c0 @ get control register v4 bic r0, r0, #0x0e00 bic r0, r0, #0x0002 +#ifdef CONFIG_ALIGNMENT_TRAP + orr r0, r0, #0x003f @ I...S..DPWCAM +#else orr r0, r0, #0x003d @ I...S..DPWC.M +#endif orr r0, r0, #0x1100 @ v4 supports separate I cache mov pc, lr - .section ".text.init",#alloc,#execinstr - .Lsa_fastclock: mcr p15, 0, r4, c15, c1, 2 @ Enable clock switching mov pc, lr .LC0: .long SYMBOL_NAME(__entry) - .long SYMBOL_NAME(machine_type) + .long SYMBOL_NAME(__machine_arch_type) .long SYMBOL_NAME(__bss_start) .long SYMBOL_NAME(processor_id) .long SYMBOL_NAME(_end) + .long SYMBOL_NAME(cr_alignment) .long SYMBOL_NAME(init_task_union)+8192 .align .Lalready_done_mmap: adr r4, .LC0 - ldmia r4, {r3, r4, r5, r6, r8, sp} @ Setup stack + ldmia r4, {r3, r4, r5, r6, r7, r8, sp} @ Setup stack add r10, r10, r3 @ Add base back in mov fp, #0 -1: cmp r5, r8 @ Clear BSS +1: cmp r5, r7 @ Clear BSS strcc fp, [r5],#4 bcc 1b + bic r2, r0, #2 @ Clear 'A' bit + stmia r8, {r0, r2} @ Save control register values + str r1, [r4] @ Save machine type str r9, [r6] @ Save processor ID mov lr, pc @@ -310,10 +357,12 @@ b SYMBOL_NAME(start_kernel) .text -#ifdef DEBUG + +#ifdef CONFIG_DEBUG_LL /* * Some debugging routines (useful if you've got MM problems and - * printk isn't working). For DEBUGGING ONLY!!! + * printk isn't working). For DEBUGGING ONLY!!! Do not leave + * references to these in a production kernel! */ #if defined(CONFIG_ARCH_RPC) .macro addruart,rx @@ -362,64 +411,71 @@ beq 1001b .endm -#elif defined(CONFIG_ARCH_EBSA285) +#elif defined(CONFIG_HOST_FOOTBRIDGE) || defined(CONFIG_ADDIN_FOOTBRIDGE) +#ifndef CONFIG_DEBUG_DC21285_PORT + /* For NetWinder debugging */ .macro addruart,rx - mov \rx, #0xfe000000 + mov \rx, #0xff000000 + orr \rx, \rx, #0x000003f8 .endm .macro senduart,rd,rx - str \rd, [\rx, #0x160] @ UARTDR + strb \rd, [\rx] .endm .macro busyuart,rd,rx -1001: ldr \rd, [\rx, #0x178] @ UARTFLG - tst \rd, #1 << 3 - bne 1001b +1002: ldrb \rd, [\rx, #0x5] + and \rd, \rd, #0x60 + teq \rd, #0x60 + bne 1002b .endm .macro waituart,rd,rx +1001: ldrb \rd, [\rx, #0x6] + tst \rd, #0x10 + beq 1001b .endm +#else + /* For EBSA285 debugging */ + .equ dc21285_high, ARMCSR_BASE & 0xff000000 + .equ dc21285_low, ARMCSR_BASE & 0x00ffffff -#elif defined(CONFIG_ARCH_NEXUSPCI) .macro addruart,rx - ldr \rx, =0xfff00000 + mov \rx, #dc21285_high + .if dc21285_low + orr \rx, \rx, #dc21285_low + .endif .endm .macro senduart,rd,rx - str \rd, [\rx, #0xc] + str \rd, [\rx, #0x160] @ UARTDR .endm .macro busyuart,rd,rx -1001: ldr \rd, [\rx, #0x4] - tst \rd, #1 << 0 +1001: ldr \rd, [\rx, #0x178] @ UARTFLG + tst \rd, #1 << 3 bne 1001b .endm .macro waituart,rd,rx .endm - -#elif defined(CONFIG_ARCH_VNC) +#endif +#elif defined(CONFIG_ARCH_NEXUSPCI) .macro addruart,rx - mov \rx, #0xff000000 - orr \rx, \rx, #0x00e00000 - orr \rx, \rx, #0x000003f8 + ldr \rx, =0xfff00000 .endm .macro senduart,rd,rx - strb \rd, [\rx] + str \rd, [\rx, #0xc] .endm .macro busyuart,rd,rx -1002: ldrb \rd, [\rx, #0x5] - and \rd, \rd, #0x60 - teq \rd, #0x60 - bne 1002b +1001: ldr \rd, [\rx, #0x4] + tst \rd, #1 << 0 + bne 1001b .endm .macro waituart,rd,rx -1001: ldrb \rd, [\rx, #0x6] - tst \rd, #0x10 - beq 1001b .endm #else #error Unknown architecture @@ -475,8 +531,6 @@ mov r1, r0 mov r0, #0 b 1b - - .ltorg .bss hexbuf: .space 16 diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/hw-ebsa285.c linux/arch/arm/kernel/hw-ebsa285.c --- v2.3.6/linux/arch/arm/kernel/hw-ebsa285.c Wed Dec 23 09:44:40 1998 +++ linux/arch/arm/kernel/hw-ebsa285.c Wed Dec 31 16:00:00 1969 @@ -1,161 +0,0 @@ -/* - * arch/arm/kernel/hw-ebsa286.c - * - * EBSA285 hardware specific functions - * - * Copyright (C) 1998 Russell King, Phil Blundel - */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -extern int setup_arm_irq(int, struct irqaction *); - -extern void pci_set_cmd(struct pci_dev *dev, unsigned short clear, unsigned short set); -extern void pci_set_base_addr(struct pci_dev *dev, int idx, unsigned int addr); -extern void pci_set_irq_line(struct pci_dev *dev, unsigned int irq); - -static int irqmap_ebsa[] __initdata = { 9, 8, 18, 11 }; -static int irqmap_cats[] __initdata = { 18, 8, 9, 11 }; - -__initfunc(static int ebsa_irqval(struct pci_dev *dev)) -{ - unsigned char pin; - - pcibios_read_config_byte(dev->bus->number, - dev->devfn, - PCI_INTERRUPT_PIN, - &pin); - - return irqmap_ebsa[(PCI_SLOT(dev->devfn) + pin) & 3]; -} - -__initfunc(static int cats_irqval(struct pci_dev *dev)) -{ - if (dev->irq >= 128) - return 32 + (dev->irq & 0x1f); - - switch (dev->irq) { - case 1: - case 2: - case 3: - case 4: - return irqmap_cats[dev->irq - 1]; - case 0: - return 0; - } - - printk("PCI: device %02x:%02x has unknown irq line %x\n", - dev->bus->number, dev->devfn, dev->irq); - return 0; -} - -__initfunc(void pcibios_fixup_ebsa285(struct pci_dev *dev)) -{ - char cmd; - - /* sort out the irq mapping for this device */ - switch (machine_type) { - case MACH_TYPE_EBSA285: - dev->irq = ebsa_irqval(dev); - break; - case MACH_TYPE_CATS: - dev->irq = cats_irqval(dev); - break; - } - - /* Turn on bus mastering - boot loader doesn't - * - perhaps it should! - dag - */ - pci_read_config_byte(dev, PCI_COMMAND, &cmd); - pci_write_config_byte(dev, PCI_COMMAND, cmd | PCI_COMMAND_MASTER); -} - -static void irq_pci_err(int irq, void *dev_id, struct pt_regs *regs) -{ - const char *err = "unknown"; - unsigned long cmd = *(unsigned long *)0xfe000004 & 0xffff; - unsigned long ctrl = *(unsigned long *)0xfe00013c & 0xffffde07; - static unsigned long next_warn[7]; - int idx = 6; - - switch(irq) { - case IRQ_PCIPARITY: - *(unsigned long *)0xfe000004 = cmd | 1 << 31; - idx = 0; - err = "parity"; - break; - - case IRQ_PCITARGETABORT: - *(unsigned long *)0xfe000004 = cmd | 1 << 28; - idx = 1; - err = "target abort"; - break; - - case IRQ_PCIMASTERABORT: - *(unsigned long *)0xfe000004 = cmd | 1 << 29; - idx = 2; - err = "master abort"; - break; - - case IRQ_PCIDATAPARITY: - *(unsigned long *)0xfe000004 = cmd | 1 << 24; - idx = 3; - err = "data parity"; - break; - - case IRQ_DISCARDTIMER: - *(unsigned long *)0xfe00013c = ctrl | 1 << 8; - idx = 4; - err = "discard timer"; - break; - - case IRQ_SERR: - *(unsigned long *)0xfe00013c = ctrl | 1 << 3; - idx = 5; - err = "system"; - break; - } - if (time_after_eq(jiffies, next_warn[idx])) { - next_warn[idx] = jiffies + 3 * HZ / 100; - printk(KERN_ERR "PCI %s error detected\n", err); - } -} - -static struct irqaction irq_pci_error = { - irq_pci_err, SA_INTERRUPT, 0, "PCI error", NULL, NULL -}; - -__initfunc(void pcibios_init_ebsa285(void)) -{ - setup_arm_irq(IRQ_PCIPARITY, &irq_pci_error); - setup_arm_irq(IRQ_PCITARGETABORT, &irq_pci_error); - setup_arm_irq(IRQ_PCIMASTERABORT, &irq_pci_error); - setup_arm_irq(IRQ_PCIDATAPARITY, &irq_pci_error); - setup_arm_irq(IRQ_DISCARDTIMER, &irq_pci_error); - setup_arm_irq(IRQ_SERR, &irq_pci_error); - - /* - * Map our SDRAM at a known address in PCI space, just in case - * the firmware had other ideas. Using a nonzero base is slightly - * bizarre but apparently necessary to avoid problems with some - * video cards. - * - * We should really only do this if the central function is enabled. - */ - *(unsigned long *)0xfe000010 = 0; - *(unsigned long *)0xfe000018 = 0xe0000000; - *(unsigned long *)0xfe0000f8 = 0; - *(unsigned long *)0xfe0000fc = 0; - *(unsigned long *)0xfe000100 = 0x01fc0000; - *(unsigned long *)0xfe000104 = 0; - *(unsigned long *)0xfe000108 = 0x80000000; - *(unsigned long *)0xfe000004 = 0x17; -} diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/hw-footbridge.c linux/arch/arm/kernel/hw-footbridge.c --- v2.3.6/linux/arch/arm/kernel/hw-footbridge.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/kernel/hw-footbridge.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,893 @@ +/* + * arch/arm/kernel/hw-footbridge.c + * + * Footbridge-dependent machine fixup + * + * Copyright (C) 1998, 1999 Russell King, Phil Blundell + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define IRDA_IO_BASE 0x180 +#define ETHER10_IO_BASE 0x301 +#define GP1_IO_BASE 0x338 +#define GP2_IO_BASE 0x33a +#define DEC21143_IO_BASE 0x401 +#define DEC21143_MEM_BASE 0x00800000 +#define CYBER2000_MEM_BASE 0x01000000 + +int have_isa_bridge; + +extern int setup_arm_irq(int, struct irqaction *); +extern void pci_set_cmd(struct pci_dev *dev, unsigned short clear, unsigned short set); +extern void pci_set_base_addr(struct pci_dev *dev, int idx, unsigned int addr); +extern void pci_set_irq_line(struct pci_dev *dev, unsigned int irq); +extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); + +#ifdef CONFIG_PCI + +static int irqmap_ebsa[] __initdata = { IRQ_IN1, IRQ_IN0, IRQ_PCI, IRQ_IN3 }; + +__initfunc(static int ebsa_irqval(struct pci_dev *dev)) +{ + unsigned char pin; + + pcibios_read_config_byte(dev->bus->number, + dev->devfn, + PCI_INTERRUPT_PIN, + &pin); + + return irqmap_ebsa[(PCI_SLOT(dev->devfn) + pin) & 3]; +} + +#ifdef CONFIG_CATS +static int irqmap_cats[] __initdata = { IRQ_PCI, IRQ_IN0, IRQ_IN1, IRQ_IN3 }; + +__initfunc(static int cats_irqval(struct pci_dev *dev)) +{ + if (dev->irq >= 128) + return 16 + (dev->irq & 0x1f); + + switch (dev->irq) { + case 1: + case 2: + case 3: + case 4: + return irqmap_cats[dev->irq - 1]; + case 0: + return 0; + } + + printk("PCI: device %02x:%02x has unknown irq line %x\n", + dev->bus->number, dev->devfn, dev->irq); + return 0; +} +#endif + +__initfunc(void pcibios_fixup_ebsa285(struct pci_dev *dev)) +{ + /* Latency timer of 32 */ + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 32); + + /* 32-byte cache line size */ + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 8); + + /* Set SysErr enable, Parity enable */ + pci_set_cmd(dev, 0, PCI_COMMAND_FAST_BACK | PCI_COMMAND_SERR | PCI_COMMAND_PARITY); + + /* If this device is an ISA bridge, set the + * have_isa_bridge flag. We will then go looking + * for things like keyboard, etc + */ + if ((dev->class >> 8) == PCI_CLASS_BRIDGE_ISA || + (dev->class >> 8) == PCI_CLASS_BRIDGE_EISA) + have_isa_bridge = !0; + + /* sort out the irq mapping for this device */ + switch (machine_arch_type) { + case MACH_TYPE_EBSA285: + dev->irq = ebsa_irqval(dev); + /* Turn on bus mastering - boot loader doesn't + * - perhaps it should! - dag + */ + pci_set_cmd(dev, 0, PCI_COMMAND_MASTER); + break; + +#ifdef CONFIG_CATS + case MACH_TYPE_CATS: + dev->irq = cats_irqval(dev); + /* Turn on bus mastering - boot loader doesn't + * - perhaps it should! - dag + */ + pci_set_cmd(dev, 0, PCI_COMMAND_MASTER); + break; +#endif +#ifdef CONFIG_ARCH_NETWINDER + case MACH_TYPE_NETWINDER: + /* disable ROM */ + pci_write_config_dword(dev, PCI_ROM_ADDRESS, 0); + +#define DEV(v,d) ((v)<<16|(d)) + switch (DEV(dev->vendor, dev->device)) { + /* Ether 100 */ + case DEV(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142): + pci_set_base_addr(dev, 0, DEC21143_IO_BASE); + pci_set_base_addr(dev, 1, DEC21143_MEM_BASE); + pci_set_cmd(dev, 0, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO); + /* Put the chip to sleep in case the driver isn't loaded */ + pci_write_config_dword(dev, 0x40, 0x80000000); + dev->irq = IRQ_NETWINDER_ETHER100; + break; + + /* Ether 10 */ + case DEV(PCI_VENDOR_ID_WINBOND2,0x5a5a): + pci_set_base_addr(dev, 0, ETHER10_IO_BASE); + pci_set_cmd(dev, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY, PCI_COMMAND_IO); + dev->irq = IRQ_NETWINDER_ETHER10; + break; + + /* ISA bridge */ + case DEV(PCI_VENDOR_ID_WINBOND,PCI_DEVICE_ID_WINBOND_83C553): + pci_set_base_addr(dev, 0, 0); + pci_set_cmd(dev, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY, PCI_COMMAND_IO); + /* + * Enable all memory requests from ISA to be channeled to PCI + */ + pci_write_config_byte(dev, 0x48, 255); + /* + * Disable ping-pong (as per errata) + */ + pci_write_config_byte(dev, 0x42, 0); + /* + * Enable PCI packet retry + */ + pci_write_config_byte(dev, 0x40, 0x22); + /* + * Do not use PCI CPU park enable, park on + * last master, disable GAT bit + */ + pci_write_config_byte(dev, 0x83, 0x02); + /* + * Default rotating priorities + */ + pci_write_config_byte(dev, 0x80, 0xe0); + /* + * Rotate bank 4 + */ + pci_write_config_byte(dev, 0x81, 0x01); + break; + + /* IDE */ + case DEV(PCI_VENDOR_ID_WINBOND,PCI_DEVICE_ID_WINBOND_82C105): + pci_set_base_addr(dev, 0, 0x1f1); + pci_set_base_addr(dev, 1, 0x3f5); + pci_set_base_addr(dev, 2, 0x171); + pci_set_base_addr(dev, 3, 0x375); + pci_set_base_addr(dev, 4, 0xe801); + pci_set_cmd(dev, PCI_COMMAND_MEMORY, PCI_COMMAND_MASTER | PCI_COMMAND_IO); + dev->irq = IRQ_ISA_HARDDISK1; + break; + + /* VGA */ + case DEV(PCI_VENDOR_ID_INTERG,0x2000): + pci_set_base_addr(dev, 0, CYBER2000_MEM_BASE); + pci_set_cmd(dev, PCI_COMMAND_MASTER, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + dev->irq = IRQ_NETWINDER_VGA; + break; + } +#endif + } +} + +static inline void +report_pci_dev_error(void) +{ + struct pci_dev *dev; + + for (dev = pci_devices; dev; dev = dev->next) { + unsigned short status; + + pci_read_config_word(dev, PCI_STATUS, &status); + if (status & 0xf900) { + printk(KERN_DEBUG "PCI: [%04X:%04X] status = %X\n", + dev->vendor, dev->device, status); + + pci_write_config_word(dev, PCI_STATUS, status & 0xf900); + } + } +} +#else +#define report_pci_dev_error() +#endif + +/* + * Warn on PCI errors. Please report any occurances! + */ +static void +irq_pci_err(int irq, void *dev_id, struct pt_regs *regs) +{ + static unsigned long next_warn; + unsigned long cmd = *CSR_PCICMD & 0x0000ffff; + unsigned long ctrl = (*CSR_SA110_CNTL) & 0xffffde07; + unsigned long irqstatus = *CSR_IRQ_RAWSTATUS; + int warn = time_after_eq(jiffies, next_warn); + + ctrl |= SA110_CNTL_DISCARDTIMER; + + if (warn) { + next_warn = jiffies + 3 * HZ / 100; + printk(KERN_DEBUG "PCI: "); + } + + if (irqstatus & (1 << 31)) { + if (warn) + printk("parity error "); + cmd |= 1 << 31; + } + + if (irqstatus & (1 << 30)) { + if (warn) + printk("target abort "); + cmd |= 1 << 28; + } + + if (irqstatus & (1 << 29)) { + if (warn) + printk("master abort "); + cmd |= 1 << 29; + } + + if (irqstatus & (1 << 28)) { + if (warn) + printk("data parity error "); + cmd |= 1 << 24; + } + + if (irqstatus & (1 << 27)) { + if (warn) + printk("discard timer expired "); + ctrl &= ~SA110_CNTL_DISCARDTIMER; + } + + if (irqstatus & (1 << 23)) { + if (warn) + printk("system error "); + ctrl |= SA110_CNTL_RXSERR; + } + + if (warn) + printk("pc=[<%08lX>]\n", instruction_pointer(regs)); + + report_pci_dev_error(); + + *CSR_PCICMD = cmd; + *CSR_SA110_CNTL = ctrl; +} + +static struct irqaction irq_pci_error = { + irq_pci_err, SA_INTERRUPT, 0, "PCI error", NULL, NULL +}; + +__initfunc(void pcibios_init_ebsa285(void)) +{ + setup_arm_irq(IRQ_PCI_ERR, &irq_pci_error); +} + +/* + * Netwinder stuff + */ +#ifdef CONFIG_ARCH_NETWINDER + +/* + * Winbond WB83977F accessibility stuff + */ +static inline void wb977_open(void) +{ + outb(0x87, 0x370); + outb(0x87, 0x370); +} + +static inline void wb977_close(void) +{ + outb(0xaa, 0x370); +} + +static inline void wb977_wb(int reg, int val) +{ + outb(reg, 0x370); + outb(val, 0x371); +} + +static inline void wb977_ww(int reg, int val) +{ + outb(reg, 0x370); + outb(val >> 8, 0x371); + outb(reg + 1, 0x370); + outb(val, 0x371); +} + +#define wb977_device_select(dev) wb977_wb(0x07, dev) +#define wb977_device_disable() wb977_wb(0x30, 0x00) +#define wb977_device_enable() wb977_wb(0x30, 0x01) + +/* + * This is a lock for accessing ports GP1_IO_BASE and GP2_IO_BASE + */ +spinlock_t __netwinder_data gpio_lock = SPIN_LOCK_UNLOCKED; + +static unsigned int __netwinder_data current_gpio_op = 0; +static unsigned int __netwinder_data current_gpio_io = 0; +static unsigned int __netwinder_data current_cpld = 0; + +void __netwinder_text gpio_modify_op(int mask, int set) +{ + unsigned int new_gpio, changed; + + new_gpio = (current_gpio_op & ~mask) | set; + changed = new_gpio ^ current_gpio_op; + current_gpio_op = new_gpio; + + if (changed & 0xff) + outb(new_gpio, GP1_IO_BASE); + if (changed & 0xff00) + outb(new_gpio >> 8, GP2_IO_BASE); +} + +static inline void __gpio_modify_io(int mask, int in) +{ + unsigned int new_gpio, changed; + int port; + + new_gpio = (current_gpio_io & ~mask) | in; + changed = new_gpio ^ current_gpio_io; + current_gpio_io = new_gpio; + + changed >>= 1; + new_gpio >>= 1; + + wb977_device_select(7); + + for (port = 0xe1; changed && port < 0xe8; changed >>= 1) { + wb977_wb(port, new_gpio & 1); + + port += 1; + new_gpio >>= 1; + } + + wb977_device_select(8); + + for (port = 0xe8; changed && port < 0xec; changed >>= 1) { + wb977_wb(port, new_gpio & 1); + + port += 1; + new_gpio >>= 1; + } +} + +void __netwinder_text gpio_modify_io(int mask, int in) +{ + /* Open up the SuperIO chip */ + wb977_open(); + + __gpio_modify_io(mask, in); + + /* Close up the EFER gate */ + wb977_close(); +} + +int __netwinder_text gpio_read(void) +{ + return inb(GP1_IO_BASE) | inb(GP2_IO_BASE) << 8; +} + +/* + * Initialise the Winbond W83977F global registers + */ +static inline void wb977_init_global(void) +{ + /* + * Enable R/W config registers + */ + wb977_wb(0x26, 0x40); + + /* + * Power down FDC (not used) + */ + wb977_wb(0x22, 0xfe); + + /* + * GP12, GP11, CIRRX, IRRXH, GP10 + */ + wb977_wb(0x2a, 0xc1); + + /* + * GP23, GP22, GP21, GP20, GP13 + */ + wb977_wb(0x2b, 0x6b); + + /* + * GP17, GP16, GP15, GP14 + */ + wb977_wb(0x2c, 0x55); +} + +/* + * Initialise the Winbond W83977F printer port + */ +static inline void wb977_init_printer(void) +{ + wb977_device_select(1); + + /* + * mode 1 == EPP + */ + wb977_wb(0xf0, 0x01); +} + +/* + * Initialise the Winbond W83977F keyboard controller + */ +static inline void wb977_init_keyboard(void) +{ + wb977_device_select(5); + + /* + * Keyboard controller address + */ + wb977_ww(0x60, 0x0060); + wb977_ww(0x62, 0x0064); + + /* + * Keyboard IRQ 1, active high, edge trigger + */ + wb977_wb(0x70, 1); + wb977_wb(0x71, 0x02); + + /* + * Mouse IRQ 5, active high, edge trigger + */ + wb977_wb(0x72, 5); + wb977_wb(0x73, 0x02); + + /* + * KBC 8MHz + */ + wb977_wb(0xf0, 0x40); + + /* + * Enable device + */ + wb977_device_enable(); +} + +/* + * Initialise the Winbond W83977F Infra-Red device + */ +static inline void wb977_init_irda(void) +{ + wb977_device_select(6); + + /* + * IR base address + */ + wb977_ww(0x60, IRDA_IO_BASE); + + /* + * IRDA IRQ 6, active high, edge trigger + */ + wb977_wb(0x70, 6); + wb977_wb(0x71, 0x02); + + /* + * RX DMA - ISA DMA 0 + */ + wb977_wb(0x74, 0x00); + + /* + * TX DMA - Disable Tx DMA + */ + wb977_wb(0x75, 0x04); + + /* + * Append CRC, Enable bank selection + */ + wb977_wb(0xf0, 0x03); + + /* + * Enable device + */ + wb977_device_enable(); +} + +/* + * Initialise Winbond W83977F general purpose IO + */ +static inline void wb977_init_gpio(void) +{ + unsigned long flags; + + /* + * Set up initial I/O definitions + */ + current_gpio_io = -1; + __gpio_modify_io(-1, GPIO_DONE | GPIO_WDTIMER); + + wb977_device_select(7); + + /* + * Group1 base address + */ + wb977_ww(0x60, GP1_IO_BASE); + wb977_ww(0x62, 0); + wb977_ww(0x64, 0); + + /* + * GP10 (Orage button) IRQ 10, active high, edge trigger + */ + wb977_wb(0x70, 10); + wb977_wb(0x71, 0x02); + + /* + * GP10: Debounce filter enabled, IRQ, input + */ + wb977_wb(0xe0, 0x19); + + /* + * Enable Group1 + */ + wb977_device_enable(); + + wb977_device_select(8); + + /* + * Group2 base address + */ + wb977_ww(0x60, GP2_IO_BASE); + + /* + * Clear watchdog timer regs + * - timer disable + */ + wb977_wb(0xf2, 0x00); + + /* + * - disable LED, no mouse nor keyboard IRQ + */ + wb977_wb(0xf3, 0x00); + + /* + * - timer counting, disable power LED, disable timeouot + */ + wb977_wb(0xf4, 0x00); + + /* + * Enable group2 + */ + wb977_device_enable(); + + /* + * Set Group1/Group2 outputs + */ + spin_lock_irqsave(&gpio_lock, flags); + gpio_modify_op(-1, GPIO_RED_LED | GPIO_FAN); + spin_unlock_irqrestore(&gpio_loc, flags); +} + +/* + * Initialise the Winbond W83977F chip. + */ +__initfunc(static void wb977_init(void)) +{ + request_region(0x370, 2, "W83977AF configuration"); + + /* + * Open up the SuperIO chip + */ + wb977_open(); + + /* + * Initialise the global registers + */ + wb977_init_global(); + + /* + * Initialise the various devices in + * the multi-IO chip. + */ + wb977_init_printer(); + wb977_init_keyboard(); + wb977_init_irda(); + wb977_init_gpio(); + + /* + * Close up the EFER gate + */ + wb977_close(); +} + +void __netwinder_text cpld_modify(int mask, int set) +{ + int msk; + + current_cpld = (current_cpld & ~mask) | set; + + gpio_modify_io(GPIO_DATA, 0); + gpio_modify_op(GPIO_IOLOAD, 0); + + for (msk = 8; msk; msk >>= 1) { + int bit = current_cpld & msk; + + gpio_modify_op(GPIO_DATA | GPIO_IOCLK, bit ? GPIO_DATA : 0); + gpio_modify_op(GPIO_IOCLK, GPIO_IOCLK); + } + + gpio_modify_op(GPIO_IOCLK|GPIO_DATA, 0); + gpio_modify_op(GPIO_IOLOAD|GPIO_DSCLK, GPIO_IOLOAD|GPIO_DSCLK); + gpio_modify_op(GPIO_IOLOAD, 0); +} + +__initfunc(static void cpld_init(void)) +{ + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + cpld_modify(-1, CPLD_UNMUTE | 4); + spin_unlock_irqrestore(&gpio_lock, flags); +} + +static unsigned char rwa_unlock[] __initdata = +{ 0x00, 0x00, 0x6a, 0xb5, 0xda, 0xed, 0xf6, 0xfb, 0x7d, 0xbe, 0xdf, 0x6f, 0x37, 0x1b, + 0x0d, 0x86, 0xc3, 0x61, 0xb0, 0x58, 0x2c, 0x16, 0x8b, 0x45, 0xa2, 0xd1, 0xe8, 0x74, + 0x3a, 0x9d, 0xce, 0xe7, 0x73, 0x39 }; + +#ifndef DEBUG +#define dprintk if (0) printk +#else +#define dprintk printk +#endif + +#define WRITE_RWA(r,v) do { outb((r), 0x279); outb((v), 0xa79); } while (0) + +static inline void rwa010_unlock(void) +{ + int i; + + WRITE_RWA(2, 2); + mdelay(10); + + for (i = 0; i < sizeof(rwa_unlock); i++) + outb(rwa_unlock[i], 0x279); +} + +static inline void rwa010_read_ident(void) +{ + unsigned char si[9]; + int i, j; + + WRITE_RWA(3, 0); + WRITE_RWA(0, 128); + + outb(1, 0x279); + + mdelay(10); + + dprintk("Identifier: "); + for (i = 0; i < 9; i++) { + si[i] = 0; + for (j = 0; j < 8; j++) { + int bit; + mdelay(1); + inb(0x203); + mdelay(1); + bit = inb(0x203); + dprintk("%02X ", bit); + si[i] |= bit << j; + } + mdelay(10); + dprintk("%02X ", si[i]); + } + dprintk("\n"); +} + +static inline void rwa010_global_init(void) +{ + WRITE_RWA(6, 2); // Assign a card no = 2 + + dprintk("Card no = %d\n", inb(0x203)); + + WRITE_RWA(7, 3); + WRITE_RWA(0x30, 0); + + WRITE_RWA(7, 4); + WRITE_RWA(0x30, 0); + + WRITE_RWA(7, 2); + WRITE_RWA(0x30, 0); +} + +static inline void rwa010_game_port_init(void) +{ + int i; + + WRITE_RWA(7, 5); + + dprintk("Slider base: "); + WRITE_RWA(0x61, 1); + i = inb(0x203); + + WRITE_RWA(0x60, 2); + dprintk("%02X%02X (201)\n", inb(0x203), i); + + WRITE_RWA(0x30, 1); +} + +static inline void rwa010_waveartist_init(int base, int irq, int dma) +{ + int i; + + WRITE_RWA(7, 0); + + dprintk("WaveArtist base: "); + WRITE_RWA(0x61, base); + i = inb(0x203); + + WRITE_RWA(0x60, base >> 8); + dprintk("%02X%02X (%X),", inb(0x203), i, base); + + WRITE_RWA(0x70, irq); + dprintk(" irq: %d (%d),", inb(0x203), irq); + + WRITE_RWA(0x74, dma); + dprintk(" dma: %d (%d)\n", inb(0x203), dma); + + WRITE_RWA(0x30, 1); +} + +static inline void rwa010_soundblaster_init(int sb_base, int al_base, int irq, int dma) +{ + int i; + + WRITE_RWA(7, 1); + + dprintk("SoundBlaster base: "); + WRITE_RWA(0x61, sb_base); + i = inb(0x203); + + WRITE_RWA(0x60, sb_base >> 8); + dprintk("%02X%02X (%X),", inb(0x203), i, sb_base); + + dprintk(" irq: "); + WRITE_RWA(0x70, irq); + dprintk("%d (%d),", inb(0x203), irq); + + dprintk(" 8-bit DMA: "); + WRITE_RWA(0x74, dma); + dprintk("%d (%d)\n", inb(0x203), dma); + + dprintk("AdLib base: "); + WRITE_RWA(0x63, al_base); + i = inb(0x203); + + WRITE_RWA(0x62, al_base >> 8); + dprintk("%02X%02X (%X)\n", inb(0x203), i, al_base); + + WRITE_RWA(0x30, 1); +} + +static void rwa010_soundblaster_reset(void) +{ + int i; + + outb(1, 0x226); + udelay(3); + outb(0, 0x226); + + for (i = 0; i < 5; i++) { + if (inb(0x22e) & 0x80) + break; + mdelay(1); + } + if (i == 5) + printk("SoundBlaster: DSP reset failed\n"); + + dprintk("SoundBlaster DSP reset: %02X (AA)\n", inb(0x22a)); + + for (i = 0; i < 5; i++) { + if ((inb(0x22c) & 0x80) == 0) + break; + mdelay(1); + } + + if (i == 5) + printk("SoundBlaster: DSP not ready\n"); + else { + outb(0xe1, 0x22c); + + dprintk("SoundBlaster DSP id: "); + i = inb(0x22a); + udelay(1); + i |= inb(0x22a) << 8; + dprintk("%04X\n", i); + + for (i = 0; i < 5; i++) { + if ((inb(0x22c) & 0x80) == 0) + break; + mdelay(1); + } + + if (i == 5) + printk("SoundBlaster: could not turn speaker off\n"); + + outb(0xd3, 0x22c); + } + + /* turn on OPL3 */ + outb(5, 0x38a); + outb(1, 0x38b); +} + +__initfunc(static void rwa010_init(void)) +{ + rwa010_unlock(); + rwa010_read_ident(); + rwa010_global_init(); + rwa010_game_port_init(); + rwa010_waveartist_init(0x250, 3, 7); + rwa010_soundblaster_init(0x220, 0x388, 3, 1); + rwa010_soundblaster_reset(); +} + +EXPORT_SYMBOL(gpio_lock); +EXPORT_SYMBOL(gpio_modify_op); +EXPORT_SYMBOL(gpio_modify_io); +EXPORT_SYMBOL(cpld_modify); + +#endif + +#ifdef CONFIG_LEDS +#define DEFAULT_LEDS 0 +#else +#define DEFAULT_LEDS GPIO_GREEN_LED +#endif + +__initfunc(void hw_init(void)) +{ +#ifdef CONFIG_ARCH_NETWINDER + /* + * this ought to have a better home... + * Since this calls the above routines, which are + * compiled only if CONFIG_ARCH_NETWINDER is set, + * these should only be parsed by the compiler + * in the same circumstance. + */ + if (machine_is_netwinder()) { + unsigned long flags; + + wb977_init(); + cpld_init(); + rwa010_init(); + + spin_lock_irqsave(&gpio_lock, flags); + gpio_modify_op(GPIO_RED_LED|GPIO_GREEN_LED, DEFAULT_LEDS); + spin_unlock_irqrestore(&gpio_lock, flags); + } +#endif + + leds_event(led_start); +} diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/iic.c linux/arch/arm/kernel/iic.c --- v2.3.6/linux/arch/arm/kernel/iic.c Sat Jul 18 11:55:23 1998 +++ linux/arch/arm/kernel/iic.c Thu Jun 17 01:11:35 1999 @@ -7,20 +7,24 @@ */ #include +#include #include -#include #include +#include +#include + +#define FORCE_ONES 0xdc /* * if delay loop has been calibrated then us that, * else use IOC timer 1. */ -static void iic_delay (void) +static void iic_delay(void) { extern unsigned long loops_per_sec; if (loops_per_sec != (1 << 12)) { - udelay(10); + udelay(100); /* was 10 */ return; } else { unsigned long flags; @@ -30,7 +34,7 @@ outb(255, IOC_T1LTCHH); outb(0, IOC_T1GO); outb(1<<6, IOC_IRQCLRA); /* clear T1 irq */ - outb(4, IOC_T1LTCHL); + outb(10, IOC_T1LTCHL); /* was 4 */ outb(0, IOC_T1LTCHH); outb(0, IOC_T1GO); while ((inb(IOC_IRQSTATA) & (1<<6)) == 0); @@ -38,124 +42,207 @@ } } -static inline void iic_start (void) +#define IIC_INIT() dat = (inb(IOC_CONTROL) | FORCE_ONES) & ~3 +#define IIC_SET_DAT outb(dat|=1, IOC_CONTROL); +#define IIC_CLR_DAT outb(dat&=~1, IOC_CONTROL); +#define IIC_SET_CLK outb(dat|=2, IOC_CONTROL); +#define IIC_CLR_CLK outb(dat&=~2, IOC_CONTROL); +#define IIC_DELAY iic_delay(); +#define IIC_READ_DATA() (inb(IOC_CONTROL) & 1) + +static inline void iic_set_lines(int clk, int dat) { - unsigned char out; + int old; - out = inb(IOC_CONTROL) | 0xc2; + old = inb(IOC_CONTROL) | FORCE_ONES; - outb(out, IOC_CONTROL); - iic_delay(); + old &= ~3; + + if (clk) + old |= 2; + if (dat) + old |= 1; + + outb(old, IOC_CONTROL); - outb(out ^ 1, IOC_CONTROL); iic_delay(); } -static inline void iic_stop (void) +static inline unsigned int iic_read_data(void) { - unsigned char out; + return inb(IOC_CONTROL) & 1; +} - out = inb(IOC_CONTROL) | 0xc3; +/* + * C: ==~~_ + * D: =~~__ + */ +static inline void iic_start(void) +{ + unsigned int dat; - iic_delay(); - outb(out ^ 1, IOC_CONTROL); + IIC_INIT(); - iic_delay(); - outb(out, IOC_CONTROL); + IIC_SET_DAT + IIC_DELAY + IIC_SET_CLK + IIC_DELAY + + IIC_CLR_DAT + IIC_DELAY + IIC_CLR_CLK + IIC_DELAY } -static int iic_sendbyte (unsigned char b) +/* + * C: __~~ + * D: =__~ + */ +static inline void iic_stop(void) { - unsigned char out, in; - int i; + unsigned int dat; - out = (inb(IOC_CONTROL) & 0xfc) | 0xc0; + IIC_INIT(); - outb(out, IOC_CONTROL); - for (i = 7; i >= 0; i--) { - unsigned char c; - c = out | ((b & (1 << i)) ? 1 : 0); + IIC_CLR_DAT + IIC_DELAY + IIC_SET_CLK + IIC_DELAY + IIC_SET_DAT + IIC_DELAY +} - outb(c, IOC_CONTROL); - iic_delay(); +/* + * C: __~_ + * D: =___ + */ +static inline void iic_acknowledge(void) +{ + unsigned int dat; - outb(c | 2, IOC_CONTROL); - iic_delay(); + IIC_INIT(); - outb(c, IOC_CONTROL); - } - outb(out | 1, IOC_CONTROL); - iic_delay(); + IIC_CLR_DAT + IIC_DELAY + IIC_SET_CLK + IIC_DELAY + IIC_CLR_CLK + IIC_DELAY +} - outb(out | 3, IOC_CONTROL); - iic_delay(); +/* + * C: __~_ + * D: =~H~ + */ +static inline int iic_is_acknowledged(void) +{ + unsigned int dat, ack_bit; - in = inb(IOC_CONTROL) & 1; + IIC_INIT(); - outb(out | 1, IOC_CONTROL); - iic_delay(); + IIC_SET_DAT + IIC_DELAY + IIC_SET_CLK + IIC_DELAY - outb(out, IOC_CONTROL); - iic_delay(); + ack_bit = IIC_READ_DATA(); - if(in) { - printk("No acknowledge from RTC\n"); - return 1; - } else - return 0; + IIC_CLR_CLK + IIC_DELAY + + return ack_bit == 0; +} + +/* + * C: _~__~__~__~__~__~__~__~_ + * D: =DDXDDXDDXDDXDDXDDXDDXDD + */ +static void iic_sendbyte(unsigned int b) +{ + unsigned int dat, i; + + IIC_INIT(); + + for (i = 0; i < 8; i++) { + if (b & 128) + IIC_SET_DAT + else + IIC_CLR_DAT + IIC_DELAY + + IIC_SET_CLK + IIC_DELAY + IIC_CLR_CLK + IIC_DELAY + + b <<= 1; + } } -static unsigned char iic_recvbyte (void) +/* + * C: __~_~_~_~_~_~_~_~_ + * D: =~HHHHHHHHHHHHHHHH + */ +static unsigned char iic_recvbyte(void) { - unsigned char out, in; - int i; + unsigned int dat, i, in; + + IIC_INIT(); - out = (inb(IOC_CONTROL) & 0xfc) | 0xc0; + IIC_SET_DAT + IIC_DELAY - outb(out, IOC_CONTROL); in = 0; - for (i = 7; i >= 0; i--) { - outb(out | 1, IOC_CONTROL); - iic_delay(); - outb(out | 3, IOC_CONTROL); - iic_delay(); - in = (in << 1) | (inb(IOC_CONTROL) & 1); - outb(out | 1, IOC_CONTROL); - iic_delay(); + for (i = 0; i < 8; i++) { + IIC_SET_CLK + IIC_DELAY + + in = (in << 1) | IIC_READ_DATA(); + + IIC_CLR_CLK + IIC_DELAY } - outb(out, IOC_CONTROL); - iic_delay(); - outb(out | 2, IOC_CONTROL); - iic_delay(); return in; } -void iic_control (unsigned char addr, unsigned char loc, unsigned char *buf, int len) +int iic_control (unsigned char addr, unsigned char loc, unsigned char *buf, int len) { - iic_start(); + int i, err = -EIO; - if (iic_sendbyte(addr & 0xfe)) + iic_start(); + iic_sendbyte(addr & 0xfe); + if (!iic_is_acknowledged()) goto error; - if (iic_sendbyte(loc)) + iic_sendbyte(loc); + if (!iic_is_acknowledged()) goto error; if (addr & 1) { - int i; - - for (i = 0; i < len; i++) - if (iic_sendbyte (buf[i])) - break; - } else { - int i; - iic_stop(); iic_start(); iic_sendbyte(addr|1); - for (i = 0; i < len; i++) - buf[i] = iic_recvbyte (); + if (!iic_is_acknowledged()) + goto error; + + for (i = 0; i < len - 1; i++) { + buf[i] = iic_recvbyte(); + iic_acknowledge(); + } + buf[i] = iic_recvbyte(); + } else { + for (i = 0; i < len; i++) { + iic_sendbyte(buf[i]); + + if (!iic_is_acknowledged()) + goto error; + } } + + err = 0; error: iic_stop(); + + return err; } diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/init_task.c linux/arch/arm/kernel/init_task.c --- v2.3.6/linux/arch/arm/kernel/init_task.c Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/init_task.c Thu Jun 17 01:11:35 1999 @@ -6,9 +6,10 @@ static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; +static struct file * init_fd_array[NR_OPEN] = { NULL, }; static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS; -struct mm_struct init_mm = INIT_MM; +struct mm_struct init_mm = INIT_MM(init_mm); /* * Initial task structure. @@ -20,4 +21,5 @@ * * The things we do for performance.. */ -union task_union init_task_union __attribute__((__section__(".init.task"))) = { INIT_TASK }; +union task_union init_task_union __attribute__((__section__(".init.task"))) = + { INIT_TASK(init_task_union.task) }; diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/ioport.c linux/arch/arm/kernel/ioport.c --- v2.3.6/linux/arch/arm/kernel/ioport.c Sun Sep 6 10:44:47 1998 +++ linux/arch/arm/kernel/ioport.c Wed Dec 31 16:00:00 1969 @@ -1,29 +0,0 @@ -/* - * linux/arch/arm/kernel/ioport.c - * - * Io-port support is not used for ARM - */ - -#include -#include -#include -#include -#include - -/* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */ -/*asmlinkage void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value) -{ -}*/ - -asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on) -{ - return -ENOSYS; -} - -asmlinkage int sys_iopl(long ebx,long ecx,long edx, - long esi, long edi, long ebp, long eax, long ds, - long es, long fs, long gs, long orig_eax, - long eip,long cs,long eflags,long esp,long ss) -{ - return -ENOSYS; -} diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/irq.c linux/arch/arm/kernel/irq.c --- v2.3.6/linux/arch/arm/kernel/irq.c Wed Dec 23 09:44:40 1998 +++ linux/arch/arm/kernel/irq.c Thu Jun 17 01:11:35 1999 @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -32,7 +31,6 @@ #include #include -#include #include #ifndef SMP @@ -46,10 +44,22 @@ #define cliIF() #endif +/* + * Maximum IRQ count. Currently, this is arbitary. + * However, it should not be set too low to prevent + * false triggering. Conversely, if it is set too + * high, then you could miss a stuck IRQ. + * + * Maybe we ought to set a timer and re-enable the + * IRQ at a later time? + */ +#define MAX_IRQ_CNT 100000 + unsigned int local_bh_count[NR_CPUS]; unsigned int local_irq_count[NR_CPUS]; spinlock_t irq_controller_lock; +int setup_arm_irq(int, struct irqaction *); extern int get_fiq_list(char *); extern void init_FIQ(void); @@ -60,17 +70,29 @@ unsigned int probing : 1; /* IRQ in use for a probe */ unsigned int probe_ok : 1; /* IRQ can be used for probe */ unsigned int valid : 1; /* IRQ claimable */ - unsigned int unused :26; + unsigned int noautoenable : 1; /* don't automatically enable IRQ */ + unsigned int unused :25; void (*mask_ack)(unsigned int irq); /* Mask and acknowledge IRQ */ void (*mask)(unsigned int irq); /* Mask IRQ */ void (*unmask)(unsigned int irq); /* Unmask IRQ */ struct irqaction *action; - unsigned int unused2[3]; + /* + * IRQ lock detection + */ + unsigned int lck_cnt; + unsigned int lck_pc; + unsigned int lck_jif; }; static struct irqdesc irq_desc[NR_IRQS]; /* + * Get architecture specific interrupt handlers + * and interrupt initialisation. + */ +#include + +/* * Dummy mask/unmask handler */ static void dummy_mask_unmask_irq(unsigned int irq) @@ -94,10 +116,12 @@ spin_lock_irqsave(&irq_controller_lock, flags); cliIF(); - irq_desc[irq].enabled = 1; irq_desc[irq].probing = 0; irq_desc[irq].triggered = 0; - irq_desc[irq].unmask(irq); + if (!irq_desc[irq].noautoenable) { + irq_desc[irq].enabled = 1; + irq_desc[irq].unmask(irq); + } spin_unlock_irqrestore(&irq_controller_lock, flags); } @@ -119,21 +143,52 @@ *p++ = '\n'; } -#ifdef CONFIG_ACORN +#ifdef CONFIG_ARCH_ACORN p += get_fiq_list(p); #endif return p - buf; } /* + * IRQ lock detection. + * + * Hopefully, this should get us out of a few locked situations. + * However, it may take a while for this to happen, since we need + * a large number if IRQs to appear in the same jiffie with the + * same instruction pointer (or within 2 instructions). + */ +static void check_irq_lock(struct irqdesc *desc, int irq, struct pt_regs *regs) +{ + unsigned long instr_ptr = instruction_pointer(regs); + + if (desc->lck_jif == jiffies && + desc->lck_pc >= instr_ptr && desc->lck_pc < instr_ptr + 8) { + desc->lck_cnt += 1; + + if (desc->lck_cnt > MAX_IRQ_CNT) { + printk(KERN_ERR "IRQ LOCK: IRQ%d is locking the system, disabled\n", irq); + disable_irq(irq); + } + } else { + desc->lck_cnt = 0; + desc->lck_pc = instruction_pointer(regs); + desc->lck_jif = jiffies; + } +} + +/* * do_IRQ handles all normal device IRQ's */ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) { - struct irqdesc * desc = irq_desc + irq; + struct irqdesc * desc; struct irqaction * action; int status, cpu; + irq = fixup_irq(irq); + + desc = irq_desc + irq; + spin_lock(&irq_controller_lock); desc->mask_ack(irq); spin_unlock(&irq_controller_lock); @@ -174,6 +229,12 @@ } } + /* + * Debug measure - hopefully we can continue if an + * IRQ lockup problem occurs... + */ + check_irq_lock(desc, irq, regs); + irq_exit(cpu, irq); /* @@ -181,15 +242,10 @@ * a return code from the irq handler to tell us * whether the handler wants us to do software bottom * half handling or not.. - * - * ** IMPORTANT NOTE: do_bottom_half() ENABLES IRQS!!! ** - * ** WE MUST DISABLE THEM AGAIN, ELSE IDE DISKS GO ** - * ** AWOL ** */ if (1) { if (bh_active & bh_mask) do_bottom_half(); - __cli(); } } @@ -227,11 +283,27 @@ struct irqaction *old, **p; unsigned long flags; - if (new->flags & SA_SAMPLE_RANDOM) + /* + * Some drivers like serial.c use request_irq() heavily, + * so we have to be careful not to interfere with a + * running system. + */ + if (new->flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ rand_initialize_irq(irq); + } + /* + * The following block of code has to be executed atomically + */ spin_lock_irqsave(&irq_controller_lock, flags); - p = &irq_desc[irq].action; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ @@ -252,28 +324,24 @@ if (!shared) { irq_desc[irq].nomask = (new->flags & SA_IRQNOMASK) ? 1 : 0; - irq_desc[irq].enabled = 1; irq_desc[irq].probing = 0; - irq_desc[irq].unmask(irq); + if (!irq_desc[irq].noautoenable) { + irq_desc[irq].enabled = 1; + irq_desc[irq].unmask(irq); + } } spin_unlock_irqrestore(&irq_controller_lock, flags); return 0; } -/* - * Using "struct sigaction" is slightly silly, but there - * are historical reasons and it works well, so.. - */ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irq_flags, const char * devname, void *dev_id) { unsigned long retval; struct irqaction *action; - if (!irq_desc[irq].valid) - return -EINVAL; - if (!handler) + if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler) return -EINVAL; action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL); @@ -299,28 +367,30 @@ struct irqaction * action, **p; unsigned long flags; - if (!irq_desc[irq].valid) { + if (irq >= NR_IRQS || !irq_desc[irq].valid) { printk(KERN_ERR "Trying to free IRQ%d\n",irq); #ifdef CONFIG_DEBUG_ERRORS __backtrace(); #endif return; } + + spin_lock_irqsave(&irq_controller_lock, flags); for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) { if (action->dev_id != dev_id) continue; /* Found it - now free it */ - save_flags_cli (flags); *p = action->next; - restore_flags (flags); kfree(action); - return; + goto out; } printk(KERN_ERR "Trying to free free IRQ%d\n",irq); #ifdef CONFIG_DEBUG_ERRORS __backtrace(); #endif +out: + spin_unlock_irqrestore(&irq_controller_lock, flags); } /* Start the interrupt probing. Unlike other architectures, @@ -346,7 +416,6 @@ continue; irq_desc[i].probing = 1; - irq_desc[i].enabled = 1; irq_desc[i].triggered = 0; irq_desc[i].unmask(i); irqs += 1; @@ -364,7 +433,8 @@ */ spin_lock_irq(&irq_controller_lock); for (i = 0; i < NR_IRQS; i++) { - if (irq_desc[i].probing && irq_desc[i].triggered) { + if (irq_desc[i].probing && + irq_desc[i].triggered) { irq_desc[i].probing = 0; irqs -= 1; } @@ -383,7 +453,7 @@ int probe_irq_off(unsigned long irqs) { unsigned int i; - int irq_found = -1; + int irq_found = NO_IRQ; /* * look at the interrupts, and find exactly one @@ -393,7 +463,7 @@ for (i = 0; i < NR_IRQS; i++) { if (irq_desc[i].probing && irq_desc[i].triggered) { - if (irq_found != -1) { + if (irq_found != NO_IRQ) { irq_found = NO_IRQ; goto out; } @@ -405,21 +475,19 @@ irq_found = NO_IRQ; out: spin_unlock_irq(&irq_controller_lock); + return irq_found; } -/* - * Get architecture specific interrupt handlers - * and interrupt initialisation. - */ -#include - __initfunc(void init_IRQ(void)) { extern void init_dma(void); int irq; for (irq = 0; irq < NR_IRQS; irq++) { + irq_desc[irq].probe_ok = 0; + irq_desc[irq].valid = 0; + irq_desc[irq].noautoenable = 0; irq_desc[irq].mask_ack = dummy_mask_unmask_irq; irq_desc[irq].mask = dummy_mask_unmask_irq; irq_desc[irq].unmask = dummy_mask_unmask_irq; diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/leds-ebsa110.c linux/arch/arm/kernel/leds-ebsa110.c --- v2.3.6/linux/arch/arm/kernel/leds-ebsa110.c Sun Sep 6 10:44:47 1998 +++ linux/arch/arm/kernel/leds-ebsa110.c Thu Jun 17 01:11:35 1999 @@ -7,11 +7,13 @@ * * - Red - toggles state every 50 timer interrupts */ +#include + #include #include #include -void leds_event(led_event_t ledevt) +void ebsa110_leds_event(led_event_t ledevt) { unsigned long flags; @@ -28,3 +30,7 @@ restore_flags(flags); } + +void (*leds_event)(led_event_t) = ebsa110_leds_event; + +EXPORT_SYMBOL(leds_event); diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/leds-ebsa285.c linux/arch/arm/kernel/leds-ebsa285.c --- v2.3.6/linux/arch/arm/kernel/leds-ebsa285.c Sun Sep 6 10:44:47 1998 +++ linux/arch/arm/kernel/leds-ebsa285.c Wed Dec 31 16:00:00 1969 @@ -1,44 +0,0 @@ -/* - * arch/arm/kernel/leds-ebsa285.c - * - * Copyright (C) 1998 Russell King - * - * EBSA-285 LED control routines. We use the leds as follows: - * - * - Green - toggles state every 50 timer interrupts - * - Amber - On if system is not idle - * - Red - currently unused - */ -#include -#include -#include - -static char led_state = XBUS_LED_RED | XBUS_LED_GREEN; - -void leds_event(led_event_t ledevt) -{ - unsigned long flags; - - save_flags_cli(flags); - - switch(ledevt) { - case led_idle_start: - led_state |= XBUS_LED_AMBER; - break; - - case led_idle_end: - led_state &= ~XBUS_LED_AMBER; - break; - - case led_timer: - led_state ^= XBUS_LED_GREEN; - break; - - default: - break; - } - - restore_flags(flags); - - *XBUS_LEDS = led_state; -} diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/leds-footbridge.c linux/arch/arm/kernel/leds-footbridge.c --- v2.3.6/linux/arch/arm/kernel/leds-footbridge.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/kernel/leds-footbridge.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,249 @@ +/* + * arch/arm/kernel/leds-footbridge.c + * + * Copyright (C) 1998-1999 Russell King + * + * EBSA-285 and NetWinder LED control routines. + * + * The EBSA-285 uses the leds as follows: + * - Green - toggles state every 50 timer interrupts + * - Amber - On if system is not idle + * - Red - currently unused + * + * The Netwinder uses the leds as follows: + * - Green - toggles state every 50 timer interrupts + * - Red - On if the system is not idle + * + * Changelog: + * 02-05-1999 RMK Various cleanups + */ +#include +#include +#include +#include + +#include +#include +#include +#include + +#define LED_STATE_ENABLED 1 +#define LED_STATE_CLAIMED 2 +static char led_state; +static char hw_led_state; + +static spinlock_t leds_lock = SPIN_LOCK_UNLOCKED; + +#ifdef CONFIG_ARCH_EBSA285 + +static void __ebsa285_text ebsa285_leds_event(led_event_t evt) +{ + unsigned long flags; + + spin_lock_irqsave(&leds_lock, flags); + + switch (evt) { + case led_start: + hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN; +#ifndef CONFIG_LEDS_IDLE + hw_led_state |= XBUS_LED_AMBER; +#endif + led_state |= LED_STATE_ENABLED; + break; + + case led_stop: + led_state &= ~LED_STATE_ENABLED; + break; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN | XBUS_LED_AMBER; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN | XBUS_LED_AMBER; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state ^= XBUS_LED_GREEN; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= XBUS_LED_RED; + break; + + case led_idle_end: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~XBUS_LED_RED; + break; +#endif + + case led_green_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~XBUS_LED_GREEN; + break; + + case led_green_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= XBUS_LED_GREEN; + break; + + case led_amber_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~XBUS_LED_AMBER; + break; + + case led_amber_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= XBUS_LED_AMBER; + break; + + case led_red_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~XBUS_LED_RED; + break; + + case led_red_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= XBUS_LED_RED; + break; + + default: + break; + } + + if (led_state & LED_STATE_ENABLED) + *XBUS_LEDS = hw_led_state; + + spin_unlock_irqrestore(&leds_lock, flags); +} + +#endif + +#ifdef CONFIG_ARCH_NETWINDER + +static void __netwinder_text netwinder_leds_event(led_event_t evt) +{ + unsigned long flags; + + spin_lock_irqsave(&leds_lock, flags); + + switch (evt) { + case led_start: + led_state |= LED_STATE_ENABLED; + hw_led_state = 0; + break; + + case led_stop: + led_state &= ~LED_STATE_ENABLED; + break; + + case led_claim: + led_state |= LED_STATE_CLAIMED; + hw_led_state = 0; + break; + + case led_release: + led_state &= ~LED_STATE_CLAIMED; + hw_led_state = 0; + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state ^= GPIO_GREEN_LED; + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state &= ~GPIO_RED_LED; + break; + + case led_idle_end: + if (!(led_state & LED_STATE_CLAIMED)) + hw_led_state |= GPIO_RED_LED; + break; +#endif + + case led_green_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= GPIO_GREEN_LED; + break; + + case led_green_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~GPIO_GREEN_LED; + break; + + case led_amber_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= GPIO_GREEN_LED | GPIO_RED_LED; + break; + + case led_amber_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~(GPIO_GREEN_LED | GPIO_RED_LED); + break; + + case led_red_on: + if (led_state & LED_STATE_CLAIMED) + hw_led_state |= GPIO_RED_LED; + break; + + case led_red_off: + if (led_state & LED_STATE_CLAIMED) + hw_led_state &= ~GPIO_RED_LED; + break; + + default: + break; + } + + spin_unlock_irqrestore(&leds_lock, flags); + + if (led_state & LED_STATE_ENABLED) { + spin_lock_irqsave(&gpio_lock, flags); + gpio_modify_op(GPIO_RED_LED | GPIO_GREEN_LED, hw_led_state); + spin_unlock_irqrestore(&gpio_lock, flags); + } +} + +#endif + +static void dummy_leds_event(led_event_t evt) +{ +} + +__initfunc(void +init_leds_event(led_event_t evt)) +{ + switch (machine_arch_type) { +#ifdef CONFIG_ARCH_EBSA285 + case MACH_TYPE_EBSA285: + leds_event = ebsa285_leds_event; + break; +#endif +#ifdef CONFIG_ARCH_NETWINDER + case MACH_TYPE_NETWINDER: + leds_event = netwinder_leds_event; + break; +#endif + + default: + leds_event = dummy_leds_event; + } + + leds_event(evt); +} + +void (*leds_event)(led_event_t) = init_leds_event; + +EXPORT_SYMBOL(leds_event); diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/oldlatches.c linux/arch/arm/kernel/oldlatches.c --- v2.3.6/linux/arch/arm/kernel/oldlatches.c Tue Jan 20 16:39:41 1998 +++ linux/arch/arm/kernel/oldlatches.c Thu Jun 17 01:11:35 1999 @@ -4,6 +4,7 @@ * (c) David Alan Gilbert 1995/1996 */ #include +#include #include #include @@ -40,7 +41,7 @@ } #endif -void oldlatch_init(void) +void __init oldlatch_init(void) { printk("oldlatch: init\n"); #ifdef LATCHAADDR diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/process.c linux/arch/arm/kernel/process.c --- v2.3.6/linux/arch/arm/kernel/process.c Sun Sep 6 10:44:47 1998 +++ linux/arch/arm/kernel/process.c Thu Jun 17 01:11:35 1999 @@ -34,7 +34,6 @@ #include #include -#include #include #include @@ -55,46 +54,37 @@ } /* - * The idle loop on an arm.. + * The idle loop on an ARM... */ asmlinkage int sys_idle(void) { - int ret = -EPERM; - - lock_kernel(); if (current->pid != 0) - goto out; + return -EPERM; + /* endless idle loop with no priority at all */ - current->priority = -100; - for (;;) - { + while (1) { + if (!current->need_resched && !hlt_counter) + proc_idle(); + current->policy = SCHED_YIELD; + schedule(); +#ifndef CONFIG_NO_PGT_CACHE check_pgt_cache(); -#if 0 //def ARCH_IDLE_OK - if (!hlt_counter && !current->need_resched) - proc_idle (); #endif - run_task_queue(&tq_scheduler); - schedule(); } - ret = 0; -out: - unlock_kernel(); - return ret; } +static char reboot_mode = 'h'; + __initfunc(void reboot_setup(char *str, int *ints)) { + reboot_mode = str[0]; } -/* - * This routine reboots the machine by resetting the expansion cards via - * their loaders, turning off the processor cache (if ARM3), copying the - * first instruction of the ROM to 0, and executing it there. - */ void machine_restart(char * __unused) { - proc_hard_reset (); - arch_hard_reset (); + arch_reset(reboot_mode); + panic("Reboot failed\n"); + while (1); } void machine_halt(void) @@ -150,6 +140,67 @@ } /* + * Task structure and kernel stack allocation. + * + * Taken from the i386 version. + */ +#ifdef CONFIG_CPU_32 +#define EXTRA_TASK_STRUCT 8 +static struct task_struct *task_struct_stack[EXTRA_TASK_STRUCT]; +static int task_struct_stack_ptr = -1; +#endif + +struct task_struct *alloc_task_struct(void) +{ + struct task_struct *tsk; + +#ifndef EXTRA_TASK_STRUCT + tsk = ll_alloc_task_struct(); +#else + int index; + + index = task_struct_stack_ptr; + if (index >= EXTRA_TASK_STRUCT/2) + goto use_cache; + + tsk = ll_alloc_task_struct(); + + if (!tsk) { + index = task_struct_stack_ptr; + + if (index >= 0) { +use_cache: tsk = task_struct_stack[index]; + task_struct_stack_ptr = index - 1; + } + } +#endif +#ifdef CONFIG_SYSRQ + /* You need this if you want SYSRQ-T to give sensible stack + * usage information + */ + if (tsk) { + char *p = (char *)tsk; + memzero(p+KERNEL_STACK_SIZE, KERNEL_STACK_SIZE); + } +#endif + + return tsk; +} + +void free_task_struct(struct task_struct *p) +{ +#ifdef EXTRA_TASK_STRUCT + int index = task_struct_stack_ptr + 1; + + if (index < EXTRA_TASK_STRUCT) { + task_struct_stack[index] = p; + task_struct_stack_ptr = index; + } else +#endif + ll_free_task_struct(p); +} + +/* * Free current thread data structures etc.. */ void exit_thread(void) @@ -179,9 +230,10 @@ childregs = ((struct pt_regs *)((unsigned long)p + 8192)) - 1; *childregs = *regs; childregs->ARM_r0 = 0; + childregs->ARM_sp = esp; save = ((struct context_save_struct *)(childregs)) - 1; - copy_thread_css(save); + init_thread_css(save); p->tss.save = save; return 0; @@ -224,3 +276,29 @@ dump->regs = *regs; dump->u_fpvalid = dump_fpu (regs, &dump->u_fp); } + +/* + * This is the mechanism for creating a new kernel thread. + * + * NOTE! Only a kernel-only process(ie the swapper or direct descendants + * who haven't done an "execve()") should use this: it will work within + * a system call from a "real" process, but the process memory space will + * not be free'd until both the parent and the child have exited. + */ +pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) +{ + extern int sys_exit(int) __attribute__((noreturn)); + pid_t __ret; + + __asm__ __volatile__( + "mov r0, %1 @ kernel_thread sys_clone\n" +" mov r1, #0\n" + __syscall(clone)"\n" +" mov %0, r0" + : "=r" (__ret) + : "Ir" (flags | CLONE_VM) : "r0", "r1"); + if (__ret == 0) + sys_exit((fn)(arg)); + return __ret; +} + diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/ptrace.c linux/arch/arm/kernel/ptrace.c --- v2.3.6/linux/arch/arm/kernel/ptrace.c Mon Jun 7 11:15:33 1999 +++ linux/arch/arm/kernel/ptrace.c Thu Jun 17 01:11:35 1999 @@ -34,11 +34,11 @@ */ static inline long get_stack_long(struct task_struct *task, int offset) { - unsigned char *stack; + struct pt_regs *regs; - stack = (unsigned char *)((unsigned long)task + 8192 - sizeof(struct pt_regs)); - stack += offset << 2; - return *(unsigned long *)stack; + regs = (struct pt_regs *)((unsigned long)task + 8192 - sizeof(struct pt_regs)); + + return regs->uregs[offset]; } /* @@ -50,11 +50,12 @@ static inline long put_stack_long(struct task_struct *task, int offset, unsigned long data) { - unsigned char *stack; + struct pt_regs *regs; + + regs = (struct pt_regs *)((unsigned long)task + 8192 - sizeof(struct pt_regs)); + + regs->uregs[offset] = data; - stack = (unsigned char *)((unsigned long)task + 8192 - sizeof(struct pt_regs)); - stack += offset << 2; - *(unsigned long *) stack = data; return 0; } @@ -157,11 +158,16 @@ if (MAP_NR(page) < max_mapnr) { page += addr & ~PAGE_MASK; + + flush_cache_range(vma->vm_mm, addr, addr + sizeof(unsigned long)); + *(unsigned long *)page = data; - __flush_entry_to_ram(page); + + clean_cache_area(page, sizeof(unsigned long)); + + set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); + flush_tlb_page(vma, addr & PAGE_MASK); } - set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); - flush_tlb(); } /* @@ -343,8 +349,7 @@ printk ("=%08lX ", val); return val; } -#undef pc_pointer -#define pc_pointer(x) ((x) & 0x03fffffc) + int ptrace_set_bpt (struct task_struct *child) { unsigned long insn, pc, alt; @@ -651,7 +656,6 @@ return 0; wake_up_process (child); child->exit_code = SIGKILL; - ptrace_cancel_bpt (child); /* make sure single-step breakpoint is gone. */ ptrace_cancel_bpt (child); ret = 0; diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/setup.c linux/arch/arm/kernel/setup.c --- v2.3.6/linux/arch/arm/kernel/setup.c Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/setup.c Thu Jun 17 01:11:35 1999 @@ -56,12 +56,17 @@ #define SUPPORT_CPU_SA110 #endif -#ifndef CONFIG_CMDLINE -#define CONFIG_CMDLINE "root=/dev/nfs rw" -#endif #define MEM_SIZE (16*1024*1024) #define COMMAND_LINE_SIZE 256 +#ifndef CONFIG_CMDLINE +#define CONFIG_CMDLINE "" +#endif + +extern void reboot_setup(char *str, int *ints); +extern void fpe_init(void); +extern void disable_hlt(void); + struct drive_info_struct { char dummy[32]; } drive_info; struct screen_info screen_info = { orig_video_lines: 30, @@ -87,20 +92,26 @@ /*-- Match -- --- Mask -- -- Manu -- Processor uname -m --- ELF STUFF --- --- processor asm funcs --- */ #if defined(CONFIG_CPU_26) + /* ARM2 fake ident */ { 0x41560200, 0xfffffff0, "ARM/VLSI", "arm2" , "armv1" , "v1", 0, &arm2_processor_functions }, + /* ARM250 fake ident */ { 0x41560250, 0xfffffff0, "ARM/VLSI", "arm250" , "armv2" , "v2", HWCAP_SWP, &arm250_processor_functions }, + /* ARM3 processors */ { 0x41560300, 0xfffffff0, "ARM/VLSI", "arm3" , "armv2" , "v2", HWCAP_SWP, &arm3_processor_functions }, #elif defined(CONFIG_CPU_32) #ifdef SUPPORT_CPU_ARM6 + /* ARM6 */ { 0x41560600, 0xfffffff0, "ARM/VLSI", "arm6" , "armv3" , "v3", HWCAP_SWP, &arm6_processor_functions }, + /* ARM610 */ { 0x41560610, 0xfffffff0, "ARM/VLSI", "arm610" , "armv3" , "v3", HWCAP_SWP, &arm6_processor_functions }, #endif #ifdef SUPPORT_CPU_ARM7 + /* ARM7's have a strange numbering */ { 0x41007000, 0xffffff00, "ARM/VLSI", "arm7" , "armv3" , "v3", HWCAP_SWP, &arm7_processor_functions }, /* ARM710 IDs are non-standard */ @@ -108,10 +119,16 @@ &arm7_processor_functions }, #endif #ifdef SUPPORT_CPU_SA110 - { 0x4401a100, 0xfffffff0, "DEC", "sa110" , "armv4" , "v3", HWCAP_SWP|HWCAP_HALF, +#ifdef CONFIG_ARCH_RPC + /* Acorn RiscPC's can't handle ARMv4 half-word instructions */ + { 0x4401a100, 0xfffffff0, "Intel", "sa110" , "armv4" , "v4", HWCAP_SWP, + &sa110_processor_functions }, +#else + { 0x4401a100, 0xfffffff0, "Intel", "sa110" , "armv4" , "v4", HWCAP_SWP|HWCAP_HALF, &sa110_processor_functions }, #endif #endif +#endif { 0x00000000, 0x00000000, "***", "unknown", "unknown", "**", 0, NULL } }; @@ -119,7 +136,7 @@ * From head-armv.S */ unsigned int processor_id; -unsigned int machine_type; +unsigned int __machine_arch_type; int armidindex; extern int root_mountflags; @@ -132,139 +149,10 @@ */ /* - * Risc-PC specific initialisation - */ -#ifdef CONFIG_ARCH_RPC - -#include - -unsigned int vram_half_sam; - -static void -setup_rpc(struct param_struct *params) -{ - extern void init_dram_banks(const struct param_struct *params); - - init_dram_banks(params); - - switch (params->u1.s.pages_in_vram) { - case 256: - vram_half_sam = 1024; - break; - case 512: - default: - vram_half_sam = 2048; - } -} -#else -#define setup_rpc(x) -#endif - -#ifdef PARAMS_BASE - -#ifdef CONFIG_ARCH_ACORN -int memc_ctrl_reg; -int number_ide_drives; -int number_mfm_drives; -#endif - -static struct param_struct *params = (struct param_struct *)PARAMS_BASE; - -__initfunc(static char * -setup_params(unsigned long *mem_end_p)) -{ - ROOT_DEV = to_kdev_t(params->u1.s.rootdev); - ORIG_X = params->u1.s.video_x; - ORIG_Y = params->u1.s.video_y; - ORIG_VIDEO_COLS = params->u1.s.video_num_cols; - ORIG_VIDEO_LINES = params->u1.s.video_num_rows; - -#ifdef CONFIG_ARCH_ACORN -#ifndef CONFIG_FB - { - extern int bytes_per_char_h; - extern int bytes_per_char_v; - - bytes_per_char_h = params->u1.s.bytes_per_char_h; - bytes_per_char_v = params->u1.s.bytes_per_char_v; - } -#endif - memc_ctrl_reg = params->u1.s.memc_control_reg; - number_ide_drives = (params->u1.s.adfsdrives >> 6) & 3; - number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3; - - setup_rpc(params); - - if (!(params->u1.s.flags & FLAG_READONLY)) - root_mountflags &= ~MS_RDONLY; -#endif -#ifdef CONFIG_BLK_DEV_RAM - { - extern int rd_doload; - extern int rd_prompt; - extern int rd_image_start; - - rd_image_start = params->u1.s.rd_start; - rd_prompt = (params->u1.s.flags & FLAG_RDPROMPT) == 0; - rd_doload = (params->u1.s.flags & FLAG_RDLOAD) == 0; - } -#endif - -#ifdef CONFIG_ARCH_ACORN - *mem_end_p = GET_MEMORY_END(params); -#elif defined(CONFIG_ARCH_EBSA285) - *mem_end_p = PAGE_OFFSET + params->u1.s.page_size * params->u1.s.nr_pages; -#else - *mem_end_p = PAGE_OFFSET + MEM_SIZE; -#endif - - return params->commandline; -} - -#else - -static char default_command_line[] __initdata = CONFIG_CMDLINE; - -__initfunc(static char * -setup_params(unsigned long *mem_end_p)) -{ - ROOT_DEV = 0x00ff; - -#ifdef CONFIG_BLK_DEV_RAM - { - extern int rd_doload; - extern int rd_prompt; - extern int rd_image_start; - - rd_image_start = 0; - rd_prompt = 1; - rd_doload = 1; - } -#endif - - *mem_end_p = PAGE_OFFSET + MEM_SIZE; - - return default_command_line; -} -#endif - -/* * initial ram disk */ #ifdef CONFIG_BLK_DEV_INITRD __initfunc(static void -setup_initrd(const struct param_struct *params)) -{ - if (params->u1.s.initrd_start) { - initrd_start = params->u1.s.initrd_start; - initrd_end = initrd_start + params->u1.s.initrd_size; - } else { - initrd_start = 0; - initrd_end = 0; - } -} - -__initfunc(static void check_initrd(unsigned long mem_start, unsigned long mem_end)) { if (initrd_end > mem_end) { @@ -276,7 +164,6 @@ } #else -#define setup_initrd(p) #define check_initrd(ms,me) #endif @@ -289,48 +176,47 @@ armidlist[armidindex].mask) armidindex += 1; - if (armidlist[armidindex].id == 0) { -#ifdef CONFIG_ARCH_ACORN - int i; - - for (i = 0; i < 3200; i++) - ((unsigned long *)SCREEN2_BASE)[i] = 0x77113322; -#endif + if (armidlist[armidindex].id == 0) while (1); - } processor = *armidlist[armidindex].proc; processor._proc_init(); } +static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; static char command_line[COMMAND_LINE_SIZE] = { 0, }; char saved_command_line[COMMAND_LINE_SIZE]; __initfunc(static void -setup_mem(char *cmd_line, unsigned long *mem_start, unsigned long *mem_end)) +setup_mem(char *cmd_line, unsigned long *mem_start, unsigned long *mem_sz)) { - char c, *to = command_line; + char c = ' ', *to = command_line; int len = 0; *mem_start = (unsigned long)&_end; for (;;) { - if (cmd_line[0] == ' ' && - cmd_line[1] == 'm' && - cmd_line[2] == 'e' && - cmd_line[3] == 'm' && - cmd_line[4] == '=') { - *mem_end = simple_strtoul(cmd_line+5, &cmd_line, 0); - switch(*cmd_line) { - case 'M': - case 'm': - *mem_end <<= 10; - case 'K': - case 'k': - *mem_end <<= 10; + if (c == ' ') { + if (cmd_line[0] == 'm' && + cmd_line[1] == 'e' && + cmd_line[2] == 'm' && + cmd_line[3] == '=') { + *mem_sz = simple_strtoul(cmd_line+4, &cmd_line, 0); + switch(*cmd_line) { + case 'M': + case 'm': + *mem_sz <<= 10; + case 'K': + case 'k': + *mem_sz <<= 10; + cmd_line++; + } + } + /* if there are two spaces, remove one */ + if (*cmd_line == ' ') { cmd_line++; + continue; } - *mem_end = *mem_end + PAGE_OFFSET; } c = *cmd_line++; if (!c) @@ -341,42 +227,222 @@ } *to = '\0'; + + /* remove trailing spaces */ + while (*--to == ' ' && to != command_line) + *to = '\0'; } +__initfunc(static void +setup_ram(int doload, int prompt, int image_start)) +{ +#ifdef CONFIG_BLK_DEV_RAM + extern int rd_doload; + extern int rd_prompt; + extern int rd_image_start; + + rd_image_start = image_start; + rd_prompt = prompt; + rd_doload = doload; +#endif +} + +/* + * initial ram disk + */ +__initfunc(static void +setup_initrd(unsigned int start, unsigned int size)) +{ +#ifdef CONFIG_BLK_DEV_INITRD + if (start) { + initrd_start = start; + initrd_end = start + size; + } else { + initrd_start = 0; + initrd_end = 0; + } +#endif +} + +#ifdef CONFIG_ARCH_ACORN +int memc_ctrl_reg; +int number_mfm_drives; +unsigned int vram_size; +#endif + +#ifndef PARAMS_BASE +#define PARAMS_BASE NULL +#endif + +static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } }; +#define ENDIANNESS ((char)endian_test.l) + __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, unsigned long * memory_end_p)) { + struct param_struct *params = (struct param_struct *)PARAMS_BASE; static unsigned char smptrap; - unsigned long memory_end; - char endian = 'l'; - char *from; + unsigned long memory_end = 0; + char *from = NULL; if (smptrap == 1) return; smptrap = 1; +#if defined(CONFIG_ARCH_ARC) + __machine_arch_type = MACH_TYPE_ARCHIMEDES; +#elif defined(CONFIG_ARCH_A5K) + __machine_arch_type = MACH_TYPE_A5K; +#endif + setup_processor(); - from = setup_params(&memory_end); - setup_initrd(params); + init_task.mm->start_code = TASK_SIZE; + init_task.mm->end_code = TASK_SIZE + (unsigned long) &_etext; + init_task.mm->end_data = TASK_SIZE + (unsigned long) &_edata; + init_task.mm->brk = TASK_SIZE + (unsigned long) &_end; + + /* + * Add your machine dependencies here + */ + switch (machine_arch_type) { + case MACH_TYPE_EBSA110: + /* EBSA110 locks if we execute 'wait for interrupt' */ + disable_hlt(); + params = NULL; + break; + + case MACH_TYPE_EBSA285: + if (params) { + ORIG_X = params->u1.s.video_x; + ORIG_Y = params->u1.s.video_y; + ORIG_VIDEO_COLS = params->u1.s.video_num_cols; + ORIG_VIDEO_LINES = params->u1.s.video_num_rows; + } + break; + + case MACH_TYPE_CO285: + { +#if 0 + extern unsigned long boot_memory_end; + extern char boot_command_line[]; + + from = boot_command_line; + memory_end = boot_memory_end; +#endif + params = NULL; + } + break; + + case MACH_TYPE_CATS: + /* CATS must use soft-reboot */ + reboot_setup("s", NULL); + break; + + case MACH_TYPE_NETWINDER: + /* + * to be fixed in a future NeTTrom + */ + if (params->u1.s.page_size == 4096) { + if (params->u1.s.nr_pages != 0x2000 && + params->u1.s.nr_pages != 0x4000) { + printk("Warning: bad NeTTrom parameters detected, using defaults\n"); + /* + * This stuff doesn't appear to be initialised + * properly by NeTTrom 2.0.6 and 2.0.7 + */ + params->u1.s.nr_pages = 0x2000; /* 32MB */ + params->u1.s.ramdisk_size = 0; + params->u1.s.flags = FLAG_READONLY; + params->u1.s.initrd_start = 0; + params->u1.s.initrd_size = 0; + params->u1.s.rd_start = 0; + params->u1.s.video_x = 0; + params->u1.s.video_y = 0; + params->u1.s.video_num_cols = 80; + params->u1.s.video_num_rows = 30; + } + } else { + printk("Warning: no NeTTrom parameter page detected, using " + "compiled-in settings\n"); + params = NULL; + } + break; + + default: + break; + } + + if (params) { + memory_end = params->u1.s.page_size * + params->u1.s.nr_pages; + + ROOT_DEV = to_kdev_t(params->u1.s.rootdev); + + setup_ram((params->u1.s.flags & FLAG_RDLOAD) == 0, + (params->u1.s.flags & FLAG_RDPROMPT) == 0, + params->u1.s.rd_start); + + setup_initrd(params->u1.s.initrd_start, + params->u1.s.initrd_size); + + if (!(params->u1.s.flags & FLAG_READONLY)) + root_mountflags &= ~MS_RDONLY; + +#ifdef CONFIG_ARCH_ACORN +#ifdef CONFIG_ARCH_RPC + { + extern void init_dram_banks(struct param_struct *); + init_dram_banks(params); + } +#endif + + memc_ctrl_reg = params->u1.s.memc_control_reg; + number_mfm_drives = (params->u1.s.adfsdrives >> 3) & 3; + vram_size = 0; + + switch (params->u1.s.pages_in_vram) { + case 512: + vram_size += PAGE_SIZE * 256; + case 256: + vram_size += PAGE_SIZE * 256; + default: + break; + } + + memory_end -= vram_size; +#endif + + from = params->commandline; + } else { + ROOT_DEV = 0x00ff; + + setup_ram(1, 1, 0); + setup_initrd(0, 0); + } + + if (!memory_end) + memory_end = MEM_SIZE; + + if (!from) + from = default_command_line; + +#ifdef CONFIG_NWFPE + fpe_init(); +#endif /* Save unparsed command line copy for /proc/cmdline */ memcpy(saved_command_line, from, COMMAND_LINE_SIZE); saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; setup_mem(from, memory_start_p, &memory_end); - check_initrd(*memory_start_p, memory_end); - init_task.mm->start_code = TASK_SIZE; - init_task.mm->end_code = TASK_SIZE + (unsigned long) &_etext; - init_task.mm->end_data = TASK_SIZE + (unsigned long) &_edata; - init_task.mm->brk = TASK_SIZE + (unsigned long) &_end; + memory_end += PAGE_OFFSET; - *cmdline_p = command_line; - *memory_end_p = memory_end; + check_initrd(*memory_start_p, memory_end); - sprintf(system_utsname.machine, "%s%c", armidlist[armidindex].arch_vsn, endian); - sprintf(elf_platform, "%s%c", armidlist[armidindex].elf_vsn, endian); + sprintf(system_utsname.machine, "%s%c", armidlist[armidindex].arch_vsn, ENDIANNESS); + sprintf(elf_platform, "%s%c", armidlist[armidindex].elf_vsn, ENDIANNESS); #ifdef CONFIG_VT #if defined(CONFIG_VGA_CONSOLE) @@ -385,43 +451,26 @@ conswitchp = &dummy_con; #endif #endif + + *cmdline_p = command_line; + *memory_end_p = memory_end; } -static const struct { - char *machine_name; - char *bus_name; -} machine_desc[] = { - { "DEC-EBSA110", "DEC" }, - { "Acorn-RiscPC", "Acorn" }, - { "Nexus-NexusPCI", "PCI" }, - { "DEC-EBSA285", "PCI" }, - { "Corel-Netwinder", "PCI/ISA" }, - { "Chalice-CATS", "PCI" }, - { "unknown-TBOX", "PCI" } +static const char *machine_desc[] = { + "EBSA110", + "Acorn-RiscPC", + "unknown", + "Nexus-FTV/PCI", + "EBSA285", + "Corel-NetWinder", + "Chalice-CATS", + "unknown-TBOX", + "co-EBSA285", + "CL-PS7110", + "Acorn-Archimedes", + "Acorn-A5000" }; -#if defined(CONFIG_ARCH_ARC) -#define HARDWARE "Acorn-Archimedes" -#define IO_BUS "Acorn" -#elif defined(CONFIG_ARCH_A5K) -#define HARDWARE "Acorn-A5000" -#define IO_BUS "Acorn" -#endif - -#if defined(CONFIG_CPU_ARM2) -#define OPTIMISATION "ARM2" -#elif defined(CONFIG_CPU_ARM3) -#define OPTIMISATION "ARM3" -#elif defined(CONFIG_CPU_ARM6) -#define OPTIMISATION "ARM6" -#elif defined(CONFIG_CPU_ARM7) -#define OPTIMISATION "ARM7" -#elif defined(CONFIG_CPU_SA110) -#define OPTIMISATION "StrongARM" -#else -#define OPTIMISATION "unknown" -#endif - int get_cpuinfo(char * buffer) { int len; @@ -429,25 +478,12 @@ len = sprintf(buffer, "Processor\t: %s %s rev %d\n" "BogoMips\t: %lu.%02lu\n" - "Hardware\t: %s\n" - "Optimisation\t: %s\n" - "IO Bus\t\t: %s\n", + "Hardware\t: %s\n", armidlist[armidindex].manu, armidlist[armidindex].name, (int)processor_id & 15, (loops_per_sec+2500) / 500000, ((loops_per_sec+2500) / 5000) % 100, -#ifdef HARDWARE - HARDWARE, -#else - machine_desc[machine_type].machine_name, -#endif - OPTIMISATION, -#ifdef IO_BUS - IO_BUS -#else - machine_desc[machine_type].bus_name -#endif - ); + machine_desc[machine_arch_type]); return len; } diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/signal.c linux/arch/arm/kernel/signal.c --- v2.3.6/linux/arch/arm/kernel/signal.c Thu Oct 8 09:32:22 1998 +++ linux/arch/arm/kernel/signal.c Thu Jun 17 01:11:35 1999 @@ -28,7 +28,7 @@ asmlinkage int sys_wait4(pid_t pid, unsigned long * stat_addr, int options, unsigned long *ru); -asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs); +asmlinkage int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall); extern int ptrace_cancel_bpt (struct task_struct *); extern int ptrace_set_bpt (struct task_struct *); @@ -50,7 +50,7 @@ while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(&saveset, regs)) + if (do_signal(&saveset, regs, 0)) return regs->ARM_r0; } } @@ -78,7 +78,7 @@ while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(&saveset, regs)) + if (do_signal(&saveset, regs, 0)) return regs->ARM_r0; } } @@ -158,12 +158,8 @@ #ifdef CONFIG_CPU_32 err |= __get_user(regs->ARM_cpsr, &sc->arm_cpsr); #endif - if (!valid_user_regs(regs)) - return 1; - /* send SIGTRAP if we're single-stepping */ - if (ptrace_cancel_bpt (current)) - send_sig (SIGTRAP, current, 1); + err |= !valid_user_regs(regs); return err; } @@ -173,6 +169,14 @@ struct sigframe *frame; sigset_t set; + /* + * Since we stacked the signal on a word boundary, + * then 'sp' should be word aligned here. If it's + * not, then the user is trying to mess with us. + */ + if (regs->ARM_sp & 3) + goto badframe; + frame = (struct sigframe *)regs->ARM_sp; if (verify_area(VERIFY_READ, frame, sizeof (*frame))) @@ -192,6 +196,10 @@ if (restore_sigcontext(regs, &frame->sc)) goto badframe; + /* Send SIGTRAP if we're single-stepping */ + if (ptrace_cancel_bpt (current)) + send_sig(SIGTRAP, current, 1); + return regs->ARM_r0; badframe: @@ -204,6 +212,14 @@ struct rt_sigframe *frame; sigset_t set; + /* + * Since we stacked the signal on a word boundary, + * then 'sp' should be word aligned here. If it's + * not, then the user is trying to mess with us. + */ + if (regs->ARM_sp & 3) + goto badframe; + frame = (struct rt_sigframe *)regs->ARM_sp; if (verify_area(VERIFY_READ, frame, sizeof (*frame))) @@ -220,6 +236,10 @@ if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) goto badframe; + /* Send SIGTRAP if we're single-stepping */ + if (ptrace_cancel_bpt (current)) + send_sig(SIGTRAP, current, 1); + return regs->ARM_r0; badframe: @@ -260,6 +280,26 @@ return err; } +static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, + unsigned long framesize) +{ + unsigned long sp = regs->ARM_sp; + + /* + * This is the X/Open sanctioned signal stack switching. + */ + if ((ka->sa.sa_flags & SA_ONSTACK) && ! on_sig_stack(sp)) + sp = current->sas_ss_sp + current->sas_ss_size; + + /* + * No matter what happens, 'sp' must be word + * aligned otherwise nasty things could happen + */ + sp &= ~3; + + return (void *)(sp - framesize); +} + static void setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *regs) { @@ -267,9 +307,9 @@ unsigned long retcode; int err = 0; - frame = (struct sigframe *)regs->ARM_sp - 1; + frame = get_sigframe(ka, regs, sizeof(*frame)); - if (!access_ok(VERIFT_WRITE, frame, sizeof (*frame))) + if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) goto segv_and_exit; err |= setup_sigcontext(&frame->sc, /*&frame->fpstate,*/ regs, set->sig[0]); @@ -286,7 +326,7 @@ } else { retcode = (unsigned long)&frame->retcode; err |= __put_user(SWI_SYS_SIGRETURN, &frame->retcode); - __flush_entry_to_ram (&frame->retcode); + flush_icache_range(retcode, retcode + 4); } if (err) @@ -299,6 +339,11 @@ regs->ARM_sp = (unsigned long)frame; regs->ARM_lr = retcode; regs->ARM_pc = (unsigned long)ka->sa.sa_handler; +#if defined(CONFIG_CPU_32) + /* Maybe we need to deliver a 32-bit signal to a 26-bit task. */ + if (ka->sa.sa_flags & SA_THIRTYTWO) + regs->ARM_cpsr = USR_MODE; +#endif if (valid_user_regs(regs)) return; @@ -315,7 +360,8 @@ unsigned long retcode; int err = 0; - frame = (struct rt_sigframe *)regs->ARM_sp - 1; + frame = get_sigframe(ka, regs, sizeof(struct rt_sigframe)); + if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) goto segv_and_exit; @@ -337,7 +383,7 @@ } else { retcode = (unsigned long)&frame->retcode; err |= __put_user(SWI_SYS_RT_SIGRETURN, &frame->retcode); - __flush_entry_to_ram (&frame->retcode); + flush_icache_range(retcode, retcode + 4); } if (err) @@ -350,6 +396,11 @@ regs->ARM_sp = (unsigned long)frame; regs->ARM_lr = retcode; regs->ARM_pc = (unsigned long)ka->sa.sa_handler; +#if defined(CONFIG_CPU_32) + /* Maybe we need to deliver a 32-bit signal to a 26-bit task. */ + if (ka->sa.sa_flags & SA_THIRTYTWO) + regs->ARM_cpsr = USR_MODE; +#endif if (valid_user_regs(regs)) return; @@ -393,18 +444,25 @@ * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ -asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) +asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) { - unsigned long instr, *pc = (unsigned long *)(instruction_pointer(regs)-4); struct k_sigaction *ka; siginfo_t info; - int single_stepping, swi_instr; + int single_stepping; + + /* + * We want the common case to go fast, which + * is why we may in certain cases get here from + * kernel mode. Just return without doing anything + * if so. + */ + if (!user_mode(regs)) + return 0; if (!oldset) oldset = ¤t->blocked; single_stepping = ptrace_cancel_bpt (current); - swi_instr = (!get_user (instr, pc) && (instr & 0x0f000000) == 0x0f000000); for (;;) { unsigned long signr; @@ -503,7 +561,7 @@ } /* Are we from a system call? */ - if (swi_instr) { + if (syscall) { switch (regs->ARM_r0) { case -ERESTARTNOHAND: regs->ARM_r0 = -EINTR; @@ -527,7 +585,7 @@ return 1; } - if (swi_instr && + if (syscall && (regs->ARM_r0 == -ERESTARTNOHAND || regs->ARM_r0 == -ERESTARTSYS || regs->ARM_r0 == -ERESTARTNOINTR)) { diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/sys_arm.c linux/arch/arm/kernel/sys_arm.c --- v2.3.6/linux/arch/arm/kernel/sys_arm.c Sat May 8 11:14:01 1999 +++ linux/arch/arm/kernel/sys_arm.c Thu Jun 17 01:11:35 1999 @@ -223,13 +223,7 @@ */ asmlinkage int sys_fork(struct pt_regs *regs) { - int ret; - - lock_kernel(); - ret = do_fork(SIGCHLD, regs->ARM_sp, regs); - unlock_kernel(); - - return ret; + return do_fork(SIGCHLD, regs->ARM_sp, regs); } /* Clone a task - this clones the calling program thread. @@ -237,14 +231,14 @@ */ asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, struct pt_regs *regs) { - int ret; - - lock_kernel(); if (!newsp) newsp = regs->ARM_sp; - ret = do_fork(clone_flags, newsp, regs); - unlock_kernel(); - return ret; + return do_fork(clone_flags, newsp, regs); +} + +asmlinkage int sys_vfork(struct pt_regs *regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs); } /* sys_execve() executes a new program. diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/time.c linux/arch/arm/kernel/time.c --- v2.3.6/linux/arch/arm/kernel/time.c Thu Mar 11 23:24:55 1999 +++ linux/arch/arm/kernel/time.c Thu Jun 17 01:11:35 1999 @@ -129,27 +129,12 @@ time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; - sti (); + sti(); } -/* - * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "do_timer()" routine every clocktick. - */ -static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - if (reset_timer ()) - do_timer(regs); - - update_rtc (); -} - -static struct irqaction irqtimer = { timer_interrupt, 0, 0, "timer", NULL, NULL}; - __initfunc(void time_init(void)) { - xtime.tv_sec = setup_timer(); xtime.tv_usec = 0; - setup_arm_irq(IRQ_TIMER, &irqtimer); + setup_timer(); } diff -u --recursive --new-file v2.3.6/linux/arch/arm/kernel/traps.c linux/arch/arm/kernel/traps.c --- v2.3.6/linux/arch/arm/kernel/traps.c Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/kernel/traps.c Thu Jun 17 01:11:35 1999 @@ -24,7 +24,6 @@ #include #include -extern void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret); extern void c_backtrace (unsigned long fp, int pmode); extern int ptrace_cancel_bpt (struct task_struct *); @@ -45,16 +44,17 @@ int kstack_depth_to_print = 200; -static int verify_stack_pointer (unsigned long stackptr, int size) +/* + * Stack pointers should always be within the kernels view of + * physical memory. If it is not there, then we can't dump + * out any information relating to the stack. + */ +static int verify_stack(unsigned long sp) { -#ifdef CONFIG_CPU_26 - if (stackptr < 0x02048000 || stackptr + size > 0x03000000) - return -EFAULT; -#else - if (stackptr < PAGE_OFFSET || stackptr + size > (unsigned long)high_memory) + if (sp < PAGE_OFFSET || sp > (unsigned long)high_memory) return -EFAULT; -#endif - return 0; + + return 0; } /* @@ -90,22 +90,26 @@ static void dump_instr(unsigned long pc, int user) { - unsigned long module_start, module_end; int pmin = -2, pmax = 3, ok = 0; extern char start_kernel, _etext; if (!user) { + unsigned long module_start, module_end; + unsigned long kernel_start, kernel_end; + module_start = VMALLOC_START; module_end = module_start + MODULE_RANGE; - if ((pc >= (unsigned long) &start_kernel) && - (pc <= (unsigned long) &_etext)) { - if (pc + pmin < (unsigned long) &start_kernel) - pmin = ((unsigned long) &start_kernel) - pc; - if (pc + pmax > (unsigned long) &_etext) - pmax = ((unsigned long) &_etext) - pc; + kernel_start = (unsigned long)&start_kernel; + kernel_end = (unsigned long)&_etext; + + if (pc >= kernel_start && pc < kernel_end) { + if (pc + pmin < kernel_start) + pmin = kernel_start - pc; + if (pc + pmax > kernel_end) + pmax = kernel_end - pc; ok = 1; - } else if (pc >= module_start && pc <= module_end) { + } else if (pc >= module_start && pc < module_end) { if (pc + pmin < module_start) pmin = module_start - pc; if (pc + pmax > module_end) @@ -125,119 +129,138 @@ printk ("pc not in code space\n"); } -static void dump_state(char *str, struct pt_regs *regs, int err) +spinlock_t die_lock; + +/* + * This function is protected against re-entrancy. + */ +void die(const char *str, struct pt_regs *regs, int err) { + struct task_struct *tsk = current; + + spin_lock_irq(&die_lock); + console_verbose(); printk("Internal error: %s: %x\n", str, err); printk("CPU: %d\n", smp_processor_id()); show_regs(regs); printk("Process %s (pid: %d, stackpage=%08lx)\n", - current->comm, current->pid, 4096+(unsigned long)current); -} + current->comm, current->pid, 4096+(unsigned long)tsk); -/* - * This function is protected against kernel-mode re-entrancy. If it - * is re-entered it will hang the system since we can't guarantee in - * this case that any of the functions that it calls are safe any more. - * Even the panic function could be a problem, but we'll give it a go. - */ -void die_if_kernel(char *str, struct pt_regs *regs, int err, int ret) -{ - static int died = 0; - unsigned long cstack, sstack, frameptr; - - if (user_mode(regs)) - return; + if (!user_mode(regs)) { + unsigned long sp = (unsigned long)(regs + 1); + unsigned long fp; + int dump_info = 1; + + printk("Stack: "); + if (verify_stack(sp)) { + printk("invalid kernel stack pointer %08lx", sp); + dump_info = 0; + } else if (sp < 4096+(unsigned long)tsk) + printk("kernel stack pointer underflow"); + printk("\n"); + + if (dump_info) + dump_mem(sp - 16, 8192+(unsigned long)tsk); + + dump_info = 1; + + printk("Backtrace: "); + fp = regs->ARM_fp; + if (!fp) { + printk("no frame pointer"); + dump_info = 0; + } else if (verify_stack(fp)) { + printk("invalid frame pointer %08lx", fp); + dump_info = 0; + } else if (fp < 4096+(unsigned long)tsk) + printk("frame pointer underflow"); + printk("\n"); - switch (died) { - case 2: - while (1); - case 1: - died ++; - panic ("die_if_kernel re-entered. Major kernel corruption. Please reboot me!"); - break; - case 0: - died ++; - break; - } + if (dump_info) + c_backtrace(fp, processor_mode(regs)); - dump_state(str, regs, err); + dump_instr(instruction_pointer(regs), 0); + } - cstack = (unsigned long)(regs + 1); - sstack = 4096+(unsigned long)current; + spin_unlock_irq(&die_lock); +} - printk("Stack: "); - if (verify_stack_pointer(cstack, 4)) - printk("invalid kernel stack pointer %08lx", cstack); - else if(cstack > sstack + 4096) - printk("(sp overflow)"); - else if(cstack < sstack) - printk("(sp underflow)"); - printk("\n"); - - dump_mem(cstack - 16, sstack + 4096); - - frameptr = regs->ARM_fp; - if (frameptr) { - if (verify_stack_pointer (frameptr, 4)) - printk ("Backtrace: invalid frame pointer\n"); - else { - printk("Backtrace: \n"); - c_backtrace (frameptr, processor_mode(regs)); - } - } +static void die_if_kernel(const char *str, struct pt_regs *regs, int err) +{ + if (user_mode(regs)) + return; - dump_instr(instruction_pointer(regs), 0); - died = 0; - if (ret != -1) - do_exit (ret); - else { - cli (); - while (1); - } + die(str, regs, err); } -void bad_user_access_alignment (const void *ptr) +void bad_user_access_alignment(const void *ptr) { - void *pc; - __asm__("mov %0, lr\n": "=r" (pc)); - printk (KERN_ERR "bad_user_access_alignment called: ptr = %p, pc = %p\n", ptr, pc); + printk(KERN_ERR "bad user access alignment: ptr = %p, pc = %p\n", ptr, + __builtin_return_address(0)); current->tss.error_code = 0; current->tss.trap_no = 11; - force_sig (SIGBUS, current); -/* die_if_kernel("Oops - bad user access alignment", regs, mode, SIGBUS);*/ + force_sig(SIGBUS, current); +/* die_if_kernel("Oops - bad user access alignment", regs, mode);*/ } -asmlinkage void do_undefinstr (int address, struct pt_regs *regs, int mode) +asmlinkage void do_undefinstr(int address, struct pt_regs *regs, int mode) { +#ifdef CONFIG_DEBUG_USER + printk(KERN_INFO "%s (%d): undefined instruction: pc=%08lx\n", + current->comm, current->pid, instruction_pointer(regs)); +#endif current->tss.error_code = 0; current->tss.trap_no = 6; - force_sig (SIGILL, current); - die_if_kernel("Oops - undefined instruction", regs, mode, SIGILL); + force_sig(SIGILL, current); + die_if_kernel("Oops - undefined instruction", regs, mode); } -asmlinkage void do_excpt (int address, struct pt_regs *regs, int mode) +asmlinkage void do_excpt(int address, struct pt_regs *regs, int mode) { +#ifdef CONFIG_DEBUG_USER + printk(KERN_INFO "%s (%d): address exception: pc=%08lx\n", + current->comm, current->pid, instruction_pointer(regs)); +#endif current->tss.error_code = 0; current->tss.trap_no = 11; - force_sig (SIGBUS, current); - die_if_kernel("Oops - address exception", regs, mode, SIGBUS); + force_sig(SIGBUS, current); + die_if_kernel("Oops - address exception", regs, mode); } asmlinkage void do_unexp_fiq (struct pt_regs *regs) { #ifndef CONFIG_IGNORE_FIQ - printk ("Hmm. Unexpected FIQ received, but trying to continue\n"); - printk ("You may have a hardware problem...\n"); + printk("Hmm. Unexpected FIQ received, but trying to continue\n"); + printk("You may have a hardware problem...\n"); #endif } +/* + * bad_mode handles the impossible case in the vectors. + * If you see one of these, then it's extremely serious, + * and could mean you have buggy hardware. It never + * returns, and never tries to sync. We hope that we + * can dump out some state information... + */ asmlinkage void bad_mode(struct pt_regs *regs, int reason, int proc_mode) { - printk (KERN_CRIT "Bad mode in %s handler detected: mode %s\n", - handler[reason], - processor_modes[proc_mode]); - die_if_kernel ("Oops", regs, 0, -1); + console_verbose(); + + printk(KERN_CRIT "Bad mode in %s handler detected: mode %s\n", + handler[reason], processor_modes[proc_mode]); + + /* + * Dump out the vectors and stub routines + */ + printk(KERN_CRIT "Vectors:\n"); + dump_mem(0, 0x40); + printk(KERN_CRIT "Stubs:\n"); + dump_mem(0x200, 0x4b8); + + die("Oops", regs, 0); + cli(); + while(1); } /* @@ -249,54 +272,85 @@ */ asmlinkage void math_state_restore (void) { - current->used_math = 1; + current->used_math = 1; } -asmlinkage void arm_syscall (int no, struct pt_regs *regs) +asmlinkage int arm_syscall (int no, struct pt_regs *regs) { switch (no) { case 0: /* branch through 0 */ force_sig(SIGSEGV, current); -// if (user_mode(regs)) { -// dump_state("branch through zero", regs, 0); -// if (regs->ARM_fp) -// c_backtrace (regs->ARM_fp, processor_mode(regs)); -// } - die_if_kernel ("branch through zero", regs, 0, SIGSEGV); + die_if_kernel("branch through zero", regs, 0); break; case 1: /* SWI_BREAK_POINT */ regs->ARM_pc -= 4; /* Decrement PC by one instruction */ - ptrace_cancel_bpt (current); - force_sig (SIGTRAP, current); + ptrace_cancel_bpt(current); + force_sig(SIGTRAP, current); + return regs->ARM_r0; + + case 2: /* sys_cacheflush */ +#ifdef CONFIG_CPU_32 + /* r0 = start, r1 = length, r2 = flags */ + processor.u.armv3v4._flush_cache_area(regs->ARM_r0, + regs->ARM_r1, + 1); +#endif break; default: - printk ("[%d] %s: arm syscall %d\n", current->pid, current->comm, no); - force_sig (SIGILL, current); + /* Calls 9f00xx..9f07ff are defined to return -ENOSYS + if not implemented, rather than raising SIGILL. This + way the calling program can gracefully determine whether + a feature is supported. */ + if (no <= 0x7ff) + return -ENOSYS; +#ifdef CONFIG_DEBUG_USER + /* experiance shows that these seem to indicate that + * something catastrophic has happened + */ + printk("[%d] %s: arm syscall %d\n", current->pid, current->comm, no); if (user_mode(regs)) { - show_regs (regs); - c_backtrace (regs->ARM_fp, processor_mode(regs)); + show_regs(regs); + c_backtrace(regs->ARM_fp, processor_mode(regs)); } - die_if_kernel ("Oops", regs, no, SIGILL); +#endif + force_sig(SIGILL, current); + die_if_kernel("Oops", regs, no); break; } + return 0; } asmlinkage void deferred(int n, struct pt_regs *regs) { - dump_state("old system call", regs, n); - force_sig (SIGILL, current); + /* You might think just testing `handler' would be enough, but PER_LINUX + * points it to no_lcall7 to catch undercover SVr4 binaries. Gutted. + */ + if (current->personality != PER_LINUX && current->exec_domain->handler) { + /* Hand it off to iBCS. The extra parameter and consequent type + * forcing is necessary because of the weird ARM calling convention. + */ + void (*handler)(int nr, struct pt_regs *regs) = (void *)current->exec_domain->handler; + (*handler)(n, regs); + return; + } + +#ifdef CONFIG_DEBUG_USER + printk(KERN_ERR "[%d] %s: old system call.\n", current->pid, + current->comm); +#endif + force_sig(SIGILL, current); } asmlinkage void arm_malalignedptr(const char *str, void *pc, volatile void *ptr) { - printk ("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc); + printk("Mal-aligned pointer in %s: %p (PC=%p)\n", str, ptr, pc); } -asmlinkage void arm_invalidptr (const char *function, int size) +asmlinkage void arm_invalidptr(const char *function, int size) { - printk ("Invalid pointer size in %s (PC=%p) size %d\n", + printk("Invalid pointer size in %s (pc=%p) size %d\n", function, __builtin_return_address(0), size); } diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/Makefile linux/arch/arm/lib/Makefile --- v2.3.6/linux/arch/arm/lib/Makefile Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/lib/Makefile Thu Jun 17 01:11:35 1999 @@ -6,14 +6,14 @@ L_TARGET := lib.a L_OBJS := backtrace.o bitops.o checksum.o delay.o io.o memcpy.o \ - system.o string.o uaccess.o + semaphore.o string.o system.o uaccess.o ifeq ($(PROCESSOR),armo) L_OBJS += uaccess-armo.o endif ifdef CONFIG_ARCH_ACORN - L_OBJS += loaders.o ll_char_wr.o io-acorn.o + L_OBJS += loaders.o io-acorn.o ifdef CONFIG_ARCH_A5K L_OBJS += floppydma.o endif @@ -26,12 +26,8 @@ L_OBJS += io-ebsa110.o endif -ifeq ($(MACHINE),vnc) - L_OBJS += io-ebsa285.o -endif - -ifeq ($(MACHINE),ebsa285) - L_OBJS += io-ebsa285.o +ifeq ($(MACHINE),footbridge) + L_OBJS += io-footbridge.o endif include $(TOPDIR)/Rules.make @@ -45,10 +41,4 @@ checksum.o: constants.h %.o: %.S -ifneq ($(CONFIG_BINUTILS_NEW),y) - $(CC) $(CFLAGS) -D__ASSEMBLY__ -E $< | tr ';$$' '\n#' > ..tmp.$<.s - $(CC) $(CFLAGS:-pipe=) -c -o $@ ..tmp.$<.s - $(RM) ..tmp.$<.s -else $(CC) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $< -endif diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/checksum.S linux/arch/arm/lib/checksum.S --- v2.3.6/linux/arch/arm/lib/checksum.S Sun Sep 6 10:44:47 1998 +++ linux/arch/arm/lib/checksum.S Thu Jun 17 01:11:35 1999 @@ -520,13 +520,13 @@ LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc}) ldr r4, [r0], #4 tst r2, #2 - beq Lexit + beq Lexit_r4 adcs r3, r3, r4, lsl #16 strb r4, [r1], #1 mov r4, r4, lsr #8 strb r4, [r1], #1 mov r4, r4, lsr #8 - b Lexit + b Lexit_r4 Ltoo_small: teq r2, #0 LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc}) @@ -538,10 +538,12 @@ adds r3, r3, ip strb ip, [r1], #1 strb r8, [r1], #1 -Lexit: tst r2, #1 -Ltoo_small1: ldrneb ip, [r0], #1 - strneb ip, [r1], #1 - adcnes r3, r3, ip + tst r2, #1 +Ltoo_small1: ldrneb r4, [r0], #1 +Lexit_r4: tst r2, #1 + strneb r4, [r1], #1 + andne r4, r4, #255 + adcnes r3, r3, r4 adcs r0, r3, #0 LOADREGS(ea,fp,{r4 - r8, fp, sp, pc}) @@ -598,13 +600,13 @@ adceq r0, r3, #0 LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc}) tst r2, #2 - beq Lexit + beq Lexit_r4 adcs r3, r3, r4, lsl #16 strb r4, [r1], #1 mov r4, r4, lsr #8 strb r4, [r1], #1 mov r4, r4, lsr #8 - b Lexit + b Lexit_r4 Lsrc2_aligned: mov r4, r4, lsr #16 adds r3, r3, #0 @@ -650,13 +652,13 @@ adceq r0, r3, #0 LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc}) tst r2, #2 - beq Lexit + beq Lexit_r4 adcs r3, r3, r4, lsl #16 strb r4, [r1], #1 mov r4, r4, lsr #8 strb r4, [r1], #1 ldrb r4, [r0], #1 - b Lexit + b Lexit_r4 Lsrc3_aligned: mov r4, r4, lsr #24 adds r3, r3, #0 @@ -702,14 +704,14 @@ adceq r0, r3, #0 LOADREGS(eqea,fp,{r4 - r8, fp, sp, pc}) tst r2, #2 - beq Lexit + beq Lexit_r4 adcs r3, r3, r4, lsl #16 strb r4, [r1], #1 ldr r4, [r0], #4 strb r4, [r1], #1 adcs r3, r3, r4, lsl #24 mov r4, r4, lsr #8 - b Lexit + b Lexit_r4 ENTRY(__csum_ipv6_magic) stmfd sp!, {lr} diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/floppydma.S linux/arch/arm/lib/floppydma.S --- v2.3.6/linux/arch/arm/lib/floppydma.S Tue Jan 20 16:39:41 1998 +++ linux/arch/arm/lib/floppydma.S Thu Jun 17 01:11:35 1999 @@ -26,32 +26,3 @@ strb r12, [r11, #-4] subs pc, lr, #4 SYMBOL_NAME(floppy_fiqout_end): - -@ Params: -@ r0 = length -@ r1 = address -@ r2 = floppy port -@ Puts these into R9_fiq, R10_fiq, R11_fiq -ENTRY(floppy_fiqsetup) - mov ip, sp - stmfd sp!, {fp, ip, lr, pc} - sub fp, ip, #4 - MODE(r3,ip,I_BIT|F_BIT|DEFAULT_FIQ) @ disable FIQs, IRQs, FIQ mode - mov r0, r0 - mov r9, r0 - mov r10, r1 - mov r11, r2 - RESTOREMODE(r3) @ back to normal - mov r0, r0 - LOADREGS(ea,fp,{fp, sp, pc}) - -ENTRY(floppy_fiqresidual) - mov ip, sp - stmfd sp!, {fp, ip, lr, pc} - sub fp, ip, #4 - MODE(r3,ip,I_BIT|F_BIT|DEFAULT_FIQ) @ disable FIQs, IRQs, FIQ mode - mov r0, r0 - mov r0, r9 - RESTOREMODE(r3) - mov r0, r0 - LOADREGS(ea,fp,{fp, sp, pc}) diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/getconsdata.c linux/arch/arm/lib/getconsdata.c --- v2.3.6/linux/arch/arm/lib/getconsdata.c Sun Sep 6 10:44:47 1998 +++ linux/arch/arm/lib/getconsdata.c Thu Jun 17 01:11:35 1999 @@ -67,6 +67,23 @@ unsigned long PAGE_CLEAN = _PAGE_CLEAN; #endif +#ifdef PTE_TYPE_SMALL +unsigned long HPTE_TYPE_SMALL = PTE_TYPE_SMALL; +unsigned long HPTE_AP_READ = PTE_AP_READ; +unsigned long HPTE_AP_WRITE = PTE_AP_WRITE; +#endif + +#ifdef L_PTE_PRESENT +unsigned long LPTE_PRESENT = L_PTE_PRESENT; +unsigned long LPTE_YOUNG = L_PTE_YOUNG; +unsigned long LPTE_BUFFERABLE = L_PTE_BUFFERABLE; +unsigned long LPTE_CACHEABLE = L_PTE_CACHEABLE; +unsigned long LPTE_USER = L_PTE_USER; +unsigned long LPTE_WRITE = L_PTE_WRITE; +unsigned long LPTE_EXEC = L_PTE_EXEC; +unsigned long LPTE_DIRTY = L_PTE_DIRTY; +#endif + unsigned long KSWI_BASE = 0x900000; unsigned long KSWI_SYS_BASE = 0x9f0000; unsigned long SYS_ERROR0 = 0x9f0000; diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/io-acorn.S linux/arch/arm/lib/io-acorn.S --- v2.3.6/linux/arch/arm/lib/io-acorn.S Sun Sep 6 10:44:47 1998 +++ linux/arch/arm/lib/io-acorn.S Thu Jun 17 01:11:35 1999 @@ -11,50 +11,514 @@ .text .align -#define OUT(reg) \ - mov r8, reg, lsl $16 ;\ - orr r8, r8, r8, lsr $16 ;\ - str r8, [r3, r0, lsl $2] ;\ - mov r8, reg, lsr $16 ;\ - orr r8, r8, r8, lsl $16 ;\ - str r8, [r3, r0, lsl $2] - -#define IN(reg) \ - ldr reg, [r0] ;\ - and reg, reg, ip ;\ - ldr lr, [r0] ;\ - orr reg, reg, lr, lsl $16 - - .equ pcio_base_high, PCIO_BASE & 0xff000000 - .equ pcio_base_low, PCIO_BASE & 0x00ff0000 - .equ io_base_high, IO_BASE & 0xff000000 - .equ io_base_low, IO_BASE & 0x00ff0000 - - .equ addr_io_diff_hi, pcio_base_high - io_base_high - .equ addr_io_diff_lo, pcio_base_low - io_base_low - - .macro addr reg, off - tst \off, #0x80000000 - .if addr_io_diff_hi - movne \reg, #IO_BASE - moveq \reg, #pcio_base_high - .if pcio_base_low - addeq \reg, \reg, #pcio_base_low - .endif - .else - mov \reg, #IO_BASE - addeq \reg, \reg, #addr_io_diff_lo - .endif + .equ diff_pcio_base, PCIO_BASE - IO_BASE + + .macro outw2 rd + mov r8, \rd, lsl #16 + orr r8, r8, r8, lsr #16 + str r8, [r3, r0, lsl #2] + mov r8, \rd, lsr #16 + orr r8, r8, r8, lsl #16 + str r8, [r3, r0, lsl #2] + .endm + + .macro inw2 rd, mask, temp + ldr \rd, [r0] + and \rd, \rd, \mask + ldr \temp, [r0] + orr \rd, \rd, \temp, lsl #16 + .endm + + .macro addr rd + tst \rd, #0x80000000 + mov \rd, \rd, lsl #2 + add \rd, \rd, #IO_BASE + addeq \rd, \rd, #diff_pcio_base .endm -@ Purpose: read a block of data from a hardware register to memory. -@ Proto : insw(int from_port, void *to, int len_in_words); -@ Proto : inswb(int from_port, void *to, int len_in_bytes); -@ Notes : increment to +.iosw_bad_align_msg: + .ascii "insw: bad buffer alignment (%p), called from %08lX\n\0" +.iosl_warning: + .ascii "<4>insl/outsl not implemented, called from %08lX\0" + .align + +/* + * These make no sense on Acorn machines. + * Print a warning message. + */ +ENTRY(insl) +ENTRY(outsl) + adr r0, .iosl_warning + mov r1, lr + b SYMBOL_NAME(printk) + +.iosw_bad_alignment: + adr r0, .iosw_bad_align_msg + mov r2, lr + b SYMBOL_NAME(panic) + + +/* Purpose: read a block of data from a hardware register to memory. + * Proto : void insw(int from_port, void *to, int len_in_words); + * Notes : increment to, 'to' must be 16-bit aligned + */ + +.insw_align: tst r1, #1 + bne .iosw_bad_alignment + + ldr r3, [r0] + strb r3, [r1], #1 + mov r3, r3, lsr #8 + strb r3, [r1], #1 + + subs r2, r2, #1 + bne .insw_aligned ENTRY(insw) + teq r2, #0 + RETINSTR(moveq,pc,lr) + addr r0 + tst r1, #3 + bne .insw_align + +.insw_aligned: mov ip, #0xff + orr ip, ip, ip, lsl #8 + stmfd sp!, {r4, r5, r6, lr} + + subs r2, r2, #8 + bmi .no_insw_8 + +.insw_8_lp: ldr r3, [r0] + and r3, r3, ip + ldr r4, [r0] + orr r3, r3, r4, lsl #16 + + ldr r4, [r0] + and r4, r4, ip + ldr r5, [r0] + orr r4, r4, r5, lsl #16 + + ldr r5, [r0] + and r5, r5, ip + ldr r6, [r0] + orr r5, r5, r6, lsl #16 + + ldr r6, [r0] + and r6, r6, ip + ldr lr, [r0] + orr r6, r6, lr, lsl #16 + + stmia r1!, {r3 - r6} + subs r2, r2, #8 + bpl .insw_8_lp + tst r2, #7 + LOADREGS(eqfd, sp!, {r4, r5, r6, pc}) + +.no_insw_8: tst r2, #4 + beq .no_insw_4 + + ldr r3, [r0] + and r3, r3, ip + ldr r4, [r0] + orr r3, r3, r4, lsl #16 + + ldr r4, [r0] + and r4, r4, ip + ldr r5, [r0] + orr r4, r4, r5, lsl #16 + + stmia r1!, {r3, r4} + +.no_insw_4: tst r2, #2 + beq .no_insw_2 + + ldr r3, [r0] + and r3, r3, ip + ldr r4, [r0] + orr r3, r3, r4, lsl #16 + + str r3, [r1], #4 + +.no_insw_2: tst r2, #1 + ldrne r3, [r0] + strneb r3, [r1], #1 + movne r3, r3, lsr #8 + strneb r3, [r1] + LOADREGS(fd, sp!, {r4, r5, r6, pc}) + +@ Purpose: write a block of data from memory to a hardware register. +@ Proto : outsw(int to_reg, void *from, int len_in_words); +@ Notes : increments from + +.outsw_align: tst r1, #1 + bne .iosw_bad_alignment + + add r1, r1, #2 + + ldr r3, [r1, #-4] + mov r3, r3, lsr #16 + orr r3, r3, r3, lsl #16 + str r3, [r0] + subs r2, r2, #1 + bne .outsw_aligned + +ENTRY(outsw) + teq r2, #0 + RETINSTR(moveq,pc,lr) + addr r0 + tst r1, #3 + bne .outsw_align + +.outsw_aligned: stmfd sp!, {r4, r5, r6, lr} + + subs r2, r2, #8 + bmi .no_outsw_8 +.outsw_8_lp: ldmia r1!, {r3, r4, r5, r6} + + mov ip, r3, lsl #16 + orr ip, ip, ip, lsr #16 + str ip, [r0] + + mov ip, r3, lsr #16 + orr ip, ip, ip, lsl #16 + str ip, [r0] + + mov ip, r4, lsl #16 + orr ip, ip, ip, lsr #16 + str ip, [r0] + + mov ip, r4, lsr #16 + orr ip, ip, ip, lsl #16 + str ip, [r0] + + mov ip, r5, lsl #16 + orr ip, ip, ip, lsr #16 + str ip, [r0] + + mov ip, r5, lsr #16 + orr ip, ip, ip, lsl #16 + str ip, [r0] + + mov ip, r6, lsl #16 + orr ip, ip, ip, lsr #16 + str ip, [r0] + + mov ip, r6, lsr #16 + orr ip, ip, ip, lsl #16 + str ip, [r0] + + subs r2, r2, #8 + bpl .outsw_8_lp + tst r2, #7 + LOADREGS(eqfd, sp!, {r4, r5, r6, pc}) + +.no_outsw_8: tst r2, #4 + beq .no_outsw_4 + + ldmia r1!, {r3, r4} + + mov ip, r3, lsl #16 + orr ip, ip, ip, lsr #16 + str ip, [r0] + + mov ip, r3, lsr #16 + orr ip, ip, ip, lsl #16 + str ip, [r0] + + mov ip, r4, lsl #16 + orr ip, ip, ip, lsr #16 + str ip, [r0] + + mov ip, r4, lsr #16 + orr ip, ip, ip, lsl #16 + str ip, [r0] + +.no_outsw_4: tst r2, #2 + beq .no_outsw_2 + + ldr r3, [r1], #4 + + mov ip, r3, lsl #16 + orr ip, ip, ip, lsr #16 + str ip, [r0] + + mov ip, r3, lsr #16 + orr ip, ip, ip, lsl #16 + str ip, [r0] + +.no_outsw_2: tst r2, #1 + + ldrne r3, [r1] + + movne ip, r3, lsl #16 + orrne ip, ip, ip, lsr #16 + strne ip, [r0] + + LOADREGS(fd, sp!, {r4, r5, r6, pc}) + +.insb_align: rsb ip, ip, #4 + cmp ip, r2 + movgt ip, r2 + cmp ip, #2 + ldrb r3, [r0] + strb r3, [r1], #1 + ldrgeb r3, [r0] + strgeb r3, [r1], #1 + ldrgtb r3, [r0] + strgtb r3, [r1], #1 + subs r2, r2, ip + bne .insb_aligned + +ENTRY(insb) + teq r2, #0 + moveq pc, lr + addr r0 + ands ip, r1, #3 + bne .insb_align + +.insb_aligned: stmfd sp!, {r4 - r6, lr} + + subs r2, r2, #16 + bmi .insb_no_16 + +.insb_16_lp: ldrb r3, [r0] + ldrb r4, [r0] + orr r3, r3, r4, lsl #8 + ldrb r4, [r0] + orr r3, r3, r4, lsl #16 + ldrb r4, [r0] + orr r3, r3, r4, lsl #24 + ldrb r4, [r0] + ldrb r5, [r0] + orr r4, r4, r5, lsl #8 + ldrb r5, [r0] + orr r4, r4, r5, lsl #16 + ldrb r5, [r0] + orr r4, r4, r5, lsl #24 + ldrb r5, [r0] + ldrb r6, [r0] + orr r5, r5, r6, lsl #8 + ldrb r6, [r0] + orr r5, r5, r6, lsl #16 + ldrb r6, [r0] + orr r5, r5, r6, lsl #24 + ldrb r6, [r0] + ldrb ip, [r0] + orr r6, r6, ip, lsl #8 + ldrb ip, [r0] + orr r6, r6, ip, lsl #16 + ldrb ip, [r0] + orr r6, r6, ip, lsl #24 + stmia r1!, {r3 - r6} + subs r2, r2, #16 + bpl .insb_16_lp + + tst r2, #15 + LOADREGS(eqfd, sp!, {r4 - r6, pc}) + +.insb_no_16: tst r2, #8 + beq .insb_no_8 + + ldrb r3, [r0] + ldrb r4, [r0] + orr r3, r3, r4, lsl #8 + ldrb r4, [r0] + orr r3, r3, r4, lsl #16 + ldrb r4, [r0] + orr r3, r3, r4, lsl #24 + ldrb r4, [r0] + ldrb r5, [r0] + orr r4, r4, r5, lsl #8 + ldrb r5, [r0] + orr r4, r4, r5, lsl #16 + ldrb r5, [r0] + orr r4, r4, r5, lsl #24 + stmia r1!, {r3, r4} + +.insb_no_8: tst r2, #4 + bne .insb_no_4 + + ldrb r3, [r0] + ldrb r4, [r0] + orr r3, r3, r4, lsl #8 + ldrb r4, [r0] + orr r3, r3, r4, lsl #16 + ldrb r4, [r0] + orr r3, r3, r4, lsl #24 + str r3, [r1], #4 + +.insb_no_4: ands r2, r2, #3 + LOADREGS(eqfd, sp!, {r4 - r6, pc}) + cmp r2, #2 + ldrb r3, [r0] + strb r3, [r1], #1 + ldrgeb r3, [r0] + strgeb r3, [r1], #1 + ldrgtb r3, [r0] + strgtb r3, [r1] + LOADREGS(fd, sp!, {r4 - r6, pc}) + + + +.outsb_align: rsb ip, ip, #4 + cmp ip, r2 + mov ip, r2 + cmp ip, #2 + ldrb r3, [r1], #1 + strb r3, [r0] + ldrgeb r3, [r1], #1 + strgeb r3, [r0] + ldrgtb r3, [r1], #1 + strgtb r3, [r0] + subs r2, r2, ip + bne .outsb_aligned + +ENTRY(outsb) + teq r2, #0 + moveq pc, lr + addr r0 + ands ip, r1, #3 + bne .outsb_align + +.outsb_aligned: stmfd sp!, {r4 - r6, lr} + + subs r2, r2, #16 + bmi .outsb_no_16 + +.outsb_16_lp: ldmia r1!, {r3 - r6} + strb r3, [r0] + mov r3, r3, lsr #8 + strb r3, [r0] + mov r3, r3, lsr #8 + strb r3, [r0] + mov r3, r3, lsr #8 + strb r3, [r0] + + strb r4, [r0] + mov r4, r4, lsr #8 + strb r4, [r0] + mov r4, r4, lsr #8 + strb r4, [r0] + mov r4, r4, lsr #8 + strb r4, [r0] + + strb r5, [r0] + mov r5, r5, lsr #8 + strb r5, [r0] + mov r5, r5, lsr #8 + strb r5, [r0] + mov r5, r5, lsr #8 + strb r5, [r0] + + strb r6, [r0] + mov r6, r6, lsr #8 + strb r6, [r0] + mov r6, r6, lsr #8 + strb r6, [r0] + mov r6, r6, lsr #8 + strb r6, [r0] + subs r2, r2, #16 + bpl .outsb_16_lp + + tst r2, #15 + LOADREGS(eqfd, sp!, {r4 - r6, pc}) + +.outsb_no_16: tst r2, #8 + beq .outsb_no_8 + + ldmia r1, {r3, r4} + strb r3, [r0] + mov r3, r3, lsr #8 + strb r3, [r0] + mov r3, r3, lsr #8 + strb r3, [r0] + mov r3, r3, lsr #8 + strb r3, [r0] + + strb r4, [r0] + mov r4, r4, lsr #8 + strb r4, [r0] + mov r4, r4, lsr #8 + strb r4, [r0] + mov r4, r4, lsr #8 + strb r4, [r0] + +.outsb_no_8: tst r2, #4 + bne .outsb_no_4 + + ldr r3, [r1], #4 + strb r3, [r0] + mov r3, r3, lsr #8 + strb r3, [r0] + mov r3, r3, lsr #8 + strb r3, [r0] + mov r3, r3, lsr #8 + strb r3, [r0] + +.outsb_no_4: ands r2, r2, #3 + LOADREGS(eqfd, sp!, {r4 - r6, pc}) + cmp r2, #2 + ldrb r3, [r1], #1 + strb r3, [r0] + ldrgeb r3, [r1], #1 + strgeb r3, [r0] + ldrgtb r3, [r1] + strgtb r3, [r0] + LOADREGS(fd, sp!, {r4 - r6, pc}) + + + + +@ Purpose: write a memc register +@ Proto : void memc_write(int register, int value); +@ Returns: nothing + +#if defined(CONFIG_CPU_26) +ENTRY(memc_write) + cmp r0, #7 + RETINSTR(movgt,pc,lr) + mov r0, r0, lsl #17 + mov r1, r1, lsl #15 + mov r1, r1, lsr #17 + orr r0, r0, r1, lsl #2 + add r0, r0, #0x03600000 + strb r0, [r0] + RETINSTR(mov,pc,lr) +#define CPSR2SPSR(rt) +#else +#define CPSR2SPSR(rt) \ + mrs rt, cpsr; \ + msr spsr, rt +#endif + +@ Purpose: call an expansion card loader to read bytes. +@ Proto : char read_loader(int offset, char *card_base, char *loader); +@ Returns: byte read + +ENTRY(ecard_loader_read) + stmfd sp!, {r4 - r12, lr} + mov r11, r1 + mov r1, r0 + CPSR2SPSR(r0) + mov lr, pc + mov pc, r2 + LOADREGS(fd, sp!, {r4 - r12, pc}) + +@ Purpose: call an expansion card loader to reset the card +@ Proto : void read_loader(int card_base, char *loader); +@ Returns: byte read + +ENTRY(ecard_loader_reset) + stmfd sp!, {r4 - r12, lr} + mov r11, r0 + CPSR2SPSR(r0) + mov lr, pc + add pc, r1, #8 + LOADREGS(fd, sp!, {r4 - r12, pc}) + + +#if 0 mov r2, r2, lsl#1 -ENTRY(inswb) mov ip, sp stmfd sp!, {r4 - r10, fp, ip, lr, pc} sub fp, ip, #4 @@ -122,14 +586,9 @@ bgt Linsw_notaligned LOADREGS(ea, fp, {r4 - r10, fp, sp, pc}) -@ Purpose: write a block of data from memory to a hardware register. -@ Proto : outsw(int to_reg, void *from, int len_in_words); -@ Proto : outswb(int to_reg, void *from, int len_in_bytes); -@ Notes : increments from ENTRY(outsw) - mov r2, r2, LSL#1 -ENTRY(outswb) + mov r2, r2, lsl#1 mov ip, sp stmfd sp!, {r4 - r8, fp, ip, lr, pc} sub fp, ip, #4 @@ -166,56 +625,5 @@ bgt 3b LOADREGS(ea, fp, {r4 - r8, fp, sp, pc}) -/* - * These make no sense on Acorn machines atm. - */ -ENTRY(insl) -ENTRY(outsl) - RETINSTR(mov,pc,lr) - -@ Purpose: write a memc register -@ Proto : void memc_write(int register, int value); -@ Returns: nothing - -#if defined(CONFIG_CPU_26) -ENTRY(memc_write) - cmp r0, #7 - RETINSTR(movgt,pc,lr) - mov r0, r0, lsl #17 - mov r1, r1, lsl #15 - mov r1, r1, lsr #17 - orr r0, r0, r1, lsl #2 - add r0, r0, #0x03600000 - strb r0, [r0] - RETINSTR(mov,pc,lr) -#define CPSR2SPSR(rt) -#else -#define CPSR2SPSR(rt) \ - mrs rt, cpsr; \ - msr spsr, rt #endif -@ Purpose: call an expansion card loader to read bytes. -@ Proto : char read_loader(int offset, char *card_base, char *loader); -@ Returns: byte read - -ENTRY(ecard_loader_read) - stmfd sp!, {r4 - r12, lr} - mov r11, r1 - mov r1, r0 - CPSR2SPSR(r0) - mov lr, pc - mov pc, r2 - LOADREGS(fd, sp!, {r4 - r12, pc}) - -@ Purpose: call an expansion card loader to reset the card -@ Proto : void read_loader(int card_base, char *loader); -@ Returns: byte read - -ENTRY(ecard_loader_reset) - stmfd sp!, {r4 - r12, lr} - mov r11, r0 - CPSR2SPSR(r0) - mov lr, pc - add pc, r1, #8 - LOADREGS(fd, sp!, {r4 - r12, pc}) diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/io-ebsa110.S linux/arch/arm/lib/io-ebsa110.S --- v2.3.6/linux/arch/arm/lib/io-ebsa110.S Tue Jan 20 16:39:41 1998 +++ linux/arch/arm/lib/io-ebsa110.S Thu Jun 17 01:11:35 1999 @@ -22,6 +22,22 @@ ldr lr, [r0] ;\ orr reg, reg, lr, lsl $16 +/* + * These make no sense on these machines. + * Print a warning message. + */ +ENTRY(insl) +ENTRY(outsl) +ENTRY(insb) +ENTRY(outsb) + adr r0, io_long_warning + mov r1, lr + b SYMBOL_NAME(printk) + +io_long_warning: + .ascii "<4>ins?/outs? not implemented on this architecture\0" + .align + @ Purpose: read a block of data from a hardware register to memory. @ Proto : insw(int from_port, void *to, int len_in_words); @ Proto : inswb(int from_port, void *to, int len_in_bytes); diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/io-ebsa285.S linux/arch/arm/lib/io-ebsa285.S --- v2.3.6/linux/arch/arm/lib/io-ebsa285.S Thu Dec 17 09:05:42 1998 +++ linux/arch/arm/lib/io-ebsa285.S Wed Dec 31 16:00:00 1969 @@ -1,197 +0,0 @@ -#include - -ENTRY(insl) - add r0, r0, #0xff000000 - add r0, r0, #0x00e00000 - ands ip, r1, #3 - bne 2f - -1: ldr r3, [r0] - str r3, [r1], #4 - subs r2, r2, #1 - bne 1b - mov pc, lr - -2: cmp ip, #2 - ldr ip, [r0] - blt 3f - bgt 4f - - strh ip, [r1], #2 - mov ip, ip, lsr #16 -1: subs r2, r2, #1 - ldrne r3, [r0] - orrne ip, ip, r3, lsl #16 - strne ip, [r1], #4 - movne ip, r3, lsr #16 - bne 1b - strh ip, [r1], #2 - mov pc, lr - -3: strb ip, [r1], #1 - mov ip, ip, lsr #8 - strh ip, [r1], #2 - mov ip, ip, lsr #16 -1: subs r2, r2, #1 - ldrne r3, [r0] - orrne ip, ip, r3, lsl #8 - strne ip, [r1], #4 - movne ip, r3, lsr #24 - bne 1b - strb ip, [r1], #1 - mov pc, lr - -4: strb ip, [r1], #1 - mov ip, ip, lsr #8 -1: subs r2, r2, #1 - ldrne r3, [r0] - orrne ip, ip, r3, lsl #24 - strne ip, [r1], #4 - movne ip, r3, lsr #8 - bne 1b - strb ip, [r1], #1 - mov ip, ip, lsr #8 - strh ip, [r1], #2 - mov pc, lr - -ENTRY(outsl) - add r0, r0, #0xff000000 - add r0, r0, #0x00e00000 - ands ip, r1, #3 - bne 2f - -1: ldr r3, [r1], #4 - str r3, [r0] - subs r2, r2, #1 - bne 1b - mov pc, lr - -2: bic r1, r1, #3 - cmp ip, #2 - ldr ip, [r1], #4 - mov ip, ip, lsr #16 - blt 3f - bgt 4f - -1: ldr r3, [r1], #4 - orr ip, ip, r3, lsl #16 - str ip, [r0] - mov ip, r3, lsr #16 - subs r2, r2, #1 - bne 1b - mov pc, lr - -3: ldr r3, [r1], #4 - orr ip, ip, r3, lsl #8 - str ip, [r0] - mov ip, r3, lsr #24 - subs r2, r2, #1 - bne 3b - mov pc, lr - -4: ldr r3, [r1], #4 - orr ip, ip, r3, lsl #24 - str ip, [r0] - mov ip, r3, lsr #8 - subs r2, r2, #1 - bne 4b - mov pc, lr - - /* Nobody could say these are optimal, but not to worry. */ - -ENTRY(outswb) - mov r2, r2, lsr #1 -ENTRY(outsw) - add r0, r0, #0xff000000 - add r0, r0, #0x00e00000 -1: subs r2, r2, #1 - ldrgeh r3, [r1], #2 - strgeh r3, [r0] - bgt 1b - mov pc, lr - -ENTRY(inswb) - mov r2, r2, lsr #1 -ENTRY(insw) - stmfd sp!, {r4, r5, lr} - add r0, r0, #0xff000000 - add r0, r0, #0x00e00000 - @ + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 - subs ip, r2, #8 - blo too_little - @ + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 - ands lr, r1, #3 @ check alignment - beq 1f - - ldrh r3, [r0] - strh r3, [r1], #2 - sub ip, ip, #1 - cmn ip, #8 - blo too_little - -1: ldrh r2, [r0] - ldrh r3, [r0] - orr r2, r2, r3, lsl #16 - ldrh r3, [r0] - ldrh r4, [r0] - orr r3, r3, r4, lsl #16 - ldrh r4, [r0] - ldrh r5, [r0] - orr r4, r4, r5, lsl #16 - ldrh r5, [r0] - ldrh lr, [r0] - orr r5, r5, lr, lsl #16 - stmia r1!, {r2, r3, r4, r5} - subs ip, ip, #8 - @ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 + 0 + 1 - bhs 1b - @ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 8 - 7 - cmn ip, #4 - ldrhsh r2, [r0] @ ... ... ... ... - 4 - 3 - 2 - 1 ... ... - ldrhsh r3, [r0] - orrhs r2, r2, r3, lsl #16 - ldrhsh r3, [r0] - ldrhsh r4, [r0] - orrhs r3, r3, r4, lsl #16 - stmhsia r1!, {r2, r3} - - tst ip, #2 - ldrneh r2, [r0] @ ... ... - 6 - 5 ... ... - 2 - 1 ... ... - ldrneh r3, [r0] - orrne r2, r2, r3, lsl #16 - strne r2, [r1], #4 - - tst ip, #1 - ldrneh r2, [r0] - strneh r2, [r1], #2 - - ldmfd sp!, {r4, r5, pc} - -too_little: subs r2, r2, #1 - ldrgeh r3, [r0] - strgeh r3, [r1], #2 - bgt too_little - - ldmfd sp!, {r4, r5, pc} - - -ENTRY(insb) - add r0, r0, #0xff000000 - add r0, r0, #0x00e00000 -1: teq r2, #0 - ldrneb r3, [r0] - strneb r3, [r1], #1 - subne r2, r2, #1 - bne 1b - mov pc, lr - - -ENTRY(outsb) - add r0, r0, #0xff000000 - add r0, r0, #0x00e00000 -1: teq r2, #0 - ldrneb r3, [r1], #1 - strneb r3, [r0] - subne r2, r2, #1 - bne 1b - mov pc, lr diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/io-footbridge.S linux/arch/arm/lib/io-footbridge.S --- v2.3.6/linux/arch/arm/lib/io-footbridge.S Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/lib/io-footbridge.S Thu Jun 17 01:11:35 1999 @@ -0,0 +1,200 @@ +#include +#include + + .equ pcio_high, PCIO_BASE & 0xff000000 + .equ pcio_low, PCIO_BASE & 0x00ffffff + + .macro ioaddr, rd,rn + add \rd, \rn, #pcio_high + add \rd, \rd, #pcio_low + .endm + +ENTRY(insl) + ioaddr r0, r0 + ands ip, r1, #3 + bne 2f + +1: ldr r3, [r0] + str r3, [r1], #4 + subs r2, r2, #1 + bne 1b + mov pc, lr + +2: cmp ip, #2 + ldr ip, [r0] + blt 4f + bgt 6f + + strh ip, [r1], #2 + mov ip, ip, lsr #16 +3: subs r2, r2, #1 + ldrne r3, [r0] + orrne ip, ip, r3, lsl #16 + strne ip, [r1], #4 + movne ip, r3, lsr #16 + bne 3b + strh ip, [r1], #2 + mov pc, lr + +4: strb ip, [r1], #1 + mov ip, ip, lsr #8 + strh ip, [r1], #2 + mov ip, ip, lsr #16 +5: subs r2, r2, #1 + ldrne r3, [r0] + orrne ip, ip, r3, lsl #8 + strne ip, [r1], #4 + movne ip, r3, lsr #24 + bne 5b + strb ip, [r1], #1 + mov pc, lr + +6: strb ip, [r1], #1 + mov ip, ip, lsr #8 +7: subs r2, r2, #1 + ldrne r3, [r0] + orrne ip, ip, r3, lsl #24 + strne ip, [r1], #4 + movne ip, r3, lsr #8 + bne 7b + strb ip, [r1], #1 + mov ip, ip, lsr #8 + strh ip, [r1], #2 + mov pc, lr + +ENTRY(outsl) + ioaddr r0, r0 + ands ip, r1, #3 + bne 2f + +1: ldr r3, [r1], #4 + str r3, [r0] + subs r2, r2, #1 + bne 1b + mov pc, lr + +2: bic r1, r1, #3 + cmp ip, #2 + ldr ip, [r1], #4 + mov ip, ip, lsr #16 + blt 4f + bgt 5f + +3: ldr r3, [r1], #4 + orr ip, ip, r3, lsl #16 + str ip, [r0] + mov ip, r3, lsr #16 + subs r2, r2, #1 + bne 3b + mov pc, lr + +4: ldr r3, [r1], #4 + orr ip, ip, r3, lsl #8 + str ip, [r0] + mov ip, r3, lsr #24 + subs r2, r2, #1 + bne 4b + mov pc, lr + +5: ldr r3, [r1], #4 + orr ip, ip, r3, lsl #24 + str ip, [r0] + mov ip, r3, lsr #8 + subs r2, r2, #1 + bne 5b + mov pc, lr + + /* Nobody could say these are optimal, but not to worry. */ + +ENTRY(outswb) + mov r2, r2, lsr #1 +ENTRY(outsw) + ioaddr r0, r0 +1: subs r2, r2, #1 + ldrgeh r3, [r1], #2 + strgeh r3, [r0] + bgt 1b + mov pc, lr + +ENTRY(inswb) + mov r2, r2, lsr #1 +ENTRY(insw) + stmfd sp!, {r4, r5, lr} + ioaddr r0, r0 + @ + 8 + 9 +10 +11 +12 +13 +14 +15 +16 +17 + subs ip, r2, #8 + blo too_little + @ + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + ands lr, r1, #3 @ check alignment + beq 1f + + ldrh r3, [r0] + strh r3, [r1], #2 + sub ip, ip, #1 + cmn ip, #8 + blo too_little + +1: ldrh r2, [r0] + ldrh r3, [r0] + orr r2, r2, r3, lsl #16 + ldrh r3, [r0] + ldrh r4, [r0] + orr r3, r3, r4, lsl #16 + ldrh r4, [r0] + ldrh r5, [r0] + orr r4, r4, r5, lsl #16 + ldrh r5, [r0] + ldrh lr, [r0] + orr r5, r5, lr, lsl #16 + stmia r1!, {r2, r3, r4, r5} + subs ip, ip, #8 + @ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 + 0 + 1 + bhs 1b + @ - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 8 - 7 + cmn ip, #4 + ldrhsh r2, [r0] @ ... ... ... ... - 4 - 3 - 2 - 1 ... ... + ldrhsh r3, [r0] + orrhs r2, r2, r3, lsl #16 + ldrhsh r3, [r0] + ldrhsh r4, [r0] + orrhs r3, r3, r4, lsl #16 + stmhsia r1!, {r2, r3} + + tst ip, #2 + ldrneh r2, [r0] @ ... ... - 6 - 5 ... ... - 2 - 1 ... ... + ldrneh r3, [r0] + orrne r2, r2, r3, lsl #16 + strne r2, [r1], #4 + + tst ip, #1 + ldrneh r2, [r0] + strneh r2, [r1], #2 + + ldmfd sp!, {r4, r5, pc} + +too_little: subs r2, r2, #1 + ldrgeh r3, [r0] + strgeh r3, [r1], #2 + bgt too_little + + ldmfd sp!, {r4, r5, pc} + + +ENTRY(insb) + ioaddr r0, r0 +1: teq r2, #0 + ldrneb r3, [r0] + strneb r3, [r1], #1 + subne r2, r2, #1 + bne 1b + mov pc, lr + + +ENTRY(outsb) + ioaddr r0, r0 +1: teq r2, #0 + ldrneb r3, [r1], #1 + strneb r3, [r0] + subne r2, r2, #1 + bne 1b + mov pc, lr diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/io.c linux/arch/arm/lib/io.c --- v2.3.6/linux/arch/arm/lib/io.c Sun Sep 6 10:44:47 1998 +++ linux/arch/arm/lib/io.c Thu Jun 17 01:11:35 1999 @@ -18,7 +18,7 @@ * Copy data from "real" memory space to IO memory space. * This needs to be optimized. */ -void _memcpy_toio(unsigned long to, void * from, unsigned long count) +void _memcpy_toio(unsigned long to, const void * from, unsigned long count) { while (count) { count--; diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/ll_char_wr.S linux/arch/arm/lib/ll_char_wr.S --- v2.3.6/linux/arch/arm/lib/ll_char_wr.S Wed May 20 18:54:35 1998 +++ linux/arch/arm/lib/ll_char_wr.S Wed Dec 31 16:00:00 1969 @@ -1,158 +0,0 @@ -/* - * linux/arch/arm/lib/ll_char_wr.S - * - * Copyright (C) 1995, 1996 Russell King. - * - * Speedups & 1bpp code (C) 1996 Philip Blundell & Russell King. - * - * 10-04-96 RMK Various cleanups & reduced register usage. - * 08-04-98 RMK Shifts re-ordered - */ - -@ Regs: [] = corruptible -@ {} = used -@ () = do not use - -#include -#include - .text - -#define BOLD 0x01 -#define ITALIC 0x02 -#define UNDERLINE 0x04 -#define FLASH 0x08 -#define INVERSE 0x10 - -LC0: .word SYMBOL_NAME(bytes_per_char_h) - .word SYMBOL_NAME(video_size_row) - .word SYMBOL_NAME(cmap_80) - .word SYMBOL_NAME(con_charconvtable) - -ENTRY(ll_write_char) - stmfd sp!, {r4 - r7, lr} -@ -@ Smashable regs: {r0 - r3}, [r4 - r7], (r8 - fp), [ip], (sp), [lr], (pc) -@ - eor ip, r1, #UNDERLINE << 9 -/* - * calculate colours - */ - tst r1, #INVERSE << 9 - moveq r2, r1, lsr #16 - moveq r3, r1, lsr #24 - movne r2, r1, lsr #24 - movne r3, r1, lsr #16 - and r3, r3, #255 - and r2, r2, #255 -/* - * calculate offset into character table - */ - mov r1, r1, lsl #23 - mov r1, r1, lsr #20 -/* - * calculate offset required for each row [maybe I should make this an argument to this fn. - * Have to see what the register usage is like in the calling routines. - */ - adr r4, LC0 - ldmia r4, {r4, r5, r6, lr} - ldr r4, [r4] - ldr r5, [r5] -/* - * Go to resolution-dependent routine... - */ - cmp r4, #4 - blt Lrow1bpp - eor r2, r3, r2 @ Create eor mask to change colour from bg - orr r3, r3, r3, lsl #8 @ to fg. - orr r3, r3, r3, lsl #16 - add r0, r0, r5, lsl #3 @ Move to bottom of character - add r1, r1, #7 - ldrb r7, [r6, r1] - tst ip, #UNDERLINE << 9 - eoreq r7, r7, #255 - teq r4, #8 - beq Lrow8bpplp -@ -@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc) -@ - orr r3, r3, r3, lsl #4 -Lrow4bpplp: ldr r7, [lr, r7, lsl #2] - mul r7, r2, r7 - tst r1, #7 @ avoid using r7 directly after - eor ip, r3, r7 - str ip, [r0, -r5]! - LOADREGS(eqfd, sp!, {r4 - r7, pc}) - sub r1, r1, #1 - ldrb r7, [r6, r1] - ldr r7, [lr, r7, lsl #2] - mul r7, r2, r7 - tst r1, #7 @ avoid using r7 directly after - eor ip, r3, r7 - str ip, [r0, -r5]! - subne r1, r1, #1 - ldrneb r7, [r6, r1] - bne Lrow4bpplp - LOADREGS(fd, sp!, {r4 - r7, pc}) - -@ -@ Smashable regs: {r0 - r3}, [r4], {r5 - r7}, (r8 - fp), [ip], (sp), {lr}, (pc) -@ -Lrow8bpplp: mov ip, r7, lsr #4 - ldr ip, [lr, ip, lsl #2] - mul r4, r2, ip - and ip, r7, #15 @ avoid r4 - ldr ip, [lr, ip, lsl #2] @ avoid r4 - mul ip, r2, ip @ avoid r4 - eor r4, r3, r4 @ avoid ip - tst r1, #7 @ avoid ip - sub r0, r0, r5 @ avoid ip - eor ip, r3, ip - stmia r0, {r4, ip} - LOADREGS(eqfd, sp!, {r4 - r7, pc}) - sub r1, r1, #1 - ldrb r7, [r6, r1] - mov ip, r7, lsr #4 - ldr ip, [lr, ip, lsl #2] - mul r4, r2, ip - and ip, r7, #15 @ avoid r4 - ldr ip, [lr, ip, lsl #2] @ avoid r4 - mul ip, r2, ip @ avoid r4 - eor r4, r3, r4 @ avoid ip - tst r1, #7 @ avoid ip - sub r0, r0, r5 @ avoid ip - eor ip, r3, ip - stmia r0, {r4, ip} - subne r1, r1, #1 - ldrneb r7, [r6, r1] - bne Lrow8bpplp - LOADREGS(fd, sp!, {r4 - r7, pc}) - -@ -@ Smashable regs: {r0 - r3}, [r4], {r5, r6}, [r7], (r8 - fp), [ip], (sp), [lr], (pc) -@ -Lrow1bpp: add r6, r6, r1 - ldmia r6, {r4, r7} - tst ip, #INVERSE << 9 - mvnne r4, r4 - mvnne r7, r7 - strb r4, [r0], r5 - mov r4, r4, lsr #8 - strb r4, [r0], r5 - mov r4, r4, lsr #8 - strb r4, [r0], r5 - mov r4, r4, lsr #8 - strb r4, [r0], r5 - strb r7, [r0], r5 - mov r7, r7, lsr #8 - strb r7, [r0], r5 - mov r7, r7, lsr #8 - strb r7, [r0], r5 - mov r7, r7, lsr #8 - tst ip, #UNDERLINE << 9 - mvneq r7, r7 - strb r7, [r0], r5 - LOADREGS(fd, sp!, {r4 - r7, pc}) - - .bss -ENTRY(con_charconvtable) - .space 1024 diff -u --recursive --new-file v2.3.6/linux/arch/arm/lib/semaphore.S linux/arch/arm/lib/semaphore.S --- v2.3.6/linux/arch/arm/lib/semaphore.S Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/lib/semaphore.S Thu Jun 17 01:11:35 1999 @@ -0,0 +1,34 @@ +/* + * linux/arch/arm/lib/semaphore.S + * + * Idea from i386 code, Copyright Linus Torvalds. + * Converted for ARM by Russell King + */ +#include +#include + +/* + * The semaphore operations have a special calling sequence + * that allows us to keep the distruption of the main code + * path to a minimum. These routines save and restore the + * registers that will be touched by __down etc. + */ +ENTRY(__down_failed) + stmfd sp!, {r0 - r3, ip, lr} + bl SYMBOL_NAME(__down) + LOADREGS(fd, sp!, {r0 - r3, ip, pc}) + +ENTRY(__down_interruptible_failed) + stmfd sp!, {r1 - r3, ip, lr} + bl SYMBOL_NAME(__down_interruptible) + LOADREGS(fd, sp!, {r1 - r3, ip, pc}) + +ENTRY(__down_trylock_failed) + stmfd sp!, {r1 - r3, ip, lr} + bl SYMBOL_NAME(__down_trylock) + LOADREGS(fd, sp!, {r1 - r3, ip, pc}) + +ENTRY(__up_wakeup) + stmfd sp!, {r0 - r3, ip, lr} + bl SYMBOL_NAME(__up) + LOADREGS(fd, sp!, {r0 - r3, ip, pc}) diff -u --recursive --new-file v2.3.6/linux/arch/arm/mm/fault-common.c linux/arch/arm/mm/fault-common.c --- v2.3.6/linux/arch/arm/mm/fault-common.c Sat May 8 11:06:56 1999 +++ linux/arch/arm/mm/fault-common.c Thu Jun 17 01:11:35 1999 @@ -26,25 +26,14 @@ set_pmd(pmd, mk_kernel_pmd(BAD_PAGETABLE)); } -static void -kernel_page_fault(unsigned long addr, int mode, struct pt_regs *regs, - struct task_struct *tsk, struct mm_struct *mm) +/* + * This is useful to dump out the page tables associated with + * 'addr' in mm 'mm'. + */ +void show_pte(struct mm_struct *mm, unsigned long addr) { - char *reason; - /* - * Oops. The kernel tried to access some bad page. We'll have to - * terminate things with extreme prejudice. - */ pgd_t *pgd; - if (addr < PAGE_SIZE) - reason = "NULL pointer dereference"; - else - reason = "paging request"; - - printk(KERN_ALERT "Unable to handle kernel %s at virtual address %08lx\n", - reason, addr); - printk(KERN_ALERT "memmap = %08lX, pgd = %p\n", tsk->tss.memmap, mm->pgd); pgd = pgd_offset(mm, addr); printk(KERN_ALERT "*pgd = %08lx", pgd_val(*pgd)); @@ -77,6 +66,27 @@ } while(0); printk("\n"); +} + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ +static void +kernel_page_fault(unsigned long addr, int mode, struct pt_regs *regs, + struct task_struct *tsk, struct mm_struct *mm) +{ + char *reason; + + if (addr < PAGE_SIZE) + reason = "NULL pointer dereference"; + else + reason = "paging request"; + + printk(KERN_ALERT "Unable to handle kernel %s at virtual address %08lx\n", + reason, addr); + printk(KERN_ALERT "memmap = %08lX, pgd = %p\n", tsk->tss.memmap, mm->pgd); + show_pte(mm, addr); die("Oops", regs, mode); do_exit(SIGKILL); diff -u --recursive --new-file v2.3.6/linux/arch/arm/mm/ioremap.c linux/arch/arm/mm/ioremap.c --- v2.3.6/linux/arch/arm/mm/ioremap.c Sat May 8 11:06:56 1999 +++ linux/arch/arm/mm/ioremap.c Thu Jun 17 01:11:35 1999 @@ -115,19 +115,19 @@ { void * addr; struct vm_struct * area; - unsigned long offset; + unsigned long offset, last_addr; + + /* Don't allow wraparound or zero size */ + last_addr = phys_addr + size - 1; + if (!size || last_addr < phys_addr) + return NULL; /* * Mappings have to be page-aligned */ offset = phys_addr & ~PAGE_MASK; - size = PAGE_ALIGN(size + offset); - - /* - * Don't allow mappings that wrap.. - */ - if (!size || size > phys_addr + size) - return NULL; + phys_addr &= PAGE_MASK; + size = PAGE_ALIGN(last_addr) - phys_addr; /* * Ok, go for it.. diff -u --recursive --new-file v2.3.6/linux/arch/arm/mm/proc-arm2,3.S linux/arch/arm/mm/proc-arm2,3.S --- v2.3.6/linux/arch/arm/mm/proc-arm2,3.S Sat May 8 11:07:16 1999 +++ linux/arch/arm/mm/proc-arm2,3.S Thu Jun 17 01:11:35 1999 @@ -202,15 +202,11 @@ LC0: .word SYMBOL_NAME(page_nr) /* * Function: arm2_switch_to (struct task_struct *prev, struct task_struct *next) - * * Params : prev Old task structure * : next New task structure for process to run - * * Returns : prev - * * Purpose : Perform a task switch, saving the old processes state, and restoring * the new. - * * Notes : We don't fiddle with the FP registers here - we postpone this until * the new task actually uses FP. This way, we don't swap FP for tasks * that do not require it. @@ -316,15 +312,11 @@ _arm2_proc_fin: movs pc, lr /* * Function: arm3_switch_to (struct task_struct *prev, struct task_struct *next) - * * Params : prev Old task structure * : next New task structure for process to run - * * Returns : prev - * * Purpose : Perform a task switch, saving the old processes state, and restoring * the new. - * * Notes : We don't fiddle with the FP registers here - we postpone this until * the new task actually uses FP. This way, we don't swap FP for tasks * that do not require it. diff -u --recursive --new-file v2.3.6/linux/arch/arm/mm/proc-arm6,7.S linux/arch/arm/mm/proc-arm6,7.S --- v2.3.6/linux/arch/arm/mm/proc-arm6,7.S Sat May 8 11:07:16 1999 +++ linux/arch/arm/mm/proc-arm6,7.S Thu Jun 17 01:11:35 1999 @@ -74,14 +74,14 @@ str sp, [r0, #TSS_SAVE] @ Save sp_SVC ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC ldr r2, [r1, #TSK_ADDR_LIMIT] + ldr r3, [r1, #TSS_MEMMAP] @ Page table pointer teq r2, #0 moveq r2, #DOM_KERNELDOMAIN movne r2, #DOM_USERDOMAIN mcr p15, 0, r2, c3, c0 @ Set domain reg - ldr r2, [r1, #TSS_MEMMAP] @ Page table pointer mov r1, #0 mcr p15, 0, r1, c7, c0, 0 @ flush cache - mcr p15, 0, r2, c2, c0, 0 @ update page table ptr + mcr p15, 0, r3, c2, c0, 0 @ update page table ptr mcr p15, 0, r1, c5, c0, 0 @ flush TLBs ldmfd sp!, {ip} msr spsr, ip @ Save tasks CPSR into SPSR for this return diff -u --recursive --new-file v2.3.6/linux/arch/arm/mm/proc-sa110.S linux/arch/arm/mm/proc-sa110.S --- v2.3.6/linux/arch/arm/mm/proc-sa110.S Sat May 8 11:07:16 1999 +++ linux/arch/arm/mm/proc-sa110.S Thu Jun 17 01:11:35 1999 @@ -29,11 +29,11 @@ mov r2, #1 _sa110_flush_cache_all_r2: ldr r3, =Lclean_switch + ldr ip, =FLUSH_BASE ldr r1, [r3] ands r1, r1, #1 eor r1, r1, #1 str r1, [r3] - ldr ip, =FLUSH_BASE addne ip, ip, #32768 add r1, ip, #16384 @ only necessary for 16k 1: ldr r3, [ip], #32 @@ -226,12 +226,12 @@ ldr r2, [r0, #TSS_MEMMAP] @ Get old page tables str sp, [r0, #TSS_SAVE] @ Save sp_SVC ldr sp, [r1, #TSS_SAVE] @ Get saved sp_SVC - ldr r4, [r1, #TSK_ADDR_LIMIT] - teq r4, #0 - moveq r4, #DOM_KERNELDOMAIN - movne r4, #DOM_USERDOMAIN - mcr p15, 0, r4, c3, c0 @ Set segment + ldr r5, [r1, #TSK_ADDR_LIMIT] ldr r4, [r1, #TSS_MEMMAP] @ Page table pointer + teq r5, #0 + moveq r5, #DOM_KERNELDOMAIN + movne r5, #DOM_USERDOMAIN + mcr p15, 0, r5, c3, c0 @ Set segment /* * Flushing the cache is nightmarishly slow, so we take any excuse * to get out of it. If the old page table is the same as the new, @@ -288,7 +288,8 @@ */ .align 5 _sa110_set_pmd: str r1, [r0] - mcr p15, 0, r0, c7, c10, 1 @ clean D entry (drain is done by TLB fns) + mcr p15, 0, r0, c7, c10, 1 @ clean D entry + mcr p15, 0, r0, c7, c10, 4 @ drain WB (TLB bypasses WB) mov pc, lr /* @@ -318,6 +319,7 @@ str r2, [r0] @ hardware version mov r0, r0 mcr p15, 0, r0, c7, c10, 1 @ clean D entry (drain is done by TLB fns) + mcr p15, 0, r0, c7, c10, 4 @ drain WB (TLB bypasses WB) mov pc, lr /* diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/ARM-gcc.h linux/arch/arm/nwfpe/ARM-gcc.h --- v2.3.6/linux/arch/arm/nwfpe/ARM-gcc.h Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/ARM-gcc.h Thu Jun 17 01:11:35 1999 @@ -0,0 +1,128 @@ + +/* +------------------------------------------------------------------------------- +One of the macros `BIGENDIAN' or `LITTLEENDIAN' must be defined. +------------------------------------------------------------------------------- +*/ +#define LITTLEENDIAN + +/* +------------------------------------------------------------------------------- +The macro `BITS64' can be defined to indicate that 64-bit integer types are +supported by the compiler. +------------------------------------------------------------------------------- +*/ +#define BITS64 + +/* +------------------------------------------------------------------------------- +Each of the following `typedef's defines the most convenient type that holds +integers of at least as many bits as specified. For example, `uint8' should +be the most convenient type that can hold unsigned integers of as many as +8 bits. The `flag' type must be able to hold either a 0 or 1. For most +implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed +to the same as `int'. +------------------------------------------------------------------------------- +*/ +typedef char flag; +typedef unsigned char uint8; +typedef signed char int8; +typedef int uint16; +typedef int int16; +typedef unsigned int uint32; +typedef signed int int32; +#ifdef BITS64 +typedef unsigned long long int bits64; +typedef signed long long int sbits64; +#endif + +/* +------------------------------------------------------------------------------- +Each of the following `typedef's defines a type that holds integers +of _exactly_ the number of bits specified. For instance, for most +implementation of C, `bits16' and `sbits16' should be `typedef'ed to +`unsigned short int' and `signed short int' (or `short int'), respectively. +------------------------------------------------------------------------------- +*/ +typedef unsigned char bits8; +typedef signed char sbits8; +typedef unsigned short int bits16; +typedef signed short int sbits16; +typedef unsigned int bits32; +typedef signed int sbits32; +#ifdef BITS64 +typedef unsigned long long int uint64; +typedef signed long long int int64; +#endif + +#ifdef BITS64 +/* +------------------------------------------------------------------------------- +The `LIT64' macro takes as its argument a textual integer literal and if +necessary ``marks'' the literal as having a 64-bit integer type. For +example, the Gnu C Compiler (`gcc') requires that 64-bit literals be +appended with the letters `LL' standing for `long long', which is `gcc's +name for the 64-bit integer type. Some compilers may allow `LIT64' to be +defined as the identity macro: `#define LIT64( a ) a'. +------------------------------------------------------------------------------- +*/ +#define LIT64( a ) a##LL +#endif + +/* +------------------------------------------------------------------------------- +The macro `INLINE' can be used before functions that should be inlined. If +a compiler does not support explicit inlining, this macro should be defined +to be `static'. +------------------------------------------------------------------------------- +*/ +#define INLINE extern __inline__ + + +/* For use as a GCC soft-float library we need some special function names. */ + +#ifdef __LIBFLOAT__ + +/* Some 32-bit ops can be mapped straight across by just changing the name. */ +#define float32_add __addsf3 +#define float32_sub __subsf3 +#define float32_mul __mulsf3 +#define float32_div __divsf3 +#define int32_to_float32 __floatsisf +#define float32_to_int32_round_to_zero __fixsfsi +#define float32_to_uint32_round_to_zero __fixunssfsi + +/* These ones go through the glue code. To avoid namespace pollution + we rename the internal functions too. */ +#define float32_eq ___float32_eq +#define float32_le ___float32_le +#define float32_lt ___float32_lt + +/* All the 64-bit ops have to go through the glue, so we pull the same + trick. */ +#define float64_add ___float64_add +#define float64_sub ___float64_sub +#define float64_mul ___float64_mul +#define float64_div ___float64_div +#define int32_to_float64 ___int32_to_float64 +#define float64_to_int32_round_to_zero ___float64_to_int32_round_to_zero +#define float64_to_uint32_round_to_zero ___float64_to_uint32_round_to_zero +#define float64_to_float32 ___float64_to_float32 +#define float32_to_float64 ___float32_to_float64 +#define float64_eq ___float64_eq +#define float64_le ___float64_le +#define float64_lt ___float64_lt + +#if 0 +#define float64_add __adddf3 +#define float64_sub __subdf3 +#define float64_mul __muldf3 +#define float64_div __divdf3 +#define int32_to_float64 __floatsidf +#define float64_to_int32_round_to_zero __fixdfsi +#define float64_to_uint32_round_to_zero __fixunsdfsi +#define float64_to_float32 __truncdfsf2 +#define float32_to_float64 __extendsfdf2 +#endif + +#endif diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/ChangeLog linux/arch/arm/nwfpe/ChangeLog --- v2.3.6/linux/arch/arm/nwfpe/ChangeLog Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/ChangeLog Thu Jun 17 01:11:35 1999 @@ -0,0 +1,20 @@ +1998-11-23 Scott Bambrough + + * README.FPE - fix typo in description of lfm/sfm instructions + * NOTES - Added file to describe known bugs/problems + * fpmodule.c - Changed version number to 0.94 + +1998-11-20 Scott Bambrough + + * README.FPE - fix description of URD, NRM instructions + * TODO - remove URD, NRM instructions from TODO list + * single_cpdo.c - implement URD, NRM + * double_cpdo.c - implement URD, NRM + * extended_cpdo.c - implement URD, NRM + +1998-11-19 Scott Bambrough + + * ChangeLog - Added this file to track changes made. + * fpa11.c - added code to initialize register types to typeNone + * fpa11_cpdt.c - fixed bug in storeExtended (typeExtended changed to + typeDouble in switch statement) diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/Makefile linux/arch/arm/nwfpe/Makefile --- v2.3.6/linux/arch/arm/nwfpe/Makefile Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/Makefile Thu Jun 17 01:11:35 1999 @@ -0,0 +1,31 @@ +# +# linux/arch/arm/nwfpe/Makefile +# +# Copyright (C) 1998, 1999 Philip Blundell +# + +NWFPE_OBJS := fpa11.o fpa11_cpdo.o fpa11_cpdt.o fpa11_cprt.o \ + fpmodule.o fpopcode.o softfloat.o \ + single_cpdo.o double_cpdo.o extended_cpdo.o + +ifeq ($(CONFIG_CPU_26),y) +NWFPE_OBJS += entry26.o +else +NWFPE_OBJS += entry.o +endif + +L_TARGET := math-emu.a + +ifeq ($(CONFIG_NWFPE),y) +L_OBJS = $(NWFPE_OBJS) +else + ifeq ($(CONFIG_NWFPE),m) + M_OBJS = nwfpe.o + MI_OBJS = $(NWFPE_OBJS) + endif +endif + +include $(TOPDIR)/Rules.make + +nwfpe.o: $(MI_OBJS) $(MIX_OBJS) + $(LD) $(LD_RFLAG) -r -o $@ $(MI_OBJS) $(MIX_OBJS) diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/config.h linux/arch/arm/nwfpe/config.h --- v2.3.6/linux/arch/arm/nwfpe/config.h Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/config.h Thu Jun 17 01:11:35 1999 @@ -0,0 +1,31 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#if 1 +#define C_SYMBOL_NAME(foo) foo +#else +#define C_SYMBOL_NAME(foo) _##foo +#endif + +#endif diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/double_cpdo.c linux/arch/arm/nwfpe/double_cpdo.c --- v2.3.6/linux/arch/arm/nwfpe/double_cpdo.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/double_cpdo.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,293 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#include "config.h" +#include "softfloat.h" +#include "fpopcode.h" +#include "fpa11.h" + +extern FPA11 *fpa11; + +float64 getDoubleConstant(unsigned int); + +float64 float64_exp(float64 Fm); +float64 float64_ln(float64 Fm); +float64 float64_sin(float64 rFm); +float64 float64_cos(float64 rFm); +float64 float64_arcsin(float64 rFm); +float64 float64_arctan(float64 rFm); +float64 float64_log(float64 rFm); +float64 float64_tan(float64 rFm); +float64 float64_arccos(float64 rFm); +float64 float64_pow(float64 rFn,float64 rFm); +float64 float64_pol(float64 rFn,float64 rFm); + +unsigned int DoubleCPDO(const unsigned int opcode) +{ + float64 rFm, rFn; + unsigned int Fd, Fm, Fn, nRc = 1; + + //fp_printk("DoubleCPDO(0x%08x)\n",opcode); + + Fm = getFm(opcode); + if (CONSTANT_FM(opcode)) + { + rFm = getDoubleConstant(Fm); + } + else + { + switch (fpa11->fpreg[Fm].fType) + { + case typeSingle: + rFm = float32_to_float64(fpa11->fpreg[Fm].fValue.fSingle); + break; + + case typeDouble: + rFm = fpa11->fpreg[Fm].fValue.fDouble; + break; + + case typeExtended: + // !! patb + //fp_printk("not implemented! why not?\n"); + //!! ScottB + // should never get here, if extended involved + // then other operand should be promoted then + // ExtendedCPDO called. + break; + + default: return 0; + } + } + + if (!MONADIC_INSTRUCTION(opcode)) + { + Fn = getFn(opcode); + switch (fpa11->fpreg[Fn].fType) + { + case typeSingle: + rFn = float32_to_float64(fpa11->fpreg[Fn].fValue.fSingle); + break; + + case typeDouble: + rFn = fpa11->fpreg[Fn].fValue.fDouble; + break; + + default: return 0; + } + } + + Fd = getFd(opcode); + /* !! this switch isn't optimized; better (opcode & MASK_ARITHMETIC_OPCODE)>>24, sort of */ + switch (opcode & MASK_ARITHMETIC_OPCODE) + { + /* dyadic opcodes */ + case ADF_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_add(rFn,rFm); + break; + + case MUF_CODE: + case FML_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_mul(rFn,rFm); + break; + + case SUF_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_sub(rFn,rFm); + break; + + case RSF_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_sub(rFm,rFn); + break; + + case DVF_CODE: + case FDV_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_div(rFn,rFm); + break; + + case RDF_CODE: + case FRD_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_div(rFm,rFn); + break; + +#if 0 + case POW_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_pow(rFn,rFm); + break; + + case RPW_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_pow(rFm,rFn); + break; +#endif + + case RMF_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_rem(rFn,rFm); + break; + +#if 0 + case POL_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_pol(rFn,rFm); + break; +#endif + + /* monadic opcodes */ + case MVF_CODE: + fpa11->fpreg[Fd].fValue.fDouble = rFm; + break; + + case MNF_CODE: + { + unsigned int *p = (unsigned int*)&rFm; + p[1] ^= 0x80000000; + fpa11->fpreg[Fd].fValue.fDouble = rFm; + } + break; + + case ABS_CODE: + { + unsigned int *p = (unsigned int*)&rFm; + p[1] &= 0x7fffffff; + fpa11->fpreg[Fd].fValue.fDouble = rFm; + } + break; + + case RND_CODE: + case URD_CODE: + fpa11->fpreg[Fd].fValue.fDouble = + int32_to_float64(float64_to_int32(rFm)); + break; + + case SQT_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_sqrt(rFm); + break; + +#if 0 + case LOG_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_log(rFm); + break; + + case LGN_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_ln(rFm); + break; + + case EXP_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_exp(rFm); + break; + + case SIN_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_sin(rFm); + break; + + case COS_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_cos(rFm); + break; + + case TAN_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_tan(rFm); + break; + + case ASN_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_arcsin(rFm); + break; + + case ACS_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_arccos(rFm); + break; + + case ATN_CODE: + fpa11->fpreg[Fd].fValue.fDouble = float64_arctan(rFm); + break; +#endif + + case NRM_CODE: + break; + + default: + { + nRc = 0; + } + } + + if (0 != nRc) fpa11->fpreg[Fd].fType = typeDouble; + return nRc; +} + +#if 0 +float64 float64_exp(float64 rFm) +{ + return rFm; +//series +} + +float64 float64_ln(float64 rFm) +{ + return rFm; +//series +} + +float64 float64_sin(float64 rFm) +{ + return rFm; +//series +} + +float64 float64_cos(float64 rFm) +{ + return rFm; + //series +} + +#if 0 +float64 float64_arcsin(float64 rFm) +{ +//series +} + +float64 float64_arctan(float64 rFm) +{ + //series +} +#endif + +float64 float64_log(float64 rFm) +{ + return float64_div(float64_ln(rFm),getDoubleConstant(7)); +} + +float64 float64_tan(float64 rFm) +{ + return float64_div(float64_sin(rFm),float64_cos(rFm)); +} + +float64 float64_arccos(float64 rFm) +{ +return rFm; + //return float64_sub(halfPi,float64_arcsin(rFm)); +} + +float64 float64_pow(float64 rFn,float64 rFm) +{ + return float64_exp(float64_mul(rFm,float64_ln(rFn))); +} + +float64 float64_pol(float64 rFn,float64 rFm) +{ + return float64_arctan(float64_div(rFn,rFm)); +} +#endif diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/entry.S linux/arch/arm/nwfpe/entry.S --- v2.3.6/linux/arch/arm/nwfpe/entry.S Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/entry.S Thu Jun 17 01:11:35 1999 @@ -0,0 +1,126 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + (c) Philip Blundell 1998-1999 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +/* This is the kernel's entry point into the floating point emulator. +It is called from the kernel with code similar to this: + + adrsvc al, r9, ret_from_exception @ r9 = normal FP return + adrsvc al, lr, fpundefinstr @ lr = undefined instr return + + get_current_task r10 + mov r8, #1 + strb r8, [r10, #TSK_USED_MATH] @ set current->used_math + add r10, r10, #TSS_FPESAVE @ r10 = workspace + ldr r4, .LC2 + ldr pc, [r4] @ Call FP emulator entry point + +The kernel expects the emulator to return via one of two possible +points of return it passes to the emulator. The emulator, if +successful in its emulation, jumps to ret_from_exception (passed in +r9) and the kernel takes care of returning control from the trap to +the user code. If the emulator is unable to emulate the instruction, +it returns via _fpundefinstr (passed via lr) and the kernel halts the +user program with a core dump. + +On entry to the emulator r10 points to an area of private FP workspace +reserved in the thread structure for this process. This is where the +emulator saves its registers across calls. The first word of this area +is used as a flag to detect the first time a process uses floating point, +so that the emulator startup cost can be avoided for tasks that don't +want it. + +This routine does three things: + +1) It saves SP into a variable called userRegisters. The kernel has +created a struct pt_regs on the stack and saved the user registers +into it. See /usr/include/asm/proc/ptrace.h for details. The +emulator code uses userRegisters as the base of an array of words from +which the contents of the registers can be extracted. + +2) It calls EmulateAll to emulate a floating point instruction. +EmulateAll returns 1 if the emulation was successful, or 0 if not. + +3) If an instruction has been emulated successfully, it looks ahead at +the next instruction. If it is a floating point instruction, it +executes the instruction, without returning to user space. In this +way it repeatedly looks ahead and executes floating point instructions +until it encounters a non floating point instruction, at which time it +returns via _fpreturn. + +This is done to reduce the effect of the trap overhead on each +floating point instructions. GCC attempts to group floating point +instructions to allow the emulator to spread the cost of the trap over +several floating point instructions. */ + + .globl nwfpe_enter +nwfpe_enter: + /* ?? Could put userRegisters and fpa11 into fixed regs during + emulation. This would reduce load/store overhead at the expense + of stealing two regs from the register allocator. Not sure if + it's worth it. */ + ldr r4, =userRegisters + str sp, [r4] @ save pointer to user regs + ldr r4, =fpa11 + str r10, [r4] @ store pointer to our state + mov r4, sp @ use r4 for local pointer + mov r10, lr @ save the failure-return addresses + + ldr r5, [r4, #60] @ get contents of PC; + ldr r0, [r5, #-4] @ get actual instruction into r0 +emulate: + bl EmulateAll @ emulate the instruction + cmp r0, #0 @ was emulation successful + moveq pc, r10 @ no, return failure + +next: +__x1: ldrt r6, [r5], #4 @ get the next instruction and + @ increment PC + + and r2, r6, #0x0F000000 @ test for FP insns + teq r2, #0x0C000000 + teqne r2, #0x0D000000 + teqne r2, #0x0E000000 + movne pc, r9 @ return ok if not a fp insn + + str r5, [r4, #60] @ update PC copy in regs + + mov r0, r6 @ save a copy + ldr r1, [r4, #64] @ fetch the condition codes + bl checkCondition @ check the condition + cmp r0, #0 @ r0 = 0 ==> condition failed + + @ if condition code failed to match, next insn + beq next @ get the next instruction; + + mov r0, r6 @ prepare for EmulateAll() + b emulate @ if r0 != 0, goto EmulateAll + + @ We need to be prepared for the instruction at __x1 to fault. + @ Emit the appropriate exception gunk to fix things up. + .section .fixup,"ax" + .align +__f1: mov pc, r9 + .previous + .section __ex_table,"a" + .align 3 + .long __x1, __f1 + .previous diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/entry26.S linux/arch/arm/nwfpe/entry26.S --- v2.3.6/linux/arch/arm/nwfpe/entry26.S Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/entry26.S Thu Jun 17 01:11:35 1999 @@ -0,0 +1,112 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + (c) Philip Blundell 1998-1999 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#include "../lib/constants.h" + +/* This is the kernel's entry point into the floating point emulator. +It is called from the kernel with code similar to this: + + mov fp, #0 + teqp pc, #I_BIT | MODE_SVC + ldr r4, .LC2 + ldr pc, [r4] @ Call FP module USR entry point + +The kernel expects the emulator to return via one of two possible +points of return it passes to the emulator. The emulator, if +successful in its emulation, jumps to ret_from_exception and the +kernel takes care of returning control from the trap to the user code. +If the emulator is unable to emulate the instruction, it returns to +fpundefinstr and the kernel halts the user program with a core dump. + +This routine does four things: + +1) It saves SP into a variable called userRegisters. The kernel has +created a struct pt_regs on the stack and saved the user registers +into it. See /usr/include/asm/proc/ptrace.h for details. The +emulator code uses userRegisters as the base of an array of words from +which the contents of the registers can be extracted. + +2) It locates the FP emulator work area within the TSS structure and +points `fpa11' to it. + +3) It calls EmulateAll to emulate a floating point instruction. +EmulateAll returns 1 if the emulation was successful, or 0 if not. + +4) If an instruction has been emulated successfully, it looks ahead at +the next instruction. If it is a floating point instruction, it +executes the instruction, without returning to user space. In this +way it repeatedly looks ahead and executes floating point instructions +until it encounters a non floating point instruction, at which time it +returns via _fpreturn. + +This is done to reduce the effect of the trap overhead on each +floating point instructions. GCC attempts to group floating point +instructions to allow the emulator to spread the cost of the trap over +several floating point instructions. */ + + .globl nwfpe_enter +nwfpe_enter: + ldr r4, =userRegisters + str sp, [r4] @ save pointer to user regs + + mov r10, sp, lsr #13 @ find workspace + mov r10, r10, lsl #13 + add r10, r10, #TSS_FPESAVE + + ldr r4, =fpa11 + str r10, [r4] @ store pointer to our state + mov r4, sp @ use r4 for local pointer + + ldr r5, [r4, #60] @ get contents of PC + bic r5, r5, #0xfc000003 + ldr r0, [r5, #-4] @ get actual instruction into r0 + bl EmulateAll @ emulate the instruction +1: cmp r0, #0 @ was emulation successful + beq fpundefinstr @ no, return failure + +next: + ldrt r6, [r5], #4 @ get the next instruction and + @ increment PC + + and r2, r6, #0x0F000000 @ test for FP insns + teq r2, #0x0C000000 + teqne r2, #0x0D000000 + teqne r2, #0x0E000000 + bne ret_from_exception @ return ok if not a fp insn + + ldr r9, [r4, #60] @ get new condition codes + and r9, r9, #0xfc000003 + orr r7, r5, r9 + str r7, [r4, #60] @ update PC copy in regs + + mov r0, r6 @ save a copy + mov r1, r9 @ fetch the condition codes + bl checkCondition @ check the condition + cmp r0, #0 @ r0 = 0 ==> condition failed + + @ if condition code failed to match, next insn + beq next @ get the next instruction; + + mov r0, r6 @ prepare for EmulateAll() + adr lr, 1b + orr lr, lr, #3 + b EmulateAll @ if r0 != 0, goto EmulateAll diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/extended_cpdo.c linux/arch/arm/nwfpe/extended_cpdo.c --- v2.3.6/linux/arch/arm/nwfpe/extended_cpdo.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/extended_cpdo.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,276 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#include "config.h" +#include "softfloat.h" +#include "fpopcode.h" +#include "fpa11.h" + +floatx80 getExtendedConstant(unsigned int); + +floatx80 floatx80_exp(floatx80 Fm); +floatx80 floatx80_ln(floatx80 Fm); +floatx80 floatx80_sin(floatx80 rFm); +floatx80 floatx80_cos(floatx80 rFm); +floatx80 floatx80_arcsin(floatx80 rFm); +floatx80 floatx80_arctan(floatx80 rFm); +floatx80 floatx80_log(floatx80 rFm); +floatx80 floatx80_tan(floatx80 rFm); +floatx80 floatx80_arccos(floatx80 rFm); +floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm); +floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm); + +unsigned int ExtendedCPDO(const unsigned int opcode) +{ + floatx80 rFm, rFn; + unsigned int Fd, Fm, Fn, nRc = 1; + + //fp_printk("ExtendedCPDO(0x%08x)\n",opcode); + + Fm = getFm(opcode); + if (CONSTANT_FM(opcode)) + { + rFm = getExtendedConstant(Fm); + } + else + { + switch (fpa11->fpreg[Fm].fType) + { + case typeSingle: + rFm = float32_to_floatx80(fpa11->fpreg[Fm].fValue.fSingle); + break; + + case typeDouble: + rFm = float64_to_floatx80(fpa11->fpreg[Fm].fValue.fDouble); + break; + + case typeExtended: + rFm = fpa11->fpreg[Fm].fValue.fExtended; + break; + + default: return 0; + } + } + + if (!MONADIC_INSTRUCTION(opcode)) + { + Fn = getFn(opcode); + switch (fpa11->fpreg[Fn].fType) + { + case typeSingle: + rFn = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle); + break; + + case typeDouble: + rFn = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble); + break; + + case typeExtended: + rFn = fpa11->fpreg[Fn].fValue.fExtended; + break; + + default: return 0; + } + } + + Fd = getFd(opcode); + switch (opcode & MASK_ARITHMETIC_OPCODE) + { + /* dyadic opcodes */ + case ADF_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_add(rFn,rFm); + break; + + case MUF_CODE: + case FML_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_mul(rFn,rFm); + break; + + case SUF_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_sub(rFn,rFm); + break; + + case RSF_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_sub(rFm,rFn); + break; + + case DVF_CODE: + case FDV_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_div(rFn,rFm); + break; + + case RDF_CODE: + case FRD_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_div(rFm,rFn); + break; + +#if 0 + case POW_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_pow(rFn,rFm); + break; + + case RPW_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_pow(rFm,rFn); + break; +#endif + + case RMF_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_rem(rFn,rFm); + break; + +#if 0 + case POL_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_pol(rFn,rFm); + break; +#endif + + /* monadic opcodes */ + case MVF_CODE: + fpa11->fpreg[Fd].fValue.fExtended = rFm; + break; + + case MNF_CODE: + rFm.high ^= 0x8000; + fpa11->fpreg[Fd].fValue.fExtended = rFm; + break; + + case ABS_CODE: + rFm.high &= 0x7fff; + fpa11->fpreg[Fd].fValue.fExtended = rFm; + break; + + case RND_CODE: + case URD_CODE: + fpa11->fpreg[Fd].fValue.fExtended = + int32_to_floatx80(floatx80_to_int32(rFm)); + break; + + case SQT_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_sqrt(rFm); + break; + +#if 0 + case LOG_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_log(rFm); + break; + + case LGN_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_ln(rFm); + break; + + case EXP_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_exp(rFm); + break; + + case SIN_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_sin(rFm); + break; + + case COS_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_cos(rFm); + break; + + case TAN_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_tan(rFm); + break; + + case ASN_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_arcsin(rFm); + break; + + case ACS_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_arccos(rFm); + break; + + case ATN_CODE: + fpa11->fpreg[Fd].fValue.fExtended = floatx80_arctan(rFm); + break; +#endif + + case NRM_CODE: + break; + + default: + { + nRc = 0; + } + } + + if (0 != nRc) fpa11->fpreg[Fd].fType = typeExtended; + return nRc; +} + +#if 0 +floatx80 floatx80_exp(floatx80 Fm) +{ +//series +} + +floatx80 floatx80_ln(floatx80 Fm) +{ +//series +} + +floatx80 floatx80_sin(floatx80 rFm) +{ +//series +} + +floatx80 floatx80_cos(floatx80 rFm) +{ +//series +} + +floatx80 floatx80_arcsin(floatx80 rFm) +{ +//series +} + +floatx80 floatx80_arctan(floatx80 rFm) +{ + //series +} + +floatx80 floatx80_log(floatx80 rFm) +{ + return floatx80_div(floatx80_ln(rFm),getExtendedConstant(7)); +} + +floatx80 floatx80_tan(floatx80 rFm) +{ + return floatx80_div(floatx80_sin(rFm),floatx80_cos(rFm)); +} + +floatx80 floatx80_arccos(floatx80 rFm) +{ + //return floatx80_sub(halfPi,floatx80_arcsin(rFm)); +} + +floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm) +{ + return floatx80_exp(floatx80_mul(rFm,floatx80_ln(rFn))); +} + +floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm) +{ + return floatx80_arctan(floatx80_div(rFn,rFm)); +} +#endif diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpa11.c linux/arch/arm/nwfpe/fpa11.c --- v2.3.6/linux/arch/arm/nwfpe/fpa11.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/fpa11.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,206 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#include "config.h" +#include "fpa11.h" +#include "milieu.h" +#include "fpopcode.h" + +#include "fpmodule.h" +#include "fpmodule.inl" + +/* forward declarations */ +unsigned int EmulateCPDO(const unsigned int); +unsigned int EmulateCPDT(const unsigned int); +unsigned int EmulateCPRT(const unsigned int); + +/* Emulator registers */ +FPA11 *fpa11; + +/* Reset the FPA11 chip. Called to initialize and reset the emulator. */ +void resetFPA11(void) +{ + int i; + /* initialize the registers */ + for (i=0;i<=7;i++) + { + fpa11->fpreg[i].fType = typeNone; + } + + /* FPSR: set system id to FP_EMULATOR, clear all other bits */ + fpa11->fpsr = FP_EMULATOR; + + /* FPCR: set SB, AB and DA bits, clear all others */ +#if MAINTAIN_FPCR + fpa11->fpcr = MASK_RESET; +#endif +} + +void SetRoundingMode(const unsigned int opcode) +{ +#if MAINTAIN_FPCR + fpa11->fpcr &= ~MASK_ROUNDING_MODE; +#endif + switch (opcode & MASK_ROUNDING_MODE) + { + default: + case ROUND_TO_NEAREST: + float_rounding_mode = float_round_nearest_even; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_TO_NEAREST; +#endif + break; + + case ROUND_TO_PLUS_INFINITY: + float_rounding_mode = float_round_up; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_TO_PLUS_INFINITY; +#endif + break; + + case ROUND_TO_MINUS_INFINITY: + float_rounding_mode = float_round_down; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_TO_MINUS_INFINITY; +#endif + break; + + case ROUND_TO_ZERO: + float_rounding_mode = float_round_to_zero; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_TO_ZERO; +#endif + break; + } +} + +void SetRoundingPrecision(const unsigned int opcode) +{ +#if MAINTAIN_FPCR + fpa11->fpcr &= ~MASK_ROUNDING_PRECISION; +#endif + switch (opcode & MASK_ROUNDING_PRECISION) + { + case ROUND_SINGLE: + floatx80_rounding_precision = 32; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_SINGLE; +#endif + break; + + case ROUND_DOUBLE: + floatx80_rounding_precision = 64; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_DOUBLE; +#endif + break; + + case ROUND_EXTENDED: + floatx80_rounding_precision = 80; +#if MAINTAIN_FPCR + fpa11->fpcr |= ROUND_EXTENDED; +#endif + break; + + default: floatx80_rounding_precision = 80; + } +} + +/* Emulate the instruction in the opcode. */ +unsigned int EmulateAll(unsigned int opcode) +{ + unsigned int nRc = 0; + + if (fpa11->initflag == 0) /* good place for __builtin_expect */ + { + resetFPA11(); + SetRoundingMode(ROUND_TO_NEAREST); + SetRoundingPrecision(ROUND_EXTENDED); + fpa11->initflag = 1; + } + + if (TEST_OPCODE(opcode,MASK_CPRT)) + { + /* Emulate conversion opcodes. */ + /* Emulate register transfer opcodes. */ + /* Emulate comparison opcodes. */ + nRc = EmulateCPRT(opcode); + } + else if (TEST_OPCODE(opcode,MASK_CPDO)) + { + /* Emulate monadic arithmetic opcodes. */ + /* Emulate dyadic arithmetic opcodes. */ + nRc = EmulateCPDO(opcode); + } + else if (TEST_OPCODE(opcode,MASK_CPDT)) + { + /* Emulate load/store opcodes. */ + /* Emulate load/store multiple opcodes. */ + nRc = EmulateCPDT(opcode); + } + else + { + /* Invalid instruction detected. Return FALSE. */ + nRc = 0; + } + + return(nRc); +} + +#if 0 +unsigned int EmulateAll1(unsigned int opcode) +{ + switch ((opcode >> 24) & 0xf) + { + case 0xc: + case 0xd: + if ((opcode >> 20) & 0x1) + { + switch ((opcode >> 8) & 0xf) + { + case 0x1: return PerformLDF(opcode); break; + case 0x2: return PerformLFM(opcode); break; + default: return 0; + } + } + else + { + switch ((opcode >> 8) & 0xf) + { + case 0x1: return PerformSTF(opcode); break; + case 0x2: return PerformSFM(opcode); break; + default: return 0; + } + } + break; + + case 0xe: + if (opcode & 0x10) + return EmulateCPDO(opcode); + else + return EmulateCPRT(opcode); + break; + + default: return 0; + } +} +#endif + diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpa11.h linux/arch/arm/nwfpe/fpa11.h --- v2.3.6/linux/arch/arm/nwfpe/fpa11.h Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/fpa11.h Thu Jun 17 01:11:35 1999 @@ -0,0 +1,61 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#ifndef __FPA11_H__ +#define __FPA11_H__ + +/* includes */ +#include "fpsr.h" /* FP control and status register definitions */ +#include "softfloat.h" + +#define typeNone 0x00 +#define typeSingle 0x01 +#define typeDouble 0x02 +#define typeExtended 0x03 + +typedef struct tagFPREG { + unsigned int fType; + union { + float32 fSingle; + float64 fDouble; + floatx80 fExtended; + } fValue; +} FPREG; + +/* FPA11 device model */ +typedef struct tagFPA11 { + int initflag; /* this is special. The kernel guarantees + to set it to 0 when a thread is launched, + so we can use it to detect whether this + instance of the emulator needs to be + initialised. */ + FPREG fpreg[8]; /* 8 floating point registers */ + FPSR fpsr; /* floating point status register */ + FPCR fpcr; /* floating point control register */ +} FPA11; + +extern void resetFPA11(void); +extern void SetRoundingMode(const unsigned int); +extern void SetRoundingPrecision(const unsigned int); + +extern FPA11 *fpa11; + +#endif diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpa11.inl linux/arch/arm/nwfpe/fpa11.inl --- v2.3.6/linux/arch/arm/nwfpe/fpa11.inl Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/fpa11.inl Thu Jun 17 01:11:35 1999 @@ -0,0 +1,47 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#include "fpa11.h" + +/* Read and write floating point status register */ +extern __inline__ unsigned int readFPSR(void) +{ + return(fpa11->fpsr); +} + +extern __inline__ void writeFPSR(FPSR reg) +{ + /* the sysid byte in the status register is readonly */ + fpa11->fpsr = (fpa11->fpsr & MASK_SYSID) | (reg & ~MASK_SYSID); +} + +/* Read and write floating point control register */ +extern __inline__ FPCR readFPCR(void) +{ + /* clear SB, AB and DA bits before returning FPCR */ + return(fpa11->fpcr & ~MASK_RFC); +} + +extern __inline__ void writeFPCR(FPCR reg) +{ + fpa11->fpcr &= ~MASK_WFC; /* clear SB, AB and DA bits */ + fpa11->fpcr |= (reg & MASK_WFC); /* write SB, AB and DA bits */ +} diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpa11_cpdo.c linux/arch/arm/nwfpe/fpa11_cpdo.c --- v2.3.6/linux/arch/arm/nwfpe/fpa11_cpdo.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/fpa11_cpdo.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,117 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#include "config.h" +#include "fpa11.h" +#include "fpopcode.h" + +unsigned int SingleCPDO(const unsigned int opcode); +unsigned int DoubleCPDO(const unsigned int opcode); +unsigned int ExtendedCPDO(const unsigned int opcode); + +unsigned int EmulateCPDO(const unsigned int opcode) +{ + unsigned int Fd, nType, nDest, nRc = 1; + + //fp_printk("EmulateCPDO(0x%08x)\n",opcode); + + /* Get the destination size. If not valid let Linux perform + an invalid instruction trap. */ + nDest = getDestinationSize(opcode); + if (typeNone == nDest) return 0; + + SetRoundingMode(opcode); + + /* Compare the size of the operands in Fn and Fm. + Choose the largest size and perform operations in that size, + in order to make use of all the precision of the operands. + If Fm is a constant, we just grab a constant of a size + matching the size of the operand in Fn. */ + if (MONADIC_INSTRUCTION(opcode)) + nType = nDest; + else + nType = fpa11->fpreg[getFn(opcode)].fType; + + if (!CONSTANT_FM(opcode)) + { + register unsigned int Fm = getFm(opcode); + if (nType < fpa11->fpreg[Fm].fType) + { + nType = fpa11->fpreg[Fm].fType; + } + } + + switch (nType) + { + case typeSingle : nRc = SingleCPDO(opcode); break; + case typeDouble : nRc = DoubleCPDO(opcode); break; + case typeExtended : nRc = ExtendedCPDO(opcode); break; + default : nRc = 0; + } + + /* If the operation succeeded, check to see if the result in the + destination register is the correct size. If not force it + to be. */ + Fd = getFd(opcode); + nType = fpa11->fpreg[Fd].fType; + if ((0 != nRc) && (nDest != nType)) + { + switch (nDest) + { + case typeSingle: + { + if (typeDouble == nType) + fpa11->fpreg[Fd].fValue.fSingle = + float64_to_float32(fpa11->fpreg[Fd].fValue.fDouble); + else + fpa11->fpreg[Fd].fValue.fSingle = + floatx80_to_float32(fpa11->fpreg[Fd].fValue.fExtended); + } + break; + + case typeDouble: + { + if (typeSingle == nType) + fpa11->fpreg[Fd].fValue.fDouble = + float32_to_float64(fpa11->fpreg[Fd].fValue.fSingle); + else + fpa11->fpreg[Fd].fValue.fDouble = + floatx80_to_float64(fpa11->fpreg[Fd].fValue.fExtended); + } + break; + + case typeExtended: + { + if (typeSingle == nType) + fpa11->fpreg[Fd].fValue.fExtended = + float32_to_floatx80(fpa11->fpreg[Fd].fValue.fSingle); + else + fpa11->fpreg[Fd].fValue.fExtended = + float64_to_floatx80(fpa11->fpreg[Fd].fValue.fDouble); + } + break; + } + + fpa11->fpreg[Fd].fType = nDest; + } + + return nRc; +} diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpa11_cpdt.c linux/arch/arm/nwfpe/fpa11_cpdt.c --- v2.3.6/linux/arch/arm/nwfpe/fpa11_cpdt.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/fpa11_cpdt.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,330 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + (c) Philip Blundell, 1998 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#include "config.h" +#include "softfloat.h" +#include "fpopcode.h" +#include "fpa11.h" +#include "fpmodule.h" +#include "fpmodule.inl" + +#include + +extern __inline__ +void loadSingle(const unsigned int Fn,const unsigned int *pMem) +{ + fpa11->fpreg[Fn].fType = typeSingle; + get_user(fpa11->fpreg[Fn].fValue.fSingle, pMem); +} + +extern __inline__ +void loadDouble(const unsigned int Fn,const unsigned int *pMem) +{ + unsigned int *p; + p = (unsigned int*)&fpa11->fpreg[Fn].fValue.fDouble; + fpa11->fpreg[Fn].fType = typeDouble; + get_user(p[0], &pMem[1]); + get_user(p[1], &pMem[0]); /* sign & exponent */ +} + +extern __inline__ +void loadExtended(const unsigned int Fn,const unsigned int *pMem) +{ + unsigned int *p; + p = (unsigned int*)&fpa11->fpreg[Fn].fValue.fExtended; + fpa11->fpreg[Fn].fType = typeExtended; + get_user(p[0], &pMem[0]); /* sign & exponent */ + get_user(p[1], &pMem[2]); /* ls bits */ + get_user(p[2], &pMem[1]); /* ms bits */ +} + +extern __inline__ +void loadMultiple(const unsigned int Fn,const unsigned int *pMem) +{ + register unsigned int *p; + unsigned long x; + + p = (unsigned int*)&(fpa11->fpreg[Fn].fValue); + get_user(x, &pMem[0]); + fpa11->fpreg[Fn].fType = (x >> 14) & 0x00000003; + + switch (fpa11->fpreg[Fn].fType) + { + case typeSingle: + case typeDouble: + { + get_user(p[0], &pMem[2]); /* Single */ + get_user(p[1], &pMem[1]); /* double msw */ + p[2] = 0; /* empty */ + } + break; + + case typeExtended: + { + get_user(p[1], &pMem[2]); + get_user(p[2], &pMem[1]); /* msw */ + p[0] = (x & 0x80003fff); + } + break; + } +} + +extern __inline__ +void storeSingle(const unsigned int Fn,unsigned int *pMem) +{ + float32 val; + register unsigned int *p = (unsigned int*)&val; + + switch (fpa11->fpreg[Fn].fType) + { + case typeDouble: + val = float64_to_float32(fpa11->fpreg[Fn].fValue.fDouble); + break; + + case typeExtended: + val = floatx80_to_float32(fpa11->fpreg[Fn].fValue.fExtended); + break; + + default: val = fpa11->fpreg[Fn].fValue.fSingle; + } + + put_user(p[0], pMem); +} + +extern __inline__ +void storeDouble(const unsigned int Fn,unsigned int *pMem) +{ + float64 val; + register unsigned int *p = (unsigned int*)&val; + + switch (fpa11->fpreg[Fn].fType) + { + case typeSingle: + val = float32_to_float64(fpa11->fpreg[Fn].fValue.fSingle); + break; + + case typeExtended: + val = floatx80_to_float64(fpa11->fpreg[Fn].fValue.fExtended); + break; + + default: val = fpa11->fpreg[Fn].fValue.fDouble; + } + put_user(p[1], &pMem[0]); /* msw */ + put_user(p[0], &pMem[1]); /* lsw */ +} + +extern __inline__ +void storeExtended(const unsigned int Fn,unsigned int *pMem) +{ + floatx80 val; + register unsigned int *p = (unsigned int*)&val; + + switch (fpa11->fpreg[Fn].fType) + { + case typeSingle: + val = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle); + break; + + case typeDouble: + val = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble); + break; + + default: val = fpa11->fpreg[Fn].fValue.fExtended; + } + + put_user(p[0], &pMem[0]); /* sign & exp */ + put_user(p[1], &pMem[2]); + put_user(p[2], &pMem[1]); /* msw */ +} + +extern __inline__ +void storeMultiple(const unsigned int Fn,unsigned int *pMem) +{ + register unsigned int nType, *p; + + p = (unsigned int*)&(fpa11->fpreg[Fn].fValue); + nType = fpa11->fpreg[Fn].fType; + + switch (nType) + { + case typeSingle: + case typeDouble: + { + put_user(p[0], &pMem[2]); /* single */ + put_user(p[1], &pMem[1]); /* double msw */ + put_user(nType << 14, &pMem[0]); + } + break; + + case typeExtended: + { + put_user(p[2], &pMem[1]); /* msw */ + put_user(p[1], &pMem[2]); + put_user((p[0] & 0x80003fff) | (nType << 14), &pMem[0]); + } + break; + } +} + +unsigned int PerformLDF(const unsigned int opcode) +{ + unsigned int *pBase, *pAddress, *pFinal, nRc = 1; + + //fp_printk("PerformLDF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode)); + + pBase = (unsigned int*)readRegister(getRn(opcode)); + if (REG_PC == getRn(opcode)) pBase += 2; + + pFinal = pBase; + if (BIT_UP_SET(opcode)) + pFinal += getOffset(opcode); + else + pFinal -= getOffset(opcode); + + if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase; + + switch (opcode & MASK_TRANSFER_LENGTH) + { + case TRANSFER_SINGLE : loadSingle(getFd(opcode),pAddress); break; + case TRANSFER_DOUBLE : loadDouble(getFd(opcode),pAddress); break; + case TRANSFER_EXTENDED: loadExtended(getFd(opcode),pAddress); break; + default: nRc = 0; + } + + if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal); + return nRc; +} + +unsigned int PerformSTF(const unsigned int opcode) +{ + unsigned int *pBase, *pAddress, *pFinal, nRc = 1; + + //fp_printk("PerformSTF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode)); + SetRoundingMode(ROUND_TO_NEAREST); + + pBase = (unsigned int*)readRegister(getRn(opcode)); + if (REG_PC == getRn(opcode)) pBase += 2; + + pFinal = pBase; + if (BIT_UP_SET(opcode)) + pFinal += getOffset(opcode); + else + pFinal -= getOffset(opcode); + + if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase; + + switch (opcode & MASK_TRANSFER_LENGTH) + { + case TRANSFER_SINGLE : storeSingle(getFd(opcode),pAddress); break; + case TRANSFER_DOUBLE : storeDouble(getFd(opcode),pAddress); break; + case TRANSFER_EXTENDED: storeExtended(getFd(opcode),pAddress); break; + default: nRc = 0; + } + + if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal); + return nRc; +} + +unsigned int PerformLFM(const unsigned int opcode) +{ + unsigned int i, Fd, *pBase, *pAddress, *pFinal; + pBase = (unsigned int*)readRegister(getRn(opcode)); + if (REG_PC == getRn(opcode)) pBase += 2; + + pFinal = pBase; + if (BIT_UP_SET(opcode)) + pFinal += getOffset(opcode); + else + pFinal -= getOffset(opcode); + + if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase; + + Fd = getFd(opcode); + for (i=getRegisterCount(opcode);i>0;i--) + { + loadMultiple(Fd,pAddress); + pAddress += 3; Fd++; + if (Fd == 8) Fd = 0; + } + + if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal); + return 1; +} + +unsigned int PerformSFM(const unsigned int opcode) +{ + unsigned int i, Fd, *pBase, *pAddress, *pFinal; + + pBase = (unsigned int*)readRegister(getRn(opcode)); + if (REG_PC == getRn(opcode)) pBase += 2; + + pFinal = pBase; + if (BIT_UP_SET(opcode)) + pFinal += getOffset(opcode); + else + pFinal -= getOffset(opcode); + + if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase; + + Fd = getFd(opcode); + for (i=getRegisterCount(opcode);i>0;i--) + { + storeMultiple(Fd,pAddress); + pAddress += 3; Fd++; + if (Fd == 8) Fd = 0; + } + + if (WRITE_BACK(opcode)) writeRegister(getRn(opcode),(unsigned int)pFinal); + return 1; +} + +#if 1 +unsigned int EmulateCPDT(const unsigned int opcode) +{ + unsigned int nRc = 0; + + //fp_printk("EmulateCPDT(0x%08x)\n",opcode); + + if (LDF_OP(opcode)) + { + nRc = PerformLDF(opcode); + } + else if (LFM_OP(opcode)) + { + nRc = PerformLFM(opcode); + } + else if (STF_OP(opcode)) + { + nRc = PerformSTF(opcode); + } + else if (SFM_OP(opcode)) + { + nRc = PerformSFM(opcode); + } + else + { + nRc = 0; + } + + return nRc; +} +#endif diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpa11_cprt.c linux/arch/arm/nwfpe/fpa11_cprt.c --- v2.3.6/linux/arch/arm/nwfpe/fpa11_cprt.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/fpa11_cprt.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,313 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + (c) Philip Blundell, 1999 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#include "config.h" +#include "milieu.h" +#include "softfloat.h" +#include "fpopcode.h" +#include "fpa11.h" +#include "fpa11.inl" +#include "fpmodule.h" +#include "fpmodule.inl" + +extern flag floatx80_is_nan(floatx80); +extern flag float64_is_nan( float64); +extern flag float32_is_nan( float32); + +void SetRoundingMode(const unsigned int opcode); + +unsigned int PerformFLT(const unsigned int opcode); +unsigned int PerformFIX(const unsigned int opcode); + +static unsigned int +PerformComparison(const unsigned int opcode); + +unsigned int EmulateCPRT(const unsigned int opcode) +{ + unsigned int nRc = 1; + + //fp_printk("EmulateCPRT(0x%08x)\n",opcode); + + if (opcode & 0x800000) + { + /* This is some variant of a comparison (PerformComparison will + sort out which one). Since most of the other CPRT + instructions are oddball cases of some sort or other it makes + sense to pull this out into a fast path. */ + return PerformComparison(opcode); + } + + /* Hint to GCC that we'd like a jump table rather than a load of CMPs */ + switch ((opcode & 0x700000) >> 20) + { + case FLT_CODE >> 20: nRc = PerformFLT(opcode); break; + case FIX_CODE >> 20: nRc = PerformFIX(opcode); break; + + case WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break; + case RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break; + +#if 0 + /* ?? Not at all sure about the mode checks here. Linux never + calls the emulator from a non-USR fault but we always run in SVC + mode. Is there even any point trying to emulate the way FPA11 + behaves in this respect? + + No - and I quote: 'The FPCR may only be present in some + implementations: it is there to control the hardware in an + implementation-specific manner, ... The user mode of the + ARM is not permitted to use this register, and the WFC and + RFC instructions will trap if tried from user mode.' + Therefore, we do not provide the RFC and WFC instructions. + (rmk, 3/05/1999) + */ + case WFC_CODE >> 20: + { + int mode = 0; + __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode)); + nRc = (0x13 == mode) ? 1 : 0; /* in SVC processor mode? */ + if (nRc) writeFPCR(readRegister(getRd(opcode))); + } + break; + + case RFC_CODE >> 20: + { + int mode = 0; + __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode)); + nRc = (0x13 == mode) ? 1 : 0; /* in SVC processor mode? */ + if (nRc) writeRegister(getRd(opcode),readFPCR()); break; + } + break; +#endif + + default: nRc = 0; + } + + return nRc; +} + +unsigned int PerformFLT(const unsigned int opcode) +{ + unsigned int nRc = 1; + SetRoundingMode(opcode); + SetRoundingPrecision(opcode); + + switch (opcode & MASK_ROUNDING_PRECISION) + { + case ROUND_SINGLE: + { + fpa11->fpreg[getFn(opcode)].fType = typeSingle; + fpa11->fpreg[getFn(opcode)].fValue.fSingle = + int32_to_float32(readRegister(getRd(opcode))); + } + break; + + case ROUND_DOUBLE: + { + fpa11->fpreg[getFn(opcode)].fType = typeDouble; + fpa11->fpreg[getFn(opcode)].fValue.fDouble = + int32_to_float64(readRegister(getRd(opcode))); + } + break; + + case ROUND_EXTENDED: + { + fpa11->fpreg[getFn(opcode)].fType = typeExtended; + fpa11->fpreg[getFn(opcode)].fValue.fExtended = + int32_to_floatx80(readRegister(getRd(opcode))); + } + break; + + default: nRc = 0; + } + + return nRc; +} + +unsigned int PerformFIX(const unsigned int opcode) +{ + unsigned int nRc = 1; + unsigned int Fn = getFm(opcode); + + SetRoundingMode(opcode); + + switch (fpa11->fpreg[Fn].fType) + { + case typeSingle: + { + writeRegister(getRd(opcode), + float32_to_int32(fpa11->fpreg[Fn].fValue.fSingle)); + } + break; + + case typeDouble: + { + writeRegister(getRd(opcode), + float64_to_int32(fpa11->fpreg[Fn].fValue.fDouble)); + } + break; + + case typeExtended: + { + writeRegister(getRd(opcode), + floatx80_to_int32(fpa11->fpreg[Fn].fValue.fExtended)); + } + break; + + default: nRc = 0; + } + + return nRc; +} + + +static unsigned int __inline__ +PerformComparisonOperation(floatx80 Fn, floatx80 Fm) +{ + unsigned int flags = 0; + + /* test for less than condition */ + if (floatx80_lt(Fn,Fm)) + { + flags |= CC_NEGATIVE; + } + + /* test for equal condition */ + if (floatx80_eq(Fn,Fm)) + { + flags |= CC_ZERO; + } + + /* test for greater than or equal condition */ + if (floatx80_lt(Fm,Fn)) + { + flags |= CC_CARRY; + } + + writeConditionCodes(flags); + return 1; +} + +/* This instruction sets the flags N, Z, C, V in the FPSR. */ + +static unsigned int PerformComparison(const unsigned int opcode) +{ + unsigned int Fn, Fm; + floatx80 rFn, rFm; + int e_flag = opcode & 0x400000; /* 1 if CxFE */ + int n_flag = opcode & 0x200000; /* 1 if CNxx */ + unsigned int flags = 0; + + //fp_printk("PerformComparison(0x%08x)\n",opcode); + + Fn = getFn(opcode); + Fm = getFm(opcode); + + /* Check for unordered condition and convert all operands to 80-bit + format. + ?? Might be some mileage in avoiding this conversion if possible. + Eg, if both operands are 32-bit, detect this and do a 32-bit + comparison (cheaper than an 80-bit one). */ + switch (fpa11->fpreg[Fn].fType) + { + case typeSingle: + //fp_printk("single.\n"); + if (float32_is_nan(fpa11->fpreg[Fn].fValue.fSingle)) + goto unordered; + rFn = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle); + break; + + case typeDouble: + //fp_printk("double.\n"); + if (float64_is_nan(fpa11->fpreg[Fn].fValue.fDouble)) + goto unordered; + rFn = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble); + break; + + case typeExtended: + //fp_printk("extended.\n"); + if (floatx80_is_nan(fpa11->fpreg[Fn].fValue.fExtended)) + goto unordered; + rFn = fpa11->fpreg[Fn].fValue.fExtended; + break; + + default: return 0; + } + + if (CONSTANT_FM(opcode)) + { + //fp_printk("Fm is a constant: #%d.\n",Fm); + rFm = getExtendedConstant(Fm); + if (floatx80_is_nan(rFm)) + goto unordered; + } + else + { + //fp_printk("Fm = r%d which contains a ",Fm); + switch (fpa11->fpreg[Fm].fType) + { + case typeSingle: + //fp_printk("single.\n"); + if (float32_is_nan(fpa11->fpreg[Fm].fValue.fSingle)) + goto unordered; + rFm = float32_to_floatx80(fpa11->fpreg[Fm].fValue.fSingle); + break; + + case typeDouble: + //fp_printk("double.\n"); + if (float64_is_nan(fpa11->fpreg[Fm].fValue.fDouble)) + goto unordered; + rFm = float64_to_floatx80(fpa11->fpreg[Fm].fValue.fDouble); + break; + + case typeExtended: + //fp_printk("extended.\n"); + if (floatx80_is_nan(fpa11->fpreg[Fm].fValue.fExtended)) + goto unordered; + rFm = fpa11->fpreg[Fm].fValue.fExtended; + break; + + default: return 0; + } + } + + if (n_flag) + { + rFm.high ^= 0x8000; + } + + return PerformComparisonOperation(rFn,rFm); + + unordered: + /* ?? The FPA data sheet is pretty vague about this, in particular + about whether the non-E comparisons can ever raise exceptions. + This implementation is based on a combination of what it says in + the data sheet, observation of how the Acorn emulator actually + behaves (and how programs expect it to) and guesswork. */ + flags |= CC_OVERFLOW; + + if (BIT_AC & readFPSR()) flags |= CC_CARRY; + + if (e_flag) float_raise(float_flag_invalid); + + writeConditionCodes(flags); + return 1; +} diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpmodule.c linux/arch/arm/nwfpe/fpmodule.c --- v2.3.6/linux/arch/arm/nwfpe/fpmodule.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/fpmodule.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,167 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + (c) Philip Blundell, 1998-1999 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#include "config.h" + +#ifdef MODULE +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +/* XXX */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +/* XXX */ + +#include "softfloat.h" +#include "fpopcode.h" +#include "fpmodule.h" +#include "fpa11.h" +#include "fpa11.inl" + +/* external data */ +extern FPA11 *fpa11; + +/* kernel symbols required for signal handling */ +typedef struct task_struct* PTASK; + +#ifdef MODULE +int fp_printk(const char *,...); +void fp_send_sig(unsigned long sig, PTASK p, int priv); +#if LINUX_VERSION_CODE > 0x20115 +MODULE_AUTHOR("Scott Bambrough "); +MODULE_DESCRIPTION("NWFPE floating point emulator"); +#endif + +#else +#define fp_printk printk +#define fp_send_sig send_sig +#define kern_fp_enter fp_enter +#endif + +/* kernel function prototypes required */ +void C_SYMBOL_NAME(fp_setup)(void); + +/* external declarations for saved kernel symbols */ +extern unsigned int C_SYMBOL_NAME(kern_fp_enter); + +/* forward declarations */ +extern void nwfpe_enter(void); + +/* Original value of fp_enter from kernel before patched by fpe_init. */ +static unsigned int orig_fp_enter; + +/* Address of user registers on the kernel stack. */ +unsigned int *userRegisters; + +void __init C_SYMBOL_NAME(fpe_version)(void) +{ + static const char szTitle[] = "<4>NetWinder Floating Point Emulator "; + static const char szVersion[] = "V0.94.1 "; + static const char szCopyright[] = "(c) 1998 Corel Computer Corp.\n"; + C_SYMBOL_NAME(fp_printk)(szTitle); + C_SYMBOL_NAME(fp_printk)(szVersion); + C_SYMBOL_NAME(fp_printk)(szCopyright); +} + +int __init fpe_init(void) +{ + /* Display title, version and copyright information. */ + C_SYMBOL_NAME(fpe_version)(); + + /* Save pointer to the old FP handler and then patch ourselves in */ + orig_fp_enter = C_SYMBOL_NAME(kern_fp_enter); + C_SYMBOL_NAME(kern_fp_enter) = (unsigned int)C_SYMBOL_NAME(nwfpe_enter); + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + return(fpe_init()); +} + +void cleanup_module(void) +{ + /* Restore the values we saved earlier. */ + C_SYMBOL_NAME(kern_fp_enter) = orig_fp_enter; +} +#endif + +#define _ARM_pc 60 +#define _ARM_cpsr 64 + +/* +ScottB: November 4, 1998 + +Moved this function out of softfloat-specialize into fpmodule.c. +This effectively isolates all the changes required for integrating with the +Linux kernel into fpmodule.c. Porting to NetBSD should only require modifying +fpmodule.c to integrate with the NetBSD kernel (I hope!). + +[1/1/99: Not quite true any more unfortunately. There is Linux-specific +code to access data in user space in some other source files at the +moment. --philb] + +float_exception_flags is a global variable in SoftFloat. + +This function is called by the SoftFloat routines to raise a floating +point exception. We check the trap enable byte in the FPSR, and raise +a SIGFPE exception if necessary. If not the relevant bits in the +cumulative exceptions flag byte are set and we return. +*/ + +void float_raise(signed char flags) +{ +#if 0 + printk(KERN_DEBUG "NWFPE: exception %08x at %08x from %08x\n", flags, + __builtin_return_address(0), userRegisters[15]); +#endif + + float_exception_flags |= flags; + if (readFPSR() & (flags << 16)) + { + /* raise exception */ + C_SYMBOL_NAME(fp_send_sig)(SIGFPE,C_SYMBOL_NAME(current),1); + } + else + { + /* set the cumulative exceptions flags */ + writeFPSR(flags); + } +} diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpmodule.h linux/arch/arm/nwfpe/fpmodule.h --- v2.3.6/linux/arch/arm/nwfpe/fpmodule.h Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/fpmodule.h Thu Jun 17 01:11:35 1999 @@ -0,0 +1,53 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#ifndef __FPMODULE_H__ +#define __FPMODULE_H__ + +#include + +#ifdef CONFIG_CPU_32 +#define REG_ORIG_R0 17 +#define REG_CPSR 16 +#else +#define REG_ORIG_R0 16 +#define REG_CPSR 15 +#endif + +#define REG_PC 15 +#define REG_LR 14 +#define REG_SP 13 +#define REG_IP 12 +#define REG_FP 11 +#define REG_R10 10 +#define REG_R9 9 +#define REG_R9 9 +#define REG_R8 8 +#define REG_R7 7 +#define REG_R6 6 +#define REG_R5 5 +#define REG_R4 4 +#define REG_R3 3 +#define REG_R2 2 +#define REG_R1 1 +#define REG_R0 0 + +#endif diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpmodule.inl linux/arch/arm/nwfpe/fpmodule.inl --- v2.3.6/linux/arch/arm/nwfpe/fpmodule.inl Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/fpmodule.inl Thu Jun 17 01:11:35 1999 @@ -0,0 +1,88 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + + Direct questions, comments to Scott Bambrough + + 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. + */ + +/* Address of user registers on the kernel stack. */ +extern unsigned int *userRegisters; + +extern __inline__ +unsigned int readRegister(const unsigned int nReg) +{ + /* Note: The CPU thinks it has dealt with the current instruction. As + a result the program counter has been advanced to the next + instruction, and points 4 bytes beyond the actual instruction + that caused the invalid instruction trap to occur. We adjust + for this in this routine. LDF/STF instructions with Rn = PC + depend on the PC being correct, as they use PC+8 in their + address calculations. */ + unsigned int val = userRegisters[nReg]; + + if (REG_PC == nReg) + val -= 4; + + return val; +} + +extern __inline__ +void writeRegister(const unsigned int nReg, const unsigned int val) +{ + userRegisters[nReg] = val; +} + +extern __inline__ +unsigned int readCPSR(void) +{ + return (readRegister(REG_CPSR)); +} + +extern __inline__ +void writeCPSR(const unsigned int val) +{ + writeRegister(REG_CPSR, val); +} + +extern __inline__ +unsigned int readConditionCodes(void) +{ +#ifdef __FPEM_TEST__ + return (0); +#else + return (readCPSR() & CC_MASK); +#endif +} + +extern __inline__ +void writeConditionCodes(const unsigned int val) +{ + unsigned int rval; + + /* + * Operate directly on userRegisters since + * the CPSR may be the PC register itself. + */ + rval = userRegisters[REG_CPSR] & ~CC_MASK; + userRegisters[REG_CPSR] = rval | (val & CC_MASK); +} + +extern __inline__ +unsigned int readMemoryInt(unsigned int *pMem) +{ + return *pMem; +} diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpopcode.c linux/arch/arm/nwfpe/fpopcode.c --- v2.3.6/linux/arch/arm/nwfpe/fpopcode.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/fpopcode.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,164 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#include "config.h" +#include "softfloat.h" +#include "fpopcode.h" +#include "fpsr.h" +#include "fpa11.h" +#include "fpmodule.h" +#include "fpmodule.inl" + +static floatx80 floatx80Constant[] = { + { 0x0000, 0x0000000000000000ULL}, /* extended 0.0 */ + { 0x3fff, 0x8000000000000000ULL}, /* extended 1.0 */ + { 0x4000, 0x8000000000000000ULL}, /* extended 2.0 */ + { 0x4000, 0xc000000000000000ULL}, /* extended 3.0 */ + { 0x4001, 0x8000000000000000ULL}, /* extended 4.0 */ + { 0x4001, 0xa000000000000000ULL}, /* extended 5.0 */ + { 0x3ffe, 0x8000000000000000ULL}, /* extended 0.5 */ + { 0x4002, 0xa000000000000000ULL} /* extended 10.0 */ +}; + +static float64 float64Constant[] = { + 0x0000000000000000ULL, /* double 0.0 */ + 0x3ff0000000000000ULL, /* double 1.0 */ + 0x4000000000000000ULL, /* double 2.0 */ + 0x4008000000000000ULL, /* double 3.0 */ + 0x4010000000000000ULL, /* double 4.0 */ + 0x4014000000000000ULL, /* double 5.0 */ + 0x3fe0000000000000ULL, /* double 0.5 */ + 0x4024000000000000ULL /* double 10.0 */ +}; + +static float32 float32Constant[] = { + 0x00000000, /* single 0.0 */ + 0x3f800000, /* single 1.0 */ + 0x40000000, /* single 2.0 */ + 0x40400000, /* single 3.0 */ + 0x40800000, /* single 4.0 */ + 0x40a00000, /* single 5.0 */ + 0x3f000000, /* single 0.5 */ + 0x41200000 /* single 10.0 */ +}; + +floatx80 getExtendedConstant(const unsigned int nIndex) +{ + return floatx80Constant[nIndex]; +} + +float64 getDoubleConstant(const unsigned int nIndex) +{ + return float64Constant[nIndex]; +} + +float32 getSingleConstant(const unsigned int nIndex) +{ + return float32Constant[nIndex]; +} + +unsigned int getTransferLength(const unsigned int opcode) +{ + unsigned int nRc; + + switch (opcode & MASK_TRANSFER_LENGTH) + { + case 0x00000000: nRc = 1; break; /* single precision */ + case 0x00008000: nRc = 2; break; /* double precision */ + case 0x00400000: nRc = 3; break; /* extended precision */ + default: nRc = 0; + } + + return(nRc); +} + +unsigned int getRegisterCount(const unsigned int opcode) +{ + unsigned int nRc; + + switch (opcode & MASK_REGISTER_COUNT) + { + case 0x00000000: nRc = 4; break; + case 0x00008000: nRc = 1; break; + case 0x00400000: nRc = 2; break; + case 0x00408000: nRc = 3; break; + default: nRc = 0; + } + + return(nRc); +} + +unsigned int getRoundingPrecision(const unsigned int opcode) +{ + unsigned int nRc; + + switch (opcode & MASK_ROUNDING_PRECISION) + { + case 0x00000000: nRc = 1; break; + case 0x00000080: nRc = 2; break; + case 0x00080000: nRc = 3; break; + default: nRc = 0; + } + + return(nRc); +} + +unsigned int getDestinationSize(const unsigned int opcode) +{ + unsigned int nRc; + + switch (opcode & MASK_DESTINATION_SIZE) + { + case 0x00000000: nRc = typeSingle; break; + case 0x00000080: nRc = typeDouble; break; + case 0x00080000: nRc = typeExtended; break; + default: nRc = typeNone; + } + + return(nRc); +} + +/* contition code lookup table + index into the table is test code: EQ, NE, ... LT, GT, AL, NV + bit position in short is condition code: NZCV */ +unsigned short aCC[16] = { + 0xF0F0, // EQ == Z set + 0x0F0F, // NE + 0xCCCC, // CS == C set + 0x3333, // CC + 0xFF00, // MI == N set + 0x00FF, // PL + 0xAAAA, // VS == V set + 0x5555, // VC + 0x0C0C, // HI == C set && Z clear + 0xF3F3, // LS == C clear || Z set + 0xAA55, // GE == (N==V) + 0x55AA, // LT == (N!=V) + 0x0A05, // GT == (!Z && (N==V)) + 0xF5FA, // LE == (Z || (N!=V)) + 0xFFFF, // AL always + 0 // NV +}; + +unsigned int checkCondition(const unsigned int opcode, const unsigned int ccodes) +{ + return (aCC[opcode>>28] >> (ccodes>>28)) & 1; +} diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpopcode.h linux/arch/arm/nwfpe/fpopcode.h --- v2.3.6/linux/arch/arm/nwfpe/fpopcode.h Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/fpopcode.h Thu Jun 17 01:11:35 1999 @@ -0,0 +1,376 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#ifndef __FPOPCODE_H__ +#define __FPOPCODE_H__ + +/* +ARM Floating Point Instruction Classes +| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +|c o n d|1 1 0 P|U|u|W|L| Rn |v| Fd |0|0|0|1| o f f s e t | CPDT +|c o n d|1 1 0 P|U|w|W|L| Rn |x| Fd |0|0|0|1| o f f s e t | CPDT +| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +|c o n d|1 1 1 0|a|b|c|d|e| Fn |j| Fd |0|0|0|1|f|g|h|0|i| Fm | CPDO +|c o n d|1 1 1 0|a|b|c|L|e| Fn | Rd |0|0|0|1|f|g|h|1|i| Fm | CPRT +|c o n d|1 1 1 0|a|b|c|1|e| Fn |1|1|1|1|0|0|0|1|f|g|h|1|i| Fm | comparisons +| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + +CPDT data transfer instructions + LDF, STF, LFM, SFM + +CPDO dyadic arithmetic instructions + ADF, MUF, SUF, RSF, DVF, RDF, + POW, RPW, RMF, FML, FDV, FRD, POL + +CPDO monadic arithmetic instructions + MVF, MNF, ABS, RND, SQT, LOG, LGN, EXP, + SIN, COS, TAN, ASN, ACS, ATN, URD, NRM + +CPRT joint arithmetic/data transfer instructions + FIX (arithmetic followed by load/store) + FLT (load/store followed by arithmetic) + CMF, CNF CMFE, CNFE (comparisons) + WFS, RFS (write/read floating point status register) + WFC, RFC (write/read floating point control register) + +cond condition codes +P pre/post index bit: 0 = postindex, 1 = preindex +U up/down bit: 0 = stack grows down, 1 = stack grows up +W write back bit: 1 = update base register (Rn) +L load/store bit: 0 = store, 1 = load +Rn base register +Rd destination/source register +Fd floating point destination register +Fn floating point source register +Fm floating point source register or floating point constant + +uv transfer length (TABLE 1) +wx register count (TABLE 2) +abcd arithmetic opcode (TABLES 3 & 4) +ef destination size (rounding precision) (TABLE 5) +gh rounding mode (TABLE 6) +j dyadic/monadic bit: 0 = dyadic, 1 = monadic +i constant bit: 1 = constant (TABLE 6) +*/ + +/* +TABLE 1 ++-------------------------+---+---+---------+---------+ +| Precision | u | v | FPSR.EP | length | ++-------------------------+---+---+---------+---------+ +| Single | 0 ü 0 | x | 1 words | +| Double | 1 ü 1 | x | 2 words | +| Extended | 1 ü 1 | x | 3 words | +| Packed decimal | 1 ü 1 | 0 | 3 words | +| Expanded packed decimal | 1 ü 1 | 1 | 4 words | ++-------------------------+---+---+---------+---------+ +Note: x = don't care +*/ + +/* +TABLE 2 ++---+---+---------------------------------+ +| w | x | Number of registers to transfer | ++---+---+---------------------------------+ +| 0 ü 1 | 1 | +| 1 ü 0 | 2 | +| 1 ü 1 | 3 | +| 0 ü 0 | 4 | ++---+---+---------------------------------+ +*/ + +/* +TABLE 3: Dyadic Floating Point Opcodes ++---+---+---+---+----------+-----------------------+-----------------------+ +| a | b | c | d | Mnemonic | Description | Operation | ++---+---+---+---+----------+-----------------------+-----------------------+ +| 0 | 0 | 0 | 0 | ADF | Add | Fd := Fn + Fm | +| 0 | 0 | 0 | 1 | MUF | Multiply | Fd := Fn * Fm | +| 0 | 0 | 1 | 0 | SUF | Subtract | Fd := Fn - Fm | +| 0 | 0 | 1 | 1 | RSF | Reverse subtract | Fd := Fm - Fn | +| 0 | 1 | 0 | 0 | DVF | Divide | Fd := Fn / Fm | +| 0 | 1 | 0 | 1 | RDF | Reverse divide | Fd := Fm / Fn | +| 0 | 1 | 1 | 0 | POW | Power | Fd := Fn ^ Fm | +| 0 | 1 | 1 | 1 | RPW | Reverse power | Fd := Fm ^ Fn | +| 1 | 0 | 0 | 0 | RMF | Remainder | Fd := IEEE rem(Fn/Fm) | +| 1 | 0 | 0 | 1 | FML | Fast Multiply | Fd := Fn * Fm | +| 1 | 0 | 1 | 0 | FDV | Fast Divide | Fd := Fn / Fm | +| 1 | 0 | 1 | 1 | FRD | Fast reverse divide | Fd := Fm / Fn | +| 1 | 1 | 0 | 0 | POL | Polar angle (ArcTan2) | Fd := arctan2(Fn,Fm) | +| 1 | 1 | 0 | 1 | | undefined instruction | trap | +| 1 | 1 | 1 | 0 | | undefined instruction | trap | +| 1 | 1 | 1 | 1 | | undefined instruction | trap | ++---+---+---+---+----------+-----------------------+-----------------------+ +Note: POW, RPW, POL are deprecated, and are available for backwards + compatibility only. +*/ + +/* +TABLE 4: Monadic Floating Point Opcodes ++---+---+---+---+----------+-----------------------+-----------------------+ +| a | b | c | d | Mnemonic | Description | Operation | ++---+---+---+---+----------+-----------------------+-----------------------+ +| 0 | 0 | 0 | 0 | MVF | Move | Fd := Fm | +| 0 | 0 | 0 | 1 | MNF | Move negated | Fd := - Fm | +| 0 | 0 | 1 | 0 | ABS | Absolute value | Fd := abs(Fm) | +| 0 | 0 | 1 | 1 | RND | Round to integer | Fd := int(Fm) | +| 0 | 1 | 0 | 0 | SQT | Square root | Fd := sqrt(Fm) | +| 0 | 1 | 0 | 1 | LOG | Log base 10 | Fd := log10(Fm) | +| 0 | 1 | 1 | 0 | LGN | Log base e | Fd := ln(Fm) | +| 0 | 1 | 1 | 1 | EXP | Exponent | Fd := e ^ Fm | +| 1 | 0 | 0 | 0 | SIN | Sine | Fd := sin(Fm) | +| 1 | 0 | 0 | 1 | COS | Cosine | Fd := cos(Fm) | +| 1 | 0 | 1 | 0 | TAN | Tangent | Fd := tan(Fm) | +| 1 | 0 | 1 | 1 | ASN | Arc Sine | Fd := arcsin(Fm) | +| 1 | 1 | 0 | 0 | ACS | Arc Cosine | Fd := arccos(Fm) | +| 1 | 1 | 0 | 1 | ATN | Arc Tangent | Fd := arctan(Fm) | +| 1 | 1 | 1 | 0 | URD | Unnormalized round | Fd := int(Fm) | +| 1 | 1 | 1 | 1 | NRM | Normalize | Fd := norm(Fm) | ++---+---+---+---+----------+-----------------------+-----------------------+ +Note: LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN are deprecated, and are + available for backwards compatibility only. +*/ + +/* +TABLE 5 ++-------------------------+---+---+ +| Rounding Precision | e | f | ++-------------------------+---+---+ +| IEEE Single precision | 0 ü 0 | +| IEEE Double precision | 0 ü 1 | +| IEEE Extended precision | 1 ü 0 | +| undefined (trap) | 1 ü 1 | ++-------------------------+---+---+ +*/ + +/* +TABLE 5 ++---------------------------------+---+---+ +| Rounding Mode | g | h | ++---------------------------------+---+---+ +| Round to nearest (default) | 0 ü 0 | +| Round toward plus infinity | 0 ü 1 | +| Round toward negative infinity | 1 ü 0 | +| Round toward zero | 1 ü 1 | ++---------------------------------+---+---+ +*/ + +/* +=== +=== Definitions for load and store instructions +=== +*/ + +/* bit masks */ +#define BIT_PREINDEX 0x01000000 +#define BIT_UP 0x00800000 +#define BIT_WRITE_BACK 0x00200000 +#define BIT_LOAD 0x00100000 + +/* masks for load/store */ +#define MASK_CPDT 0x0c000000 /* data processing opcode */ +#define MASK_OFFSET 0x000000ff +#define MASK_TRANSFER_LENGTH 0x00408000 +#define MASK_REGISTER_COUNT MASK_TRANSFER_LENGTH +#define MASK_COPROCESSOR 0x00000f00 + +/* Tests for transfer length */ +#define TRANSFER_SINGLE 0x00000000 +#define TRANSFER_DOUBLE 0x00008000 +#define TRANSFER_EXTENDED 0x00400000 +#define TRANSFER_PACKED MASK_TRANSFER_LENGTH + +/* Get the coprocessor number from the opcode. */ +#define getCoprocessorNumber(opcode) ((opcode & MASK_COPROCESSOR) >> 8) + +/* Get the offset from the opcode. */ +#define getOffset(opcode) (opcode & MASK_OFFSET) + +/* Tests for specific data transfer load/store opcodes. */ +#define TEST_OPCODE(opcode,mask) (((opcode) & (mask)) == (mask)) + +#define LOAD_OP(opcode) TEST_OPCODE((opcode),MASK_CPDT | BIT_LOAD) +#define STORE_OP(opcode) ((opcode & (MASK_CPDT | BIT_LOAD)) == MASK_CPDT) + +#define LDF_OP(opcode) (LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 1)) +#define LFM_OP(opcode) (LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 2)) +#define STF_OP(opcode) (STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 1)) +#define SFM_OP(opcode) (STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 2)) + +#define PREINDEXED(opcode) ((opcode & BIT_PREINDEX) != 0) +#define POSTINDEXED(opcode) ((opcode & BIT_PREINDEX) == 0) +#define BIT_UP_SET(opcode) ((opcode & BIT_UP) != 0) +#define BIT_UP_CLEAR(opcode) ((opcode & BIT_DOWN) == 0) +#define WRITE_BACK(opcode) ((opcode & BIT_WRITE_BACK) != 0) +#define LOAD(opcode) ((opcode & BIT_LOAD) != 0) +#define STORE(opcode) ((opcode & BIT_LOAD) == 0) + +/* +=== +=== Definitions for arithmetic instructions +=== +*/ +/* bit masks */ +#define BIT_MONADIC 0x00008000 +#define BIT_CONSTANT 0x00000008 + +#define CONSTANT_FM(opcode) ((opcode & BIT_CONSTANT) != 0) +#define MONADIC_INSTRUCTION(opcode) ((opcode & BIT_MONADIC) != 0) + +/* instruction identification masks */ +#define MASK_CPDO 0x0e000000 /* arithmetic opcode */ +#define MASK_ARITHMETIC_OPCODE 0x00f08000 +#define MASK_DESTINATION_SIZE 0x00080080 + +/* dyadic arithmetic opcodes. */ +#define ADF_CODE 0x00000000 +#define MUF_CODE 0x00100000 +#define SUF_CODE 0x00200000 +#define RSF_CODE 0x00300000 +#define DVF_CODE 0x00400000 +#define RDF_CODE 0x00500000 +#define POW_CODE 0x00600000 +#define RPW_CODE 0x00700000 +#define RMF_CODE 0x00800000 +#define FML_CODE 0x00900000 +#define FDV_CODE 0x00a00000 +#define FRD_CODE 0x00b00000 +#define POL_CODE 0x00c00000 +/* 0x00d00000 is an invalid dyadic arithmetic opcode */ +/* 0x00e00000 is an invalid dyadic arithmetic opcode */ +/* 0x00f00000 is an invalid dyadic arithmetic opcode */ + +/* monadic arithmetic opcodes. */ +#define MVF_CODE 0x00008000 +#define MNF_CODE 0x00108000 +#define ABS_CODE 0x00208000 +#define RND_CODE 0x00308000 +#define SQT_CODE 0x00408000 +#define LOG_CODE 0x00508000 +#define LGN_CODE 0x00608000 +#define EXP_CODE 0x00708000 +#define SIN_CODE 0x00808000 +#define COS_CODE 0x00908000 +#define TAN_CODE 0x00a08000 +#define ASN_CODE 0x00b08000 +#define ACS_CODE 0x00c08000 +#define ATN_CODE 0x00d08000 +#define URD_CODE 0x00e08000 +#define NRM_CODE 0x00f08000 + +/* +=== +=== Definitions for register transfer and comparison instructions +=== +*/ + +#define MASK_CPRT 0x0e000010 /* register transfer opcode */ +#define MASK_CPRT_CODE 0x00f00000 +#define FLT_CODE 0x00000000 +#define FIX_CODE 0x00100000 +#define WFS_CODE 0x00200000 +#define RFS_CODE 0x00300000 +#define WFC_CODE 0x00400000 +#define RFC_CODE 0x00500000 +#define CMF_CODE 0x00900000 +#define CNF_CODE 0x00b00000 +#define CMFE_CODE 0x00d00000 +#define CNFE_CODE 0x00f00000 + +/* +=== +=== Common definitions +=== +*/ + +/* register masks */ +#define MASK_Rd 0x0000f000 +#define MASK_Rn 0x000f0000 +#define MASK_Fd 0x00007000 +#define MASK_Fm 0x00000007 +#define MASK_Fn 0x00070000 + +/* condition code masks */ +#define CC_MASK 0xf0000000 +#define CC_NEGATIVE 0x80000000 +#define CC_ZERO 0x40000000 +#define CC_CARRY 0x20000000 +#define CC_OVERFLOW 0x10000000 +#define CC_EQ 0x00000000 +#define CC_NE 0x10000000 +#define CC_CS 0x20000000 +#define CC_HS CC_CS +#define CC_CC 0x30000000 +#define CC_LO CC_CC +#define CC_MI 0x40000000 +#define CC_PL 0x50000000 +#define CC_VS 0x60000000 +#define CC_VC 0x70000000 +#define CC_HI 0x80000000 +#define CC_LS 0x90000000 +#define CC_GE 0xa0000000 +#define CC_LT 0xb0000000 +#define CC_GT 0xc0000000 +#define CC_LE 0xd0000000 +#define CC_AL 0xe0000000 +#define CC_NV 0xf0000000 + +/* rounding masks/values */ +#define MASK_ROUNDING_MODE 0x00000060 +#define ROUND_TO_NEAREST 0x00000000 +#define ROUND_TO_PLUS_INFINITY 0x00000020 +#define ROUND_TO_MINUS_INFINITY 0x00000040 +#define ROUND_TO_ZERO 0x00000060 + +#define MASK_ROUNDING_PRECISION 0x00080080 +#define ROUND_SINGLE 0x00000000 +#define ROUND_DOUBLE 0x00000080 +#define ROUND_EXTENDED 0x00080000 + +/* Get the condition code from the opcode. */ +#define getCondition(opcode) (opcode >> 28) + +/* Get the source register from the opcode. */ +#define getRn(opcode) ((opcode & MASK_Rn) >> 16) + +/* Get the destination floating point register from the opcode. */ +#define getFd(opcode) ((opcode & MASK_Fd) >> 12) + +/* Get the first source floating point register from the opcode. */ +#define getFn(opcode) ((opcode & MASK_Fn) >> 16) + +/* Get the second source floating point register from the opcode. */ +#define getFm(opcode) (opcode & MASK_Fm) + +/* Get the destination register from the opcode. */ +#define getRd(opcode) ((opcode & MASK_Rd) >> 12) + +/* Get the rounding mode from the opcode. */ +#define getRoundingMode(opcode) ((opcode & MASK_ROUNDING_MODE) >> 5) + +float32 getSingleConstant(const unsigned int nIndex); +float64 getDoubleConstant(const unsigned int nIndex); +floatx80 getExtendedConstant(const unsigned int nIndex); + +unsigned int getRegisterCount(const unsigned int opcode); +unsigned int getDestinationSize(const unsigned int opcode); + +#endif diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/fpsr.h linux/arch/arm/nwfpe/fpsr.h --- v2.3.6/linux/arch/arm/nwfpe/fpsr.h Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/fpsr.h Thu Jun 17 01:11:35 1999 @@ -0,0 +1,108 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#ifndef __FPSR_H__ +#define __FPSR_H__ + +/* +The FPSR is a 32 bit register consisting of 4 parts, each exactly +one byte. + + SYSTEM ID + EXCEPTION TRAP ENABLE BYTE + SYSTEM CONTROL BYTE + CUMULATIVE EXCEPTION FLAGS BYTE + +The FPCR is a 32 bit register consisting of bit flags. +*/ + +/* SYSTEM ID +------------ +Note: the system id byte is read only */ + +typedef unsigned int FPSR; /* type for floating point status register */ +typedef unsigned int FPCR; /* type for floating point control register */ + +#define MASK_SYSID 0xff000000 +#define BIT_HARDWARE 0x80000000 +#define FP_EMULATOR 0x01000000 /* System ID for emulator */ +#define FP_ACCELERATOR 0x81000000 /* System ID for FPA11 */ + +/* EXCEPTION TRAP ENABLE BYTE +----------------------------- */ + +#define MASK_TRAP_ENABLE 0x00ff0000 +#define MASK_TRAP_ENABLE_STRICT 0x001f0000 +#define BIT_IXE 0x00100000 /* inexact exception enable */ +#define BIT_UFE 0x00080000 /* underflow exception enable */ +#define BIT_OFE 0x00040000 /* overflow exception enable */ +#define BIT_DZE 0x00020000 /* divide by zero exception enable */ +#define BIT_IOE 0x00010000 /* invalid operation exception enable */ + +/* SYSTEM CONTROL BYTE +---------------------- */ + +#define MASK_SYSTEM_CONTROL 0x0000ff00 +#define MASK_TRAP_STRICT 0x00001f00 + +#define BIT_AC 0x00100000 /* use alternative C-flag definition + for compares */ +#define BIT_EP 0x00080000 /* use expanded packed decimal format */ +#define BIT_SO 0x00040000 /* select synchronous operation of FPA */ +#define BIT_NE 0x00020000 /* NaN exception bit */ +#define BIT_ND 0x00010000 /* no denormalized numbers bit */ + +/* CUMULATIVE EXCEPTION FLAGS BYTE +---------------------------------- */ + +#define MASK_EXCEPTION_FLAGS 0x000000ff +#define MASK_EXCEPTION_FLAGS_STRICT 0x0000001f + +#define BIT_IXC 0x00000010 /* inexact exception flag */ +#define BIT_UFC 0x00000008 /* underflow exception flag */ +#define BIT_OFC 0x00000004 /* overfloat exception flag */ +#define BIT_DZC 0x00000002 /* divide by zero exception flag */ +#define BIT_IOC 0x00000001 /* invalid operation exception flag */ + +/* Floating Point Control Register +----------------------------------*/ + +#define BIT_RU 0x80000000 /* rounded up bit */ +#define BIT_IE 0x10000000 /* inexact bit */ +#define BIT_MO 0x08000000 /* mantissa overflow bit */ +#define BIT_EO 0x04000000 /* exponent overflow bit */ +#define BIT_SB 0x00000800 /* store bounce */ +#define BIT_AB 0x00000400 /* arithmetic bounce */ +#define BIT_RE 0x00000200 /* rounding exception */ +#define BIT_DA 0x00000100 /* disable FPA */ + +#define MASK_OP 0x00f08010 /* AU operation code */ +#define MASK_PR 0x00080080 /* AU precision */ +#define MASK_S1 0x00070000 /* AU source register 1 */ +#define MASK_S2 0x00000007 /* AU source register 2 */ +#define MASK_DS 0x00007000 /* AU destination register */ +#define MASK_RM 0x00000060 /* AU rounding mode */ +#define MASK_ALU 0x9cfff2ff /* only ALU can write these bits */ +#define MASK_RESET 0x00000d00 /* bits set on reset, all others cleared */ +#define MASK_WFC MASK_RESET +#define MASK_RFC ~MASK_RESET + +#endif diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/milieu.h linux/arch/arm/nwfpe/milieu.h --- v2.3.6/linux/arch/arm/nwfpe/milieu.h Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/milieu.h Thu Jun 17 01:11:35 1999 @@ -0,0 +1,48 @@ + +/* +=============================================================================== + +This C header file is part of the SoftFloat IEC/IEEE Floating-point +Arithmetic Package, Release 2. + +Written by John R. Hauser. This work was made possible in part by the +International Computer Science Institute, located at Suite 600, 1947 Center +Street, Berkeley, California 94704. Funding was partially provided by the +National Science Foundation under grant MIP-9311980. The original version +of this code was written as part of a project to build a fixed-point vector +processor in collaboration with the University of California at Berkeley, +overseen by Profs. Nelson Morgan and John Wawrzynek. More information +is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ +arithmetic/softfloat.html'. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort +has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT +TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO +PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY +AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. + +Derivative works are acceptable, even for commercial purposes, so long as +(1) they include prominent notice that the work is derivative, and (2) they +include prominent notice akin to these three paragraphs for those parts of +this code that are retained. + +=============================================================================== +*/ + +/* +------------------------------------------------------------------------------- +Include common integer types and flags. +------------------------------------------------------------------------------- +*/ +#include "ARM-gcc.h" + +/* +------------------------------------------------------------------------------- +Symbolic Boolean literals. +------------------------------------------------------------------------------- +*/ +enum { + FALSE = 0, + TRUE = 1 +}; + diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/single_cpdo.c linux/arch/arm/nwfpe/single_cpdo.c --- v2.3.6/linux/arch/arm/nwfpe/single_cpdo.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/single_cpdo.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,259 @@ +/* + NetWinder Floating Point Emulator + (c) Corel Computer Corporation, 1998 + + Direct questions, comments to Scott Bambrough + + 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. +*/ + +#include "config.h" +#include "milieu.h" +#include "softfloat.h" +#include "fpopcode.h" +#include "fpa11.h" + +float32 getSingleConstant(unsigned int); + +float32 float32_exp(float32 Fm); +float32 float32_ln(float32 Fm); +float32 float32_sin(float32 rFm); +float32 float32_cos(float32 rFm); +float32 float32_arcsin(float32 rFm); +float32 float32_arctan(float32 rFm); +float32 float32_log(float32 rFm); +float32 float32_tan(float32 rFm); +float32 float32_arccos(float32 rFm); +float32 float32_pow(float32 rFn,float32 rFm); +float32 float32_pol(float32 rFn,float32 rFm); + +unsigned int SingleCPDO(const unsigned int opcode) +{ + float32 rFm, rFn; + unsigned int Fd, Fm, Fn, nRc = 1; + + Fm = getFm(opcode); + if (CONSTANT_FM(opcode)) + { + rFm = getSingleConstant(Fm); + } + else + { + switch (fpa11->fpreg[Fm].fType) + { + case typeSingle: + rFm = fpa11->fpreg[Fm].fValue.fSingle; + break; + + default: return 0; + } + } + + if (!MONADIC_INSTRUCTION(opcode)) + { + Fn = getFn(opcode); + switch (fpa11->fpreg[Fn].fType) + { + case typeSingle: + rFn = fpa11->fpreg[Fn].fValue.fSingle; + break; + + default: return 0; + } + } + + Fd = getFd(opcode); + switch (opcode & MASK_ARITHMETIC_OPCODE) + { + /* dyadic opcodes */ + case ADF_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_add(rFn,rFm); + break; + + case MUF_CODE: + case FML_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_mul(rFn,rFm); + break; + + case SUF_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_sub(rFn,rFm); + break; + + case RSF_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_sub(rFm,rFn); + break; + + case DVF_CODE: + case FDV_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_div(rFn,rFm); + break; + + case RDF_CODE: + case FRD_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_div(rFm,rFn); + break; + +#if 0 + case POW_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_pow(rFn,rFm); + break; + + case RPW_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_pow(rFm,rFn); + break; +#endif + + case RMF_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_rem(rFn,rFm); + break; + +#if 0 + case POL_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_pol(rFn,rFm); + break; +#endif + + /* monadic opcodes */ + case MVF_CODE: + fpa11->fpreg[Fd].fValue.fSingle = rFm; + break; + + case MNF_CODE: + rFm ^= 0x80000000; + fpa11->fpreg[Fd].fValue.fSingle = rFm; + break; + + case ABS_CODE: + rFm &= 0x7fffffff; + fpa11->fpreg[Fd].fValue.fSingle = rFm; + break; + + case RND_CODE: + case URD_CODE: + fpa11->fpreg[Fd].fValue.fSingle = + int32_to_float32(float32_to_int32(rFm)); + break; + + case SQT_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_sqrt(rFm); + break; + +#if 0 + case LOG_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_log(rFm); + break; + + case LGN_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_ln(rFm); + break; + + case EXP_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_exp(rFm); + break; + + case SIN_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_sin(rFm); + break; + + case COS_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_cos(rFm); + break; + + case TAN_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_tan(rFm); + break; + + case ASN_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_arcsin(rFm); + break; + + case ACS_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_arccos(rFm); + break; + + case ATN_CODE: + fpa11->fpreg[Fd].fValue.fSingle = float32_arctan(rFm); + break; +#endif + + case NRM_CODE: + break; + + default: + { + nRc = 0; + } + } + + if (0 != nRc) fpa11->fpreg[Fd].fType = typeSingle; + return nRc; +} + +#if 0 +float32 float32_exp(float32 Fm) +{ +//series +} + +float32 float32_ln(float32 Fm) +{ +//series +} + +float32 float32_sin(float32 rFm) +{ +//series +} + +float32 float32_cos(float32 rFm) +{ +//series +} + +float32 float32_arcsin(float32 rFm) +{ +//series +} + +float32 float32_arctan(float32 rFm) +{ + //series +} + +float32 float32_arccos(float32 rFm) +{ + //return float32_sub(halfPi,float32_arcsin(rFm)); +} + +float32 float32_log(float32 rFm) +{ + return float32_div(float32_ln(rFm),getSingleConstant(7)); +} + +float32 float32_tan(float32 rFm) +{ + return float32_div(float32_sin(rFm),float32_cos(rFm)); +} + +float32 float32_pow(float32 rFn,float32 rFm) +{ + return float32_exp(float32_mul(rFm,float32_ln(rFn))); +} + +float32 float32_pol(float32 rFn,float32 rFm) +{ + return float32_arctan(float32_div(rFn,rFm)); +} +#endif diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/softfloat-macros linux/arch/arm/nwfpe/softfloat-macros --- v2.3.6/linux/arch/arm/nwfpe/softfloat-macros Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/softfloat-macros Thu Jun 17 01:11:35 1999 @@ -0,0 +1,740 @@ + +/* +=============================================================================== + +This C source fragment is part of the SoftFloat IEC/IEEE Floating-point +Arithmetic Package, Release 2. + +Written by John R. Hauser. This work was made possible in part by the +International Computer Science Institute, located at Suite 600, 1947 Center +Street, Berkeley, California 94704. Funding was partially provided by the +National Science Foundation under grant MIP-9311980. The original version +of this code was written as part of a project to build a fixed-point vector +processor in collaboration with the University of California at Berkeley, +overseen by Profs. Nelson Morgan and John Wawrzynek. More information +is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ +arithmetic/softfloat.html'. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort +has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT +TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO +PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY +AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. + +Derivative works are acceptable, even for commercial purposes, so long as +(1) they include prominent notice that the work is derivative, and (2) they +include prominent notice akin to these three paragraphs for those parts of +this code that are retained. + +=============================================================================== +*/ + +/* +------------------------------------------------------------------------------- +Shifts `a' right by the number of bits given in `count'. If any nonzero +bits are shifted off, they are ``jammed'' into the least significant bit of +the result by setting the least significant bit to 1. The value of `count' +can be arbitrarily large; in particular, if `count' is greater than 32, the +result will be either 0 or 1, depending on whether `a' is zero or nonzero. +The result is stored in the location pointed to by `zPtr'. +------------------------------------------------------------------------------- +*/ +INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr ) +{ + bits32 z; + if ( count == 0 ) { + z = a; + } + else if ( count < 32 ) { + z = ( a>>count ) | ( ( a<<( ( - count ) & 31 ) ) != 0 ); + } + else { + z = ( a != 0 ); + } + *zPtr = z; +} + +/* +------------------------------------------------------------------------------- +Shifts `a' right by the number of bits given in `count'. If any nonzero +bits are shifted off, they are ``jammed'' into the least significant bit of +the result by setting the least significant bit to 1. The value of `count' +can be arbitrarily large; in particular, if `count' is greater than 64, the +result will be either 0 or 1, depending on whether `a' is zero or nonzero. +The result is stored in the location pointed to by `zPtr'. +------------------------------------------------------------------------------- +*/ +INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr ) +{ + bits64 z; + + __asm__("@shift64RightJamming -- start"); + if ( count == 0 ) { + z = a; + } + else if ( count < 64 ) { + z = ( a>>count ) | ( ( a<<( ( - count ) & 63 ) ) != 0 ); + } + else { + z = ( a != 0 ); + } + __asm__("@shift64RightJamming -- end"); + *zPtr = z; +} + +/* +------------------------------------------------------------------------------- +Shifts the 128-bit value formed by concatenating `a0' and `a1' right by 64 +_plus_ the number of bits given in `count'. The shifted result is at most +64 nonzero bits; this is stored at the location pointed to by `z0Ptr'. The +bits shifted off form a second 64-bit result as follows: The _last_ bit +shifted off is the most-significant bit of the extra result, and the other +63 bits of the extra result are all zero if and only if _all_but_the_last_ +bits shifted off were all zero. This extra result is stored in the location +pointed to by `z1Ptr'. The value of `count' can be arbitrarily large. + (This routine makes more sense if `a0' and `a1' are considered to form a +fixed-point value with binary point between `a0' and `a1'. This fixed-point +value is shifted right by the number of bits given in `count', and the +integer part of the result is returned at the location pointed to by +`z0Ptr'. The fractional part of the result may be slightly corrupted as +described above, and is returned at the location pointed to by `z1Ptr'.) +------------------------------------------------------------------------------- +*/ +INLINE void + shift64ExtraRightJamming( + bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + bits64 z0, z1; + int8 negCount = ( - count ) & 63; + + if ( count == 0 ) { + z1 = a1; + z0 = a0; + } + else if ( count < 64 ) { + z1 = ( a0<>count; + } + else { + if ( count == 64 ) { + z1 = a0 | ( a1 != 0 ); + } + else { + z1 = ( ( a0 | a1 ) != 0 ); + } + z0 = 0; + } + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/* +------------------------------------------------------------------------------- +Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the +number of bits given in `count'. Any bits shifted off are lost. The value +of `count' can be arbitrarily large; in particular, if `count' is greater +than 128, the result will be 0. The result is broken into two 64-bit pieces +which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. +------------------------------------------------------------------------------- +*/ +INLINE void + shift128Right( + bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + bits64 z0, z1; + int8 negCount = ( - count ) & 63; + + if ( count == 0 ) { + z1 = a1; + z0 = a0; + } + else if ( count < 64 ) { + z1 = ( a0<>count ); + z0 = a0>>count; + } + else { + z1 = ( count < 64 ) ? ( a0>>( count & 63 ) ) : 0; + z0 = 0; + } + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/* +------------------------------------------------------------------------------- +Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the +number of bits given in `count'. If any nonzero bits are shifted off, they +are ``jammed'' into the least significant bit of the result by setting the +least significant bit to 1. The value of `count' can be arbitrarily large; +in particular, if `count' is greater than 128, the result will be either 0 +or 1, depending on whether the concatenation of `a0' and `a1' is zero or +nonzero. The result is broken into two 64-bit pieces which are stored at +the locations pointed to by `z0Ptr' and `z1Ptr'. +------------------------------------------------------------------------------- +*/ +INLINE void + shift128RightJamming( + bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + bits64 z0, z1; + int8 negCount = ( - count ) & 63; + + if ( count == 0 ) { + z1 = a1; + z0 = a0; + } + else if ( count < 64 ) { + z1 = ( a0<>count ) | ( ( a1<>count; + } + else { + if ( count == 64 ) { + z1 = a0 | ( a1 != 0 ); + } + else if ( count < 128 ) { + z1 = ( a0>>( count & 63 ) ) | ( ( ( a0<>count ); + z0 = a0>>count; + } + else { + if ( count == 64 ) { + z2 = a1; + z1 = a0; + } + else { + a2 |= a1; + if ( count < 128 ) { + z2 = a0<>( count & 63 ); + } + else { + z2 = ( count == 128 ) ? a0 : ( a0 != 0 ); + z1 = 0; + } + } + z0 = 0; + } + z2 |= ( a2 != 0 ); + } + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/* +------------------------------------------------------------------------------- +Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the +number of bits given in `count'. Any bits shifted off are lost. The value +of `count' must be less than 64. The result is broken into two 64-bit +pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. +------------------------------------------------------------------------------- +*/ +INLINE void + shortShift128Left( + bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + + *z1Ptr = a1<>( ( - count ) & 63 ) ); + +} + +/* +------------------------------------------------------------------------------- +Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' left +by the number of bits given in `count'. Any bits shifted off are lost. +The value of `count' must be less than 64. The result is broken into three +64-bit pieces which are stored at the locations pointed to by `z0Ptr', +`z1Ptr', and `z2Ptr'. +------------------------------------------------------------------------------- +*/ +INLINE void + shortShift192Left( + bits64 a0, + bits64 a1, + bits64 a2, + int16 count, + bits64 *z0Ptr, + bits64 *z1Ptr, + bits64 *z2Ptr + ) +{ + bits64 z0, z1, z2; + int8 negCount; + + z2 = a2<>negCount; + z0 |= a1>>negCount; + } + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/* +------------------------------------------------------------------------------- +Adds the 128-bit value formed by concatenating `a0' and `a1' to the 128-bit +value formed by concatenating `b0' and `b1'. Addition is modulo 2^128, so +any carry out is lost. The result is broken into two 64-bit pieces which +are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. +------------------------------------------------------------------------------- +*/ +INLINE void + add128( + bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + bits64 z1; + + z1 = a1 + b1; + *z1Ptr = z1; + *z0Ptr = a0 + b0 + ( z1 < a1 ); + +} + +/* +------------------------------------------------------------------------------- +Adds the 192-bit value formed by concatenating `a0', `a1', and `a2' to the +192-bit value formed by concatenating `b0', `b1', and `b2'. Addition is +modulo 2^192, so any carry out is lost. The result is broken into three +64-bit pieces which are stored at the locations pointed to by `z0Ptr', +`z1Ptr', and `z2Ptr'. +------------------------------------------------------------------------------- +*/ +INLINE void + add192( + bits64 a0, + bits64 a1, + bits64 a2, + bits64 b0, + bits64 b1, + bits64 b2, + bits64 *z0Ptr, + bits64 *z1Ptr, + bits64 *z2Ptr + ) +{ + bits64 z0, z1, z2; + int8 carry0, carry1; + + z2 = a2 + b2; + carry1 = ( z2 < a2 ); + z1 = a1 + b1; + carry0 = ( z1 < a1 ); + z0 = a0 + b0; + z1 += carry1; + z0 += ( z1 < carry1 ); + z0 += carry0; + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/* +------------------------------------------------------------------------------- +Subtracts the 128-bit value formed by concatenating `b0' and `b1' from the +128-bit value formed by concatenating `a0' and `a1'. Subtraction is modulo +2^128, so any borrow out (carry out) is lost. The result is broken into two +64-bit pieces which are stored at the locations pointed to by `z0Ptr' and +`z1Ptr'. +------------------------------------------------------------------------------- +*/ +INLINE void + sub128( + bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + + *z1Ptr = a1 - b1; + *z0Ptr = a0 - b0 - ( a1 < b1 ); + +} + +/* +------------------------------------------------------------------------------- +Subtracts the 192-bit value formed by concatenating `b0', `b1', and `b2' +from the 192-bit value formed by concatenating `a0', `a1', and `a2'. +Subtraction is modulo 2^192, so any borrow out (carry out) is lost. The +result is broken into three 64-bit pieces which are stored at the locations +pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'. +------------------------------------------------------------------------------- +*/ +INLINE void + sub192( + bits64 a0, + bits64 a1, + bits64 a2, + bits64 b0, + bits64 b1, + bits64 b2, + bits64 *z0Ptr, + bits64 *z1Ptr, + bits64 *z2Ptr + ) +{ + bits64 z0, z1, z2; + int8 borrow0, borrow1; + + z2 = a2 - b2; + borrow1 = ( a2 < b2 ); + z1 = a1 - b1; + borrow0 = ( a1 < b1 ); + z0 = a0 - b0; + z0 -= ( z1 < borrow1 ); + z1 -= borrow1; + z0 -= borrow0; + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/* +------------------------------------------------------------------------------- +Multiplies `a' by `b' to obtain a 128-bit product. The product is broken +into two 64-bit pieces which are stored at the locations pointed to by +`z0Ptr' and `z1Ptr'. +------------------------------------------------------------------------------- +*/ +INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr ) +{ + bits32 aHigh, aLow, bHigh, bLow; + bits64 z0, zMiddleA, zMiddleB, z1; + + aLow = a; + aHigh = a>>32; + bLow = b; + bHigh = b>>32; + z1 = ( (bits64) aLow ) * bLow; + zMiddleA = ( (bits64) aLow ) * bHigh; + zMiddleB = ( (bits64) aHigh ) * bLow; + z0 = ( (bits64) aHigh ) * bHigh; + zMiddleA += zMiddleB; + z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 ); + zMiddleA <<= 32; + z1 += zMiddleA; + z0 += ( z1 < zMiddleA ); + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/* +------------------------------------------------------------------------------- +Multiplies the 128-bit value formed by concatenating `a0' and `a1' by `b' to +obtain a 192-bit product. The product is broken into three 64-bit pieces +which are stored at the locations pointed to by `z0Ptr', `z1Ptr', and +`z2Ptr'. +------------------------------------------------------------------------------- +*/ +INLINE void + mul128By64To192( + bits64 a0, + bits64 a1, + bits64 b, + bits64 *z0Ptr, + bits64 *z1Ptr, + bits64 *z2Ptr + ) +{ + bits64 z0, z1, z2, more1; + + mul64To128( a1, b, &z1, &z2 ); + mul64To128( a0, b, &z0, &more1 ); + add128( z0, more1, 0, z1, &z0, &z1 ); + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/* +------------------------------------------------------------------------------- +Multiplies the 128-bit value formed by concatenating `a0' and `a1' to the +128-bit value formed by concatenating `b0' and `b1' to obtain a 256-bit +product. The product is broken into four 64-bit pieces which are stored at +the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'. +------------------------------------------------------------------------------- +*/ +INLINE void + mul128To256( + bits64 a0, + bits64 a1, + bits64 b0, + bits64 b1, + bits64 *z0Ptr, + bits64 *z1Ptr, + bits64 *z2Ptr, + bits64 *z3Ptr + ) +{ + bits64 z0, z1, z2, z3; + bits64 more1, more2; + + mul64To128( a1, b1, &z2, &z3 ); + mul64To128( a1, b0, &z1, &more2 ); + add128( z1, more2, 0, z2, &z1, &z2 ); + mul64To128( a0, b0, &z0, &more1 ); + add128( z0, more1, 0, z1, &z0, &z1 ); + mul64To128( a0, b1, &more1, &more2 ); + add128( more1, more2, 0, z2, &more1, &z2 ); + add128( z0, z1, 0, more1, &z0, &z1 ); + *z3Ptr = z3; + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; + +} + +/* +------------------------------------------------------------------------------- +Returns an approximation to the 64-bit integer quotient obtained by dividing +`b' into the 128-bit value formed by concatenating `a0' and `a1'. The +divisor `b' must be at least 2^63. If q is the exact quotient truncated +toward zero, the approximation returned lies between q and q + 2 inclusive. +If the exact quotient q is larger than 64 bits, the maximum positive 64-bit +unsigned integer is returned. +------------------------------------------------------------------------------- +*/ +static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b ) +{ + bits64 b0, b1; + bits64 rem0, rem1, term0, term1; + bits64 z; + if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF ); + b0 = b>>32; + z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32; + mul64To128( b, z, &term0, &term1 ); + sub128( a0, a1, term0, term1, &rem0, &rem1 ); + while ( ( (sbits64) rem0 ) < 0 ) { + z -= LIT64( 0x100000000 ); + b1 = b<<32; + add128( rem0, rem1, b0, b1, &rem0, &rem1 ); + } + rem0 = ( rem0<<32 ) | ( rem1>>32 ); + z |= ( b0<<32 <= rem0 ) ? 0xFFFFFFFF : rem0 / b0; + return z; + +} + +/* +------------------------------------------------------------------------------- +Returns an approximation to the square root of the 32-bit significand given +by `a'. Considered as an integer, `a' must be at least 2^31. If bit 0 of +`aExp' (the least significant bit) is 1, the integer returned approximates +2^31*sqrt(`a'/2^31), where `a' is considered an integer. If bit 0 of `aExp' +is 0, the integer returned approximates 2^31*sqrt(`a'/2^30). In either +case, the approximation returned lies strictly within +/-2 of the exact +value. +------------------------------------------------------------------------------- +*/ +static bits32 estimateSqrt32( int16 aExp, bits32 a ) +{ + static const bits16 sqrtOddAdjustments[] = { + 0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0, + 0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67 + }; + static const bits16 sqrtEvenAdjustments[] = { + 0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E, + 0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002 + }; + int8 index; + bits32 z; + + index = ( a>>27 ) & 15; + if ( aExp & 1 ) { + z = 0x4000 + ( a>>17 ) - sqrtOddAdjustments[ index ]; + z = ( ( a / z )<<14 ) + ( z<<15 ); + a >>= 1; + } + else { + z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ index ]; + z = a / z + z; + z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 ); + if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 ); + } + return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 ); + +} + +/* +------------------------------------------------------------------------------- +Returns the number of leading 0 bits before the most-significant 1 bit +of `a'. If `a' is zero, 32 is returned. +------------------------------------------------------------------------------- +*/ +static int8 countLeadingZeros32( bits32 a ) +{ + static const int8 countLeadingZerosHigh[] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + int8 shiftCount; + + shiftCount = 0; + if ( a < 0x10000 ) { + shiftCount += 16; + a <<= 16; + } + if ( a < 0x1000000 ) { + shiftCount += 8; + a <<= 8; + } + shiftCount += countLeadingZerosHigh[ a>>24 ]; + return shiftCount; + +} + +/* +------------------------------------------------------------------------------- +Returns the number of leading 0 bits before the most-significant 1 bit +of `a'. If `a' is zero, 64 is returned. +------------------------------------------------------------------------------- +*/ +static int8 countLeadingZeros64( bits64 a ) +{ + int8 shiftCount; + + shiftCount = 0; + if ( a < ( (bits64) 1 )<<32 ) { + shiftCount += 32; + } + else { + a >>= 32; + } + shiftCount += countLeadingZeros32( a ); + return shiftCount; + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' +is equal to the 128-bit value formed by concatenating `b0' and `b1'. +Otherwise, returns 0. +------------------------------------------------------------------------------- +*/ +INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +{ + + return ( a0 == b0 ) && ( a1 == b1 ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less +than or equal to the 128-bit value formed by concatenating `b0' and `b1'. +Otherwise, returns 0. +------------------------------------------------------------------------------- +*/ +INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +{ + + return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less +than the 128-bit value formed by concatenating `b0' and `b1'. Otherwise, +returns 0. +------------------------------------------------------------------------------- +*/ +INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +{ + + return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is +not equal to the 128-bit value formed by concatenating `b0' and `b1'. +Otherwise, returns 0. +------------------------------------------------------------------------------- +*/ +INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 ) +{ + + return ( a0 != b0 ) || ( a1 != b1 ); + +} + diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/softfloat-specialize linux/arch/arm/nwfpe/softfloat-specialize --- v2.3.6/linux/arch/arm/nwfpe/softfloat-specialize Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/softfloat-specialize Thu Jun 17 01:11:35 1999 @@ -0,0 +1,471 @@ + +/* +=============================================================================== + +This C source fragment is part of the SoftFloat IEC/IEEE Floating-point +Arithmetic Package, Release 2. + +Written by John R. Hauser. This work was made possible in part by the +International Computer Science Institute, located at Suite 600, 1947 Center +Street, Berkeley, California 94704. Funding was partially provided by the +National Science Foundation under grant MIP-9311980. The original version +of this code was written as part of a project to build a fixed-point vector +processor in collaboration with the University of California at Berkeley, +overseen by Profs. Nelson Morgan and John Wawrzynek. More information +is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ +arithmetic/softfloat.html'. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort +has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT +TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO +PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY +AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. + +Derivative works are acceptable, even for commercial purposes, so long as +(1) they include prominent notice that the work is derivative, and (2) they +include prominent notice akin to these three paragraphs for those parts of +this code that are retained. + +=============================================================================== +*/ + +/* +------------------------------------------------------------------------------- +Underflow tininess-detection mode, statically initialized to default value. +(The declaration in `softfloat.h' must match the `int8' type here.) +------------------------------------------------------------------------------- +*/ +int8 float_detect_tininess = float_tininess_after_rounding; + +/* +------------------------------------------------------------------------------- +Raises the exceptions specified by `flags'. Floating-point traps can be +defined here if desired. It is currently not possible for such a trap to +substitute a result value. If traps are not implemented, this routine +should be simply `float_exception_flags |= flags;'. + +ScottB: November 4, 1998 +Moved this function out of softfloat-specialize into fpmodule.c. +This effectively isolates all the changes required for integrating with the +Linux kernel into fpmodule.c. Porting to NetBSD should only require modifying +fpmodule.c to integrate with the NetBSD kernel (I hope!). +------------------------------------------------------------------------------- +void float_raise( int8 flags ) +{ + float_exception_flags |= flags; +} +*/ + +/* +------------------------------------------------------------------------------- +Internal canonical NaN format. +------------------------------------------------------------------------------- +*/ +typedef struct { + flag sign; + bits64 high, low; +} commonNaNT; + +/* +------------------------------------------------------------------------------- +The pattern for a default generated single-precision NaN. +------------------------------------------------------------------------------- +*/ +#define float32_default_nan 0xFFFFFFFF + +/* +------------------------------------------------------------------------------- +Returns 1 if the single-precision floating-point value `a' is a NaN; +otherwise returns 0. +------------------------------------------------------------------------------- +*/ +flag float32_is_nan( float32 a ) +{ + + return ( 0xFF000000 < (bits32) ( a<<1 ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the single-precision floating-point value `a' is a signaling +NaN; otherwise returns 0. +------------------------------------------------------------------------------- +*/ +flag float32_is_signaling_nan( float32 a ) +{ + + return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of converting the single-precision floating-point NaN +`a' to the canonical NaN format. If `a' is a signaling NaN, the invalid +exception is raised. +------------------------------------------------------------------------------- +*/ +static commonNaNT float32ToCommonNaN( float32 a ) +{ + commonNaNT z; + + if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid ); + z.sign = a>>31; + z.low = 0; + z.high = ( (bits64) a )<<41; + return z; + +} + +/* +------------------------------------------------------------------------------- +Returns the result of converting the canonical NaN `a' to the single- +precision floating-point format. +------------------------------------------------------------------------------- +*/ +static float32 commonNaNToFloat32( commonNaNT a ) +{ + + return ( ( (bits32) a.sign )<<31 ) | 0x7FC00000 | ( a.high>>41 ); + +} + +/* +------------------------------------------------------------------------------- +Takes two single-precision floating-point values `a' and `b', one of which +is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a +signaling NaN, the invalid exception is raised. +------------------------------------------------------------------------------- +*/ +static float32 propagateFloat32NaN( float32 a, float32 b ) +{ + flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + + aIsNaN = float32_is_nan( a ); + aIsSignalingNaN = float32_is_signaling_nan( a ); + bIsNaN = float32_is_nan( b ); + bIsSignalingNaN = float32_is_signaling_nan( b ); + a |= 0x00400000; + b |= 0x00400000; + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid ); + if ( aIsNaN ) { + return ( aIsSignalingNaN & bIsNaN ) ? b : a; + } + else { + return b; + } + +} + +/* +------------------------------------------------------------------------------- +The pattern for a default generated double-precision NaN. +------------------------------------------------------------------------------- +*/ +#define float64_default_nan LIT64( 0xFFFFFFFFFFFFFFFF ) + +/* +------------------------------------------------------------------------------- +Returns 1 if the double-precision floating-point value `a' is a NaN; +otherwise returns 0. +------------------------------------------------------------------------------- +*/ +flag float64_is_nan( float64 a ) +{ + + return ( LIT64( 0xFFE0000000000000 ) < (bits64) ( a<<1 ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the double-precision floating-point value `a' is a signaling +NaN; otherwise returns 0. +------------------------------------------------------------------------------- +*/ +flag float64_is_signaling_nan( float64 a ) +{ + + return + ( ( ( a>>51 ) & 0xFFF ) == 0xFFE ) + && ( a & LIT64( 0x0007FFFFFFFFFFFF ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of converting the double-precision floating-point NaN +`a' to the canonical NaN format. If `a' is a signaling NaN, the invalid +exception is raised. +------------------------------------------------------------------------------- +*/ +static commonNaNT float64ToCommonNaN( float64 a ) +{ + commonNaNT z; + + if ( float64_is_signaling_nan( a ) ) float_raise( float_flag_invalid ); + z.sign = a>>63; + z.low = 0; + z.high = a<<12; + return z; + +} + +/* +------------------------------------------------------------------------------- +Returns the result of converting the canonical NaN `a' to the double- +precision floating-point format. +------------------------------------------------------------------------------- +*/ +static float64 commonNaNToFloat64( commonNaNT a ) +{ + + return + ( ( (bits64) a.sign )<<63 ) + | LIT64( 0x7FF8000000000000 ) + | ( a.high>>12 ); + +} + +/* +------------------------------------------------------------------------------- +Takes two double-precision floating-point values `a' and `b', one of which +is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a +signaling NaN, the invalid exception is raised. +------------------------------------------------------------------------------- +*/ +static float64 propagateFloat64NaN( float64 a, float64 b ) +{ + flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + + aIsNaN = float64_is_nan( a ); + aIsSignalingNaN = float64_is_signaling_nan( a ); + bIsNaN = float64_is_nan( b ); + bIsSignalingNaN = float64_is_signaling_nan( b ); + a |= LIT64( 0x0008000000000000 ); + b |= LIT64( 0x0008000000000000 ); + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid ); + if ( aIsNaN ) { + return ( aIsSignalingNaN & bIsNaN ) ? b : a; + } + else { + return b; + } + +} + +#ifdef FLOATX80 + +/* +------------------------------------------------------------------------------- +The pattern for a default generated extended double-precision NaN. The +`high' and `low' values hold the most- and least-significant bits, +respectively. +------------------------------------------------------------------------------- +*/ +#define floatx80_default_nan_high 0xFFFF +#define floatx80_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF ) + +/* +------------------------------------------------------------------------------- +Returns 1 if the extended double-precision floating-point value `a' is a +NaN; otherwise returns 0. +------------------------------------------------------------------------------- +*/ +flag floatx80_is_nan( floatx80 a ) +{ + + return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the extended double-precision floating-point value `a' is a +signaling NaN; otherwise returns 0. +------------------------------------------------------------------------------- +*/ +flag floatx80_is_signaling_nan( floatx80 a ) +{ + //register int lr; + bits64 aLow; + + //__asm__("mov %0, lr" : : "g" (lr)); + //fp_printk("floatx80_is_signalling_nan() called from 0x%08x\n",lr); + aLow = a.low & ~ LIT64( 0x4000000000000000 ); + return + ( ( a.high & 0x7FFF ) == 0x7FFF ) + && (bits64) ( aLow<<1 ) + && ( a.low == aLow ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of converting the extended double-precision floating- +point NaN `a' to the canonical NaN format. If `a' is a signaling NaN, the +invalid exception is raised. +------------------------------------------------------------------------------- +*/ +static commonNaNT floatx80ToCommonNaN( floatx80 a ) +{ + commonNaNT z; + + if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid ); + z.sign = a.high>>15; + z.low = 0; + z.high = a.low<<1; + return z; + +} + +/* +------------------------------------------------------------------------------- +Returns the result of converting the canonical NaN `a' to the extended +double-precision floating-point format. +------------------------------------------------------------------------------- +*/ +static floatx80 commonNaNToFloatx80( commonNaNT a ) +{ + floatx80 z; + + z.low = LIT64( 0xC000000000000000 ) | ( a.high>>1 ); + z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF; + return z; + +} + +/* +------------------------------------------------------------------------------- +Takes two extended double-precision floating-point values `a' and `b', one +of which is a NaN, and returns the appropriate NaN result. If either `a' or +`b' is a signaling NaN, the invalid exception is raised. +------------------------------------------------------------------------------- +*/ +static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b ) +{ + flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + + aIsNaN = floatx80_is_nan( a ); + aIsSignalingNaN = floatx80_is_signaling_nan( a ); + bIsNaN = floatx80_is_nan( b ); + bIsSignalingNaN = floatx80_is_signaling_nan( b ); + a.low |= LIT64( 0xC000000000000000 ); + b.low |= LIT64( 0xC000000000000000 ); + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid ); + if ( aIsNaN ) { + return ( aIsSignalingNaN & bIsNaN ) ? b : a; + } + else { + return b; + } + +} + +#endif + +#ifdef FLOAT128 + +/* +------------------------------------------------------------------------------- +The pattern for a default generated quadruple-precision NaN. The `high' and +`low' values hold the most- and least-significant bits, respectively. +------------------------------------------------------------------------------- +*/ +#define float128_default_nan_high LIT64( 0xFFFFFFFFFFFFFFFF ) +#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF ) + +/* +------------------------------------------------------------------------------- +Returns 1 if the quadruple-precision floating-point value `a' is a NaN; +otherwise returns 0. +------------------------------------------------------------------------------- +*/ +flag float128_is_nan( float128 a ) +{ + + return + ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) ) + && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the quadruple-precision floating-point value `a' is a +signaling NaN; otherwise returns 0. +------------------------------------------------------------------------------- +*/ +flag float128_is_signaling_nan( float128 a ) +{ + + return + ( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE ) + && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of converting the quadruple-precision floating-point NaN +`a' to the canonical NaN format. If `a' is a signaling NaN, the invalid +exception is raised. +------------------------------------------------------------------------------- +*/ +static commonNaNT float128ToCommonNaN( float128 a ) +{ + commonNaNT z; + + if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid ); + z.sign = a.high>>63; + shortShift128Left( a.high, a.low, 16, &z.high, &z.low ); + return z; + +} + +/* +------------------------------------------------------------------------------- +Returns the result of converting the canonical NaN `a' to the quadruple- +precision floating-point format. +------------------------------------------------------------------------------- +*/ +static float128 commonNaNToFloat128( commonNaNT a ) +{ + float128 z; + + shift128Right( a.high, a.low, 16, &z.high, &z.low ); + z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF800000000000 ); + return z; + +} + +/* +------------------------------------------------------------------------------- +Takes two quadruple-precision floating-point values `a' and `b', one of +which is a NaN, and returns the appropriate NaN result. If either `a' or +`b' is a signaling NaN, the invalid exception is raised. +------------------------------------------------------------------------------- +*/ +static float128 propagateFloat128NaN( float128 a, float128 b ) +{ + flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + + aIsNaN = float128_is_nan( a ); + aIsSignalingNaN = float128_is_signaling_nan( a ); + bIsNaN = float128_is_nan( b ); + bIsSignalingNaN = float128_is_signaling_nan( b ); + a.high |= LIT64( 0x0000800000000000 ); + b.high |= LIT64( 0x0000800000000000 ); + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid ); + if ( aIsNaN ) { + return ( aIsSignalingNaN & bIsNaN ) ? b : a; + } + else { + return b; + } + +} + +#endif + diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/softfloat.c linux/arch/arm/nwfpe/softfloat.c --- v2.3.6/linux/arch/arm/nwfpe/softfloat.c Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/softfloat.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,4877 @@ +/* +=============================================================================== + +This C source file is part of the SoftFloat IEC/IEEE Floating-point +Arithmetic Package, Release 2. + +Written by John R. Hauser. This work was made possible in part by the +International Computer Science Institute, located at Suite 600, 1947 Center +Street, Berkeley, California 94704. Funding was partially provided by the +National Science Foundation under grant MIP-9311980. The original version +of this code was written as part of a project to build a fixed-point vector +processor in collaboration with the University of California at Berkeley, +overseen by Profs. Nelson Morgan and John Wawrzynek. More information +is available through the web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ +arithmetic/softfloat.html'. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort +has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT +TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO +PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY +AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. + +Derivative works are acceptable, even for commercial purposes, so long as +(1) they include prominent notice that the work is derivative, and (2) they +include prominent notice akin to these three paragraphs for those parts of +this code that are retained. + +=============================================================================== +*/ + +#include "milieu.h" +#include "softfloat.h" + +/* +------------------------------------------------------------------------------- +Floating-point rounding mode, extended double-precision rounding precision, +and exception flags. +------------------------------------------------------------------------------- +*/ +int8 float_rounding_mode = float_round_nearest_even; +int8 floatx80_rounding_precision = 80; +int8 float_exception_flags = 0; + +/* +------------------------------------------------------------------------------- +Primitive arithmetic functions, including multi-word arithmetic, and +division and square root approximations. (Can be specialized to target if +desired.) +------------------------------------------------------------------------------- +*/ +#include "softfloat-macros" + +/* +------------------------------------------------------------------------------- +Functions and definitions to determine: (1) whether tininess for underflow +is detected before or after rounding by default, (2) what (if anything) +happens when exceptions are raised, (3) how signaling NaNs are distinguished +from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs +are propagated from function inputs to output. These details are target- +specific. +------------------------------------------------------------------------------- +*/ +#include "softfloat-specialize" + +/* +------------------------------------------------------------------------------- +Takes a 64-bit fixed-point value `absZ' with binary point between bits 6 +and 7, and returns the properly rounded 32-bit integer corresponding to the +input. If `zSign' is nonzero, the input is negated before being converted +to an integer. Bit 63 of `absZ' must be zero. Ordinarily, the fixed-point +input is simply rounded to an integer, with the inexact exception raised if +the input cannot be represented exactly as an integer. If the fixed-point +input is too large, however, the invalid exception is raised and the largest +positive or negative integer is returned. +------------------------------------------------------------------------------- +*/ +static int32 roundAndPackInt32( flag zSign, bits64 absZ ) +{ + int8 roundingMode; + flag roundNearestEven; + int8 roundIncrement, roundBits; + int32 z; + + roundingMode = float_rounding_mode; + roundNearestEven = ( roundingMode == float_round_nearest_even ); + roundIncrement = 0x40; + if ( ! roundNearestEven ) { + if ( roundingMode == float_round_to_zero ) { + roundIncrement = 0; + } + else { + roundIncrement = 0x7F; + if ( zSign ) { + if ( roundingMode == float_round_up ) roundIncrement = 0; + } + else { + if ( roundingMode == float_round_down ) roundIncrement = 0; + } + } + } + roundBits = absZ & 0x7F; + absZ = ( absZ + roundIncrement )>>7; + absZ &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven ); + z = absZ; + if ( zSign ) z = - z; + if ( ( absZ>>32 ) || ( z && ( ( z < 0 ) ^ zSign ) ) ) { + float_exception_flags |= float_flag_invalid; + return zSign ? 0x80000000 : 0x7FFFFFFF; + } + if ( roundBits ) float_exception_flags |= float_flag_inexact; + return z; + +} + +/* +------------------------------------------------------------------------------- +Returns the fraction bits of the single-precision floating-point value `a'. +------------------------------------------------------------------------------- +*/ +INLINE bits32 extractFloat32Frac( float32 a ) +{ + + return a & 0x007FFFFF; + +} + +/* +------------------------------------------------------------------------------- +Returns the exponent bits of the single-precision floating-point value `a'. +------------------------------------------------------------------------------- +*/ +INLINE int16 extractFloat32Exp( float32 a ) +{ + + return ( a>>23 ) & 0xFF; + +} + +/* +------------------------------------------------------------------------------- +Returns the sign bit of the single-precision floating-point value `a'. +------------------------------------------------------------------------------- +*/ +INLINE flag extractFloat32Sign( float32 a ) +{ + + return a>>31; + +} + +/* +------------------------------------------------------------------------------- +Normalizes the subnormal single-precision floating-point value represented +by the denormalized significand `aSig'. The normalized exponent and +significand are stored at the locations pointed to by `zExpPtr' and +`zSigPtr', respectively. +------------------------------------------------------------------------------- +*/ +static void + normalizeFloat32Subnormal( bits32 aSig, int16 *zExpPtr, bits32 *zSigPtr ) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros32( aSig ) - 8; + *zSigPtr = aSig<>7; + zSig &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven ); + if ( zSig == 0 ) zExp = 0; + return packFloat32( zSign, zExp, zSig ); + +} + +/* +------------------------------------------------------------------------------- +Takes an abstract floating-point value having sign `zSign', exponent `zExp', +and significand `zSig', and returns the proper single-precision floating- +point value corresponding to the abstract input. This routine is just like +`roundAndPackFloat32' except that `zSig' does not have to be normalized in +any way. In all cases, `zExp' must be 1 less than the ``true'' floating- +point exponent. +------------------------------------------------------------------------------- +*/ +static float32 + normalizeRoundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig ) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros32( zSig ) - 1; + return roundAndPackFloat32( zSign, zExp - shiftCount, zSig<>52 ) & 0x7FF; + +} + +/* +------------------------------------------------------------------------------- +Returns the sign bit of the double-precision floating-point value `a'. +------------------------------------------------------------------------------- +*/ +INLINE flag extractFloat64Sign( float64 a ) +{ + + return a>>63; + +} + +/* +------------------------------------------------------------------------------- +Normalizes the subnormal double-precision floating-point value represented +by the denormalized significand `aSig'. The normalized exponent and +significand are stored at the locations pointed to by `zExpPtr' and +`zSigPtr', respectively. +------------------------------------------------------------------------------- +*/ +static void + normalizeFloat64Subnormal( bits64 aSig, int16 *zExpPtr, bits64 *zSigPtr ) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros64( aSig ) - 11; + *zSigPtr = aSig<>10; + zSig &= ~ ( ( ( roundBits ^ 0x200 ) == 0 ) & roundNearestEven ); + if ( zSig == 0 ) zExp = 0; + return packFloat64( zSign, zExp, zSig ); + +} + +/* +------------------------------------------------------------------------------- +Takes an abstract floating-point value having sign `zSign', exponent `zExp', +and significand `zSig', and returns the proper double-precision floating- +point value corresponding to the abstract input. This routine is just like +`roundAndPackFloat64' except that `zSig' does not have to be normalized in +any way. In all cases, `zExp' must be 1 less than the ``true'' floating- +point exponent. +------------------------------------------------------------------------------- +*/ +static float64 + normalizeRoundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig ) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros64( zSig ) - 1; + return roundAndPackFloat64( zSign, zExp - shiftCount, zSig<>15; + +} + +/* +------------------------------------------------------------------------------- +Normalizes the subnormal extended double-precision floating-point value +represented by the denormalized significand `aSig'. The normalized exponent +and significand are stored at the locations pointed to by `zExpPtr' and +`zSigPtr', respectively. +------------------------------------------------------------------------------- +*/ +static void + normalizeFloatx80Subnormal( bits64 aSig, int32 *zExpPtr, bits64 *zSigPtr ) +{ + int8 shiftCount; + + shiftCount = countLeadingZeros64( aSig ); + *zSigPtr = aSig<>48 ) & 0x7FFF; + +} + +/* +------------------------------------------------------------------------------- +Returns the sign bit of the quadruple-precision floating-point value `a'. +------------------------------------------------------------------------------- +*/ +INLINE flag extractFloat128Sign( float128 a ) +{ + + return a.high>>63; + +} + +/* +------------------------------------------------------------------------------- +Normalizes the subnormal quadruple-precision floating-point value +represented by the denormalized significand formed by the concatenation of +`aSig0' and `aSig1'. The normalized exponent is stored at the location +pointed to by `zExpPtr'. The most significant 49 bits of the normalized +significand are stored at the location pointed to by `zSig0Ptr', and the +least significant 64 bits of the normalized significand are stored at the +location pointed to by `zSig1Ptr'. +------------------------------------------------------------------------------- +*/ +static void + normalizeFloat128Subnormal( + bits64 aSig0, + bits64 aSig1, + int32 *zExpPtr, + bits64 *zSig0Ptr, + bits64 *zSig1Ptr + ) +{ + int8 shiftCount; + + if ( aSig0 == 0 ) { + shiftCount = countLeadingZeros64( aSig1 ) - 15; + if ( shiftCount < 0 ) { + *zSig0Ptr = aSig1>>( - shiftCount ); + *zSig1Ptr = aSig1<<( shiftCount & 63 ); + } + else { + *zSig0Ptr = aSig1<>( - shiftCount ); + if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) { + float_exception_flags |= float_flag_inexact; + } + return aSign ? - z : z; + +} + +/* +------------------------------------------------------------------------------- +Returns the result of converting the single-precision floating-point value +`a' to the double-precision floating-point format. The conversion is +performed according to the IEC/IEEE Standard for Binary Floating-point +Arithmetic. +------------------------------------------------------------------------------- +*/ +float64 float32_to_float64( float32 a ) +{ + flag aSign; + int16 aExp; + bits32 aSig; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + if ( aExp == 0xFF ) { + if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a ) ); + return packFloat64( aSign, 0x7FF, 0 ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloat64( aSign, 0, 0 ); + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + --aExp; + } + return packFloat64( aSign, aExp + 0x380, ( (bits64) aSig )<<29 ); + +} + +#ifdef FLOATX80 + +/* +------------------------------------------------------------------------------- +Returns the result of converting the single-precision floating-point value +`a' to the extended double-precision floating-point format. The conversion +is performed according to the IEC/IEEE Standard for Binary Floating-point +Arithmetic. +------------------------------------------------------------------------------- +*/ +floatx80 float32_to_floatx80( float32 a ) +{ + flag aSign; + int16 aExp; + bits32 aSig; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + if ( aExp == 0xFF ) { + if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a ) ); + return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 ); + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + } + aSig |= 0x00800000; + return packFloatx80( aSign, aExp + 0x3F80, ( (bits64) aSig )<<40 ); + +} + +#endif + +#ifdef FLOAT128 + +/* +------------------------------------------------------------------------------- +Returns the result of converting the single-precision floating-point value +`a' to the double-precision floating-point format. The conversion is +performed according to the IEC/IEEE Standard for Binary Floating-point +Arithmetic. +------------------------------------------------------------------------------- +*/ +float128 float32_to_float128( float32 a ) +{ + flag aSign; + int16 aExp; + bits32 aSig; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + if ( aExp == 0xFF ) { + if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a ) ); + return packFloat128( aSign, 0x7FFF, 0, 0 ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 ); + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + --aExp; + } + return packFloat128( aSign, aExp + 0x3F80, ( (bits64) aSig )<<25, 0 ); + +} + +#endif + +/* +------------------------------------------------------------------------------- +Rounds the single-precision floating-point value `a' to an integer, and +returns the result as a single-precision floating-point value. The +operation is performed according to the IEC/IEEE Standard for Binary +Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float32 float32_round_to_int( float32 a ) +{ + flag aSign; + int16 aExp; + bits32 lastBitMask, roundBitsMask; + int8 roundingMode; + float32 z; + + aExp = extractFloat32Exp( a ); + if ( 0x96 <= aExp ) { + if ( ( aExp == 0xFF ) && extractFloat32Frac( a ) ) { + return propagateFloat32NaN( a, a ); + } + return a; + } + if ( aExp <= 0x7E ) { + if ( (bits32) ( a<<1 ) == 0 ) return a; + float_exception_flags |= float_flag_inexact; + aSign = extractFloat32Sign( a ); + switch ( float_rounding_mode ) { + case float_round_nearest_even: + if ( ( aExp == 0x7E ) && extractFloat32Frac( a ) ) { + return packFloat32( aSign, 0x7F, 0 ); + } + break; + case float_round_down: + return aSign ? 0xBF800000 : 0; + case float_round_up: + return aSign ? 0x80000000 : 0x3F800000; + } + return packFloat32( aSign, 0, 0 ); + } + lastBitMask = 1; + lastBitMask <<= 0x96 - aExp; + roundBitsMask = lastBitMask - 1; + z = a; + roundingMode = float_rounding_mode; + if ( roundingMode == float_round_nearest_even ) { + z += lastBitMask>>1; + if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask; + } + else if ( roundingMode != float_round_to_zero ) { + if ( extractFloat32Sign( z ) ^ ( roundingMode == float_round_up ) ) { + z += roundBitsMask; + } + } + z &= ~ roundBitsMask; + if ( z != a ) float_exception_flags |= float_flag_inexact; + return z; + +} + +/* +------------------------------------------------------------------------------- +Returns the result of adding the absolute values of the single-precision +floating-point values `a' and `b'. If `zSign' is true, the sum is negated +before being returned. `zSign' is ignored if the result is a NaN. The +addition is performed according to the IEC/IEEE Standard for Binary +Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +static float32 addFloat32Sigs( float32 a, float32 b, flag zSign ) +{ + int16 aExp, bExp, zExp; + bits32 aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + bSig = extractFloat32Frac( b ); + bExp = extractFloat32Exp( b ); + expDiff = aExp - bExp; + aSig <<= 6; + bSig <<= 6; + if ( 0 < expDiff ) { + if ( aExp == 0xFF ) { + if ( aSig ) return propagateFloat32NaN( a, b ); + return a; + } + if ( bExp == 0 ) { + --expDiff; + } + else { + bSig |= 0x20000000; + } + shift32RightJamming( bSig, expDiff, &bSig ); + zExp = aExp; + } + else if ( expDiff < 0 ) { + if ( bExp == 0xFF ) { + if ( bSig ) return propagateFloat32NaN( a, b ); + return packFloat32( zSign, 0xFF, 0 ); + } + if ( aExp == 0 ) { + ++expDiff; + } + else { + aSig |= 0x20000000; + } + shift32RightJamming( aSig, - expDiff, &aSig ); + zExp = bExp; + } + else { + if ( aExp == 0xFF ) { + if ( aSig | bSig ) return propagateFloat32NaN( a, b ); + return a; + } + if ( aExp == 0 ) return packFloat32( zSign, 0, ( aSig + bSig )>>6 ); + zSig = 0x40000000 + aSig + bSig; + zExp = aExp; + goto roundAndPack; + } + aSig |= 0x20000000; + zSig = ( aSig + bSig )<<1; + --zExp; + if ( (sbits32) zSig < 0 ) { + zSig = aSig + bSig; + ++zExp; + } + roundAndPack: + return roundAndPackFloat32( zSign, zExp, zSig ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of subtracting the absolute values of the single- +precision floating-point values `a' and `b'. If `zSign' is true, the +difference is negated before being returned. `zSign' is ignored if the +result is a NaN. The subtraction is performed according to the IEC/IEEE +Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +static float32 subFloat32Sigs( float32 a, float32 b, flag zSign ) +{ + int16 aExp, bExp, zExp; + bits32 aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + bSig = extractFloat32Frac( b ); + bExp = extractFloat32Exp( b ); + expDiff = aExp - bExp; + aSig <<= 7; + bSig <<= 7; + if ( 0 < expDiff ) goto aExpBigger; + if ( expDiff < 0 ) goto bExpBigger; + if ( aExp == 0xFF ) { + if ( aSig | bSig ) return propagateFloat32NaN( a, b ); + float_raise( float_flag_invalid ); + return float32_default_nan; + } + if ( aExp == 0 ) { + aExp = 1; + bExp = 1; + } + if ( bSig < aSig ) goto aBigger; + if ( aSig < bSig ) goto bBigger; + return packFloat32( float_rounding_mode == float_round_down, 0, 0 ); + bExpBigger: + if ( bExp == 0xFF ) { + if ( bSig ) return propagateFloat32NaN( a, b ); + return packFloat32( zSign ^ 1, 0xFF, 0 ); + } + if ( aExp == 0 ) { + ++expDiff; + } + else { + aSig |= 0x40000000; + } + shift32RightJamming( aSig, - expDiff, &aSig ); + bSig |= 0x40000000; + bBigger: + zSig = bSig - aSig; + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; + aExpBigger: + if ( aExp == 0xFF ) { + if ( aSig ) return propagateFloat32NaN( a, b ); + return a; + } + if ( bExp == 0 ) { + --expDiff; + } + else { + bSig |= 0x40000000; + } + shift32RightJamming( bSig, expDiff, &bSig ); + aSig |= 0x40000000; + aBigger: + zSig = aSig - bSig; + zExp = aExp; + normalizeRoundAndPack: + --zExp; + return normalizeRoundAndPackFloat32( zSign, zExp, zSig ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of adding the single-precision floating-point values `a' +and `b'. The operation is performed according to the IEC/IEEE Standard for +Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float32 float32_add( float32 a, float32 b ) +{ + flag aSign, bSign; + + aSign = extractFloat32Sign( a ); + bSign = extractFloat32Sign( b ); + if ( aSign == bSign ) { + return addFloat32Sigs( a, b, aSign ); + } + else { + return subFloat32Sigs( a, b, aSign ); + } + +} + +/* +------------------------------------------------------------------------------- +Returns the result of subtracting the single-precision floating-point values +`a' and `b'. The operation is performed according to the IEC/IEEE Standard +for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float32 float32_sub( float32 a, float32 b ) +{ + flag aSign, bSign; + + aSign = extractFloat32Sign( a ); + bSign = extractFloat32Sign( b ); + if ( aSign == bSign ) { + return subFloat32Sigs( a, b, aSign ); + } + else { + return addFloat32Sigs( a, b, aSign ); + } + +} + +/* +------------------------------------------------------------------------------- +Returns the result of multiplying the single-precision floating-point values +`a' and `b'. The operation is performed according to the IEC/IEEE Standard +for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float32 float32_mul( float32 a, float32 b ) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + bits32 aSig, bSig; + bits64 zSig64; + bits32 zSig; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + bSig = extractFloat32Frac( b ); + bExp = extractFloat32Exp( b ); + bSign = extractFloat32Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0xFF ) { + if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) { + return propagateFloat32NaN( a, b ); + } + if ( ( bExp | bSig ) == 0 ) { + float_raise( float_flag_invalid ); + return float32_default_nan; + } + return packFloat32( zSign, 0xFF, 0 ); + } + if ( bExp == 0xFF ) { + if ( bSig ) return propagateFloat32NaN( a, b ); + if ( ( aExp | aSig ) == 0 ) { + float_raise( float_flag_invalid ); + return float32_default_nan; + } + return packFloat32( zSign, 0xFF, 0 ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloat32( zSign, 0, 0 ); + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + } + if ( bExp == 0 ) { + if ( bSig == 0 ) return packFloat32( zSign, 0, 0 ); + normalizeFloat32Subnormal( bSig, &bExp, &bSig ); + } + zExp = aExp + bExp - 0x7F; + aSig = ( aSig | 0x00800000 )<<7; + bSig = ( bSig | 0x00800000 )<<8; + shift64RightJamming( ( (bits64) aSig ) * bSig, 32, &zSig64 ); + zSig = zSig64; + if ( 0 <= (sbits32) ( zSig<<1 ) ) { + zSig <<= 1; + --zExp; + } + return roundAndPackFloat32( zSign, zExp, zSig ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of dividing the single-precision floating-point value `a' +by the corresponding value `b'. The operation is performed according to the +IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float32 float32_div( float32 a, float32 b ) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + bits32 aSig, bSig, zSig; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + bSig = extractFloat32Frac( b ); + bExp = extractFloat32Exp( b ); + bSign = extractFloat32Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0xFF ) { + if ( aSig ) return propagateFloat32NaN( a, b ); + if ( bExp == 0xFF ) { + if ( bSig ) return propagateFloat32NaN( a, b ); + float_raise( float_flag_invalid ); + return float32_default_nan; + } + return packFloat32( zSign, 0xFF, 0 ); + } + if ( bExp == 0xFF ) { + if ( bSig ) return propagateFloat32NaN( a, b ); + return packFloat32( zSign, 0, 0 ); + } + if ( bExp == 0 ) { + if ( bSig == 0 ) { + if ( ( aExp | aSig ) == 0 ) { + float_raise( float_flag_invalid ); + return float32_default_nan; + } + float_raise( float_flag_divbyzero ); + return packFloat32( zSign, 0xFF, 0 ); + } + normalizeFloat32Subnormal( bSig, &bExp, &bSig ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloat32( zSign, 0, 0 ); + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + } + zExp = aExp - bExp + 0x7D; + aSig = ( aSig | 0x00800000 )<<7; + bSig = ( bSig | 0x00800000 )<<8; + if ( bSig <= ( aSig + aSig ) ) { + aSig >>= 1; + ++zExp; + } + zSig = ( ( (bits64) aSig )<<32 ) / bSig; + if ( ( zSig & 0x3F ) == 0 ) { + zSig |= ( ( (bits64) bSig ) * zSig != ( (bits64) aSig )<<32 ); + } + return roundAndPackFloat32( zSign, zExp, zSig ); + +} + +/* +------------------------------------------------------------------------------- +Returns the remainder of the single-precision floating-point value `a' +with respect to the corresponding value `b'. The operation is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float32 float32_rem( float32 a, float32 b ) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, expDiff; + bits32 aSig, bSig; + bits32 q; + bits64 aSig64, bSig64, q64; + bits32 alternateASig; + sbits32 sigMean; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + bSig = extractFloat32Frac( b ); + bExp = extractFloat32Exp( b ); + bSign = extractFloat32Sign( b ); + if ( aExp == 0xFF ) { + if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) { + return propagateFloat32NaN( a, b ); + } + float_raise( float_flag_invalid ); + return float32_default_nan; + } + if ( bExp == 0xFF ) { + if ( bSig ) return propagateFloat32NaN( a, b ); + return a; + } + if ( bExp == 0 ) { + if ( bSig == 0 ) { + float_raise( float_flag_invalid ); + return float32_default_nan; + } + normalizeFloat32Subnormal( bSig, &bExp, &bSig ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return a; + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + } + expDiff = aExp - bExp; + aSig |= 0x00800000; + bSig |= 0x00800000; + if ( expDiff < 32 ) { + aSig <<= 8; + bSig <<= 8; + if ( expDiff < 0 ) { + if ( expDiff < -1 ) return a; + aSig >>= 1; + } + q = ( bSig <= aSig ); + if ( q ) aSig -= bSig; + if ( 0 < expDiff ) { + q = ( ( (bits64) aSig )<<32 ) / bSig; + q >>= 32 - expDiff; + bSig >>= 2; + aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q; + } + else { + aSig >>= 2; + bSig >>= 2; + } + } + else { + if ( bSig <= aSig ) aSig -= bSig; + aSig64 = ( (bits64) aSig )<<40; + bSig64 = ( (bits64) bSig )<<40; + expDiff -= 64; + while ( 0 < expDiff ) { + q64 = estimateDiv128To64( aSig64, 0, bSig64 ); + q64 = ( 2 < q64 ) ? q64 - 2 : 0; + aSig64 = - ( ( bSig * q64 )<<38 ); + expDiff -= 62; + } + expDiff += 64; + q64 = estimateDiv128To64( aSig64, 0, bSig64 ); + q64 = ( 2 < q64 ) ? q64 - 2 : 0; + q = q64>>( 64 - expDiff ); + bSig <<= 6; + aSig = ( ( aSig64>>33 )<<( expDiff - 1 ) ) - bSig * q; + } + do { + alternateASig = aSig; + ++q; + aSig -= bSig; + } while ( 0 <= (sbits32) aSig ); + sigMean = aSig + alternateASig; + if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) { + aSig = alternateASig; + } + zSign = ( (sbits32) aSig < 0 ); + if ( zSign ) aSig = - aSig; + return normalizeRoundAndPackFloat32( aSign ^ zSign, bExp, aSig ); + +} + +/* +------------------------------------------------------------------------------- +Returns the square root of the single-precision floating-point value `a'. +The operation is performed according to the IEC/IEEE Standard for Binary +Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float32 float32_sqrt( float32 a ) +{ + flag aSign; + int16 aExp, zExp; + bits32 aSig, zSig; + bits64 rem, term; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + if ( aExp == 0xFF ) { + if ( aSig ) return propagateFloat32NaN( a, 0 ); + if ( ! aSign ) return a; + float_raise( float_flag_invalid ); + return float32_default_nan; + } + if ( aSign ) { + if ( ( aExp | aSig ) == 0 ) return a; + float_raise( float_flag_invalid ); + return float32_default_nan; + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return 0; + normalizeFloat32Subnormal( aSig, &aExp, &aSig ); + } + zExp = ( ( aExp - 0x7F )>>1 ) + 0x7E; + aSig = ( aSig | 0x00800000 )<<8; + zSig = estimateSqrt32( aExp, aSig ) + 2; + if ( ( zSig & 0x7F ) <= 5 ) { + if ( zSig < 2 ) { + zSig = 0xFFFFFFFF; + } + else { + aSig >>= aExp & 1; + term = ( (bits64) zSig ) * zSig; + rem = ( ( (bits64) aSig )<<32 ) - term; + while ( (sbits64) rem < 0 ) { + --zSig; + rem += ( ( (bits64) zSig )<<1 ) | 1; + } + zSig |= ( rem != 0 ); + } + } + shift32RightJamming( zSig, 1, &zSig ); + return roundAndPackFloat32( 0, zExp, zSig ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the single-precision floating-point value `a' is equal to the +corresponding value `b', and 0 otherwise. The comparison is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float32_eq( float32 a, float32 b ) +{ + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid ); + } + return 0; + } + return ( a == b ) || ( (bits32) ( ( a | b )<<1 ) == 0 ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the single-precision floating-point value `a' is less than or +equal to the corresponding value `b', and 0 otherwise. The comparison is +performed according to the IEC/IEEE Standard for Binary Floating-point +Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float32_le( float32 a, float32 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + float_raise( float_flag_invalid ); + return 0; + } + aSign = extractFloat32Sign( a ); + bSign = extractFloat32Sign( b ); + if ( aSign != bSign ) return aSign || ( (bits32) ( ( a | b )<<1 ) == 0 ); + return ( a == b ) || ( aSign ^ ( a < b ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the single-precision floating-point value `a' is less than +the corresponding value `b', and 0 otherwise. The comparison is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float32_lt( float32 a, float32 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + float_raise( float_flag_invalid ); + return 0; + } + aSign = extractFloat32Sign( a ); + bSign = extractFloat32Sign( b ); + if ( aSign != bSign ) return aSign && ( (bits32) ( ( a | b )<<1 ) != 0 ); + return ( a != b ) && ( aSign ^ ( a < b ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the single-precision floating-point value `a' is equal to the +corresponding value `b', and 0 otherwise. The invalid exception is raised +if either operand is a NaN. Otherwise, the comparison is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float32_eq_signaling( float32 a, float32 b ) +{ + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + float_raise( float_flag_invalid ); + return 0; + } + return ( a == b ) || ( (bits32) ( ( a | b )<<1 ) == 0 ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the single-precision floating-point value `a' is less than or +equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not +cause an exception. Otherwise, the comparison is performed according to the +IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float32_le_quiet( float32 a, float32 b ) +{ + flag aSign, bSign; + //int16 aExp, bExp; + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid ); + } + return 0; + } + aSign = extractFloat32Sign( a ); + bSign = extractFloat32Sign( b ); + if ( aSign != bSign ) return aSign || ( (bits32) ( ( a | b )<<1 ) == 0 ); + return ( a == b ) || ( aSign ^ ( a < b ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the single-precision floating-point value `a' is less than +the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +exception. Otherwise, the comparison is performed according to the IEC/IEEE +Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float32_lt_quiet( float32 a, float32 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) + || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) + ) { + if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid ); + } + return 0; + } + aSign = extractFloat32Sign( a ); + bSign = extractFloat32Sign( b ); + if ( aSign != bSign ) return aSign && ( (bits32) ( ( a | b )<<1 ) != 0 ); + return ( a != b ) && ( aSign ^ ( a < b ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of converting the double-precision floating-point value +`a' to the 32-bit two's complement integer format. The conversion is +performed according to the IEC/IEEE Standard for Binary Floating-point +Arithmetic---which means in particular that the conversion is rounded +according to the current rounding mode. If `a' is a NaN, the largest +positive integer is returned. Otherwise, if the conversion overflows, the +largest integer with the same sign as `a' is returned. +------------------------------------------------------------------------------- +*/ +int32 float64_to_int32( float64 a ) +{ + flag aSign; + int16 aExp, shiftCount; + bits64 aSig; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + if ( ( aExp == 0x7FF ) && aSig ) aSign = 0; + if ( aExp ) aSig |= LIT64( 0x0010000000000000 ); + shiftCount = 0x42C - aExp; + if ( 0 < shiftCount ) shift64RightJamming( aSig, shiftCount, &aSig ); + return roundAndPackInt32( aSign, aSig ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of converting the double-precision floating-point value +`a' to the 32-bit two's complement integer format. The conversion is +performed according to the IEC/IEEE Standard for Binary Floating-point +Arithmetic, except that the conversion is always rounded toward zero. If +`a' is a NaN, the largest positive integer is returned. Otherwise, if the +conversion overflows, the largest integer with the same sign as `a' is +returned. +------------------------------------------------------------------------------- +*/ +int32 float64_to_int32_round_to_zero( float64 a ) +{ + flag aSign; + int16 aExp, shiftCount; + bits64 aSig, savedASig; + int32 z; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + shiftCount = 0x433 - aExp; + if ( shiftCount < 21 ) { + if ( ( aExp == 0x7FF ) && aSig ) aSign = 0; + goto invalid; + } + else if ( 52 < shiftCount ) { + if ( aExp || aSig ) float_exception_flags |= float_flag_inexact; + return 0; + } + aSig |= LIT64( 0x0010000000000000 ); + savedASig = aSig; + aSig >>= shiftCount; + z = aSig; + if ( aSign ) z = - z; + if ( ( z < 0 ) ^ aSign ) { + invalid: + float_exception_flags |= float_flag_invalid; + return aSign ? 0x80000000 : 0x7FFFFFFF; + } + if ( ( aSig<>= shiftCount; + z = aSig; + if ( aSign ) z = - z; + if ( ( z < 0 ) ^ aSign ) { + invalid: + float_exception_flags |= float_flag_invalid; + return aSign ? 0x80000000 : 0x7FFFFFFF; + } + if ( ( aSig<>1; + if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask; + } + else if ( roundingMode != float_round_to_zero ) { + if ( extractFloat64Sign( z ) ^ ( roundingMode == float_round_up ) ) { + z += roundBitsMask; + } + } + z &= ~ roundBitsMask; + if ( z != a ) float_exception_flags |= float_flag_inexact; + return z; + +} + +/* +------------------------------------------------------------------------------- +Returns the result of adding the absolute values of the double-precision +floating-point values `a' and `b'. If `zSign' is true, the sum is negated +before being returned. `zSign' is ignored if the result is a NaN. The +addition is performed according to the IEC/IEEE Standard for Binary +Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +static float64 addFloat64Sigs( float64 a, float64 b, flag zSign ) +{ + int16 aExp, bExp, zExp; + bits64 aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + bSig = extractFloat64Frac( b ); + bExp = extractFloat64Exp( b ); + expDiff = aExp - bExp; + aSig <<= 9; + bSig <<= 9; + if ( 0 < expDiff ) { + if ( aExp == 0x7FF ) { + if ( aSig ) return propagateFloat64NaN( a, b ); + return a; + } + if ( bExp == 0 ) { + --expDiff; + } + else { + bSig |= LIT64( 0x2000000000000000 ); + } + shift64RightJamming( bSig, expDiff, &bSig ); + zExp = aExp; + } + else if ( expDiff < 0 ) { + if ( bExp == 0x7FF ) { + if ( bSig ) return propagateFloat64NaN( a, b ); + return packFloat64( zSign, 0x7FF, 0 ); + } + if ( aExp == 0 ) { + ++expDiff; + } + else { + aSig |= LIT64( 0x2000000000000000 ); + } + shift64RightJamming( aSig, - expDiff, &aSig ); + zExp = bExp; + } + else { + if ( aExp == 0x7FF ) { + if ( aSig | bSig ) return propagateFloat64NaN( a, b ); + return a; + } + if ( aExp == 0 ) return packFloat64( zSign, 0, ( aSig + bSig )>>9 ); + zSig = LIT64( 0x4000000000000000 ) + aSig + bSig; + zExp = aExp; + goto roundAndPack; + } + aSig |= LIT64( 0x2000000000000000 ); + zSig = ( aSig + bSig )<<1; + --zExp; + if ( (sbits64) zSig < 0 ) { + zSig = aSig + bSig; + ++zExp; + } + roundAndPack: + return roundAndPackFloat64( zSign, zExp, zSig ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of subtracting the absolute values of the double- +precision floating-point values `a' and `b'. If `zSign' is true, the +difference is negated before being returned. `zSign' is ignored if the +result is a NaN. The subtraction is performed according to the IEC/IEEE +Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +static float64 subFloat64Sigs( float64 a, float64 b, flag zSign ) +{ + int16 aExp, bExp, zExp; + bits64 aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + bSig = extractFloat64Frac( b ); + bExp = extractFloat64Exp( b ); + expDiff = aExp - bExp; + aSig <<= 10; + bSig <<= 10; + if ( 0 < expDiff ) goto aExpBigger; + if ( expDiff < 0 ) goto bExpBigger; + if ( aExp == 0x7FF ) { + if ( aSig | bSig ) return propagateFloat64NaN( a, b ); + float_raise( float_flag_invalid ); + return float64_default_nan; + } + if ( aExp == 0 ) { + aExp = 1; + bExp = 1; + } + if ( bSig < aSig ) goto aBigger; + if ( aSig < bSig ) goto bBigger; + return packFloat64( float_rounding_mode == float_round_down, 0, 0 ); + bExpBigger: + if ( bExp == 0x7FF ) { + if ( bSig ) return propagateFloat64NaN( a, b ); + return packFloat64( zSign ^ 1, 0x7FF, 0 ); + } + if ( aExp == 0 ) { + ++expDiff; + } + else { + aSig |= LIT64( 0x4000000000000000 ); + } + shift64RightJamming( aSig, - expDiff, &aSig ); + bSig |= LIT64( 0x4000000000000000 ); + bBigger: + zSig = bSig - aSig; + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; + aExpBigger: + if ( aExp == 0x7FF ) { + if ( aSig ) return propagateFloat64NaN( a, b ); + return a; + } + if ( bExp == 0 ) { + --expDiff; + } + else { + bSig |= LIT64( 0x4000000000000000 ); + } + shift64RightJamming( bSig, expDiff, &bSig ); + aSig |= LIT64( 0x4000000000000000 ); + aBigger: + zSig = aSig - bSig; + zExp = aExp; + normalizeRoundAndPack: + --zExp; + return normalizeRoundAndPackFloat64( zSign, zExp, zSig ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of adding the double-precision floating-point values `a' +and `b'. The operation is performed according to the IEC/IEEE Standard for +Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float64 float64_add( float64 a, float64 b ) +{ + flag aSign, bSign; + + aSign = extractFloat64Sign( a ); + bSign = extractFloat64Sign( b ); + if ( aSign == bSign ) { + return addFloat64Sigs( a, b, aSign ); + } + else { + return subFloat64Sigs( a, b, aSign ); + } + +} + +/* +------------------------------------------------------------------------------- +Returns the result of subtracting the double-precision floating-point values +`a' and `b'. The operation is performed according to the IEC/IEEE Standard +for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float64 float64_sub( float64 a, float64 b ) +{ + flag aSign, bSign; + + aSign = extractFloat64Sign( a ); + bSign = extractFloat64Sign( b ); + if ( aSign == bSign ) { + return subFloat64Sigs( a, b, aSign ); + } + else { + return addFloat64Sigs( a, b, aSign ); + } + +} + +/* +------------------------------------------------------------------------------- +Returns the result of multiplying the double-precision floating-point values +`a' and `b'. The operation is performed according to the IEC/IEEE Standard +for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float64 float64_mul( float64 a, float64 b ) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + bits64 aSig, bSig, zSig0, zSig1; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + bSig = extractFloat64Frac( b ); + bExp = extractFloat64Exp( b ); + bSign = extractFloat64Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0x7FF ) { + if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) { + return propagateFloat64NaN( a, b ); + } + if ( ( bExp | bSig ) == 0 ) { + float_raise( float_flag_invalid ); + return float64_default_nan; + } + return packFloat64( zSign, 0x7FF, 0 ); + } + if ( bExp == 0x7FF ) { + if ( bSig ) return propagateFloat64NaN( a, b ); + if ( ( aExp | aSig ) == 0 ) { + float_raise( float_flag_invalid ); + return float64_default_nan; + } + return packFloat64( zSign, 0x7FF, 0 ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloat64( zSign, 0, 0 ); + normalizeFloat64Subnormal( aSig, &aExp, &aSig ); + } + if ( bExp == 0 ) { + if ( bSig == 0 ) return packFloat64( zSign, 0, 0 ); + normalizeFloat64Subnormal( bSig, &bExp, &bSig ); + } + zExp = aExp + bExp - 0x3FF; + aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10; + bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11; + mul64To128( aSig, bSig, &zSig0, &zSig1 ); + zSig0 |= ( zSig1 != 0 ); + if ( 0 <= (sbits64) ( zSig0<<1 ) ) { + zSig0 <<= 1; + --zExp; + } + return roundAndPackFloat64( zSign, zExp, zSig0 ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of dividing the double-precision floating-point value `a' +by the corresponding value `b'. The operation is performed according to +the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float64 float64_div( float64 a, float64 b ) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + bits64 aSig, bSig, zSig; + bits64 rem0, rem1; + bits64 term0, term1; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + bSig = extractFloat64Frac( b ); + bExp = extractFloat64Exp( b ); + bSign = extractFloat64Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0x7FF ) { + if ( aSig ) return propagateFloat64NaN( a, b ); + if ( bExp == 0x7FF ) { + if ( bSig ) return propagateFloat64NaN( a, b ); + float_raise( float_flag_invalid ); + return float64_default_nan; + } + return packFloat64( zSign, 0x7FF, 0 ); + } + if ( bExp == 0x7FF ) { + if ( bSig ) return propagateFloat64NaN( a, b ); + return packFloat64( zSign, 0, 0 ); + } + if ( bExp == 0 ) { + if ( bSig == 0 ) { + if ( ( aExp | aSig ) == 0 ) { + float_raise( float_flag_invalid ); + return float64_default_nan; + } + float_raise( float_flag_divbyzero ); + return packFloat64( zSign, 0x7FF, 0 ); + } + normalizeFloat64Subnormal( bSig, &bExp, &bSig ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloat64( zSign, 0, 0 ); + normalizeFloat64Subnormal( aSig, &aExp, &aSig ); + } + zExp = aExp - bExp + 0x3FD; + aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10; + bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11; + if ( bSig <= ( aSig + aSig ) ) { + aSig >>= 1; + ++zExp; + } + zSig = estimateDiv128To64( aSig, 0, bSig ); + if ( ( zSig & 0x1FF ) <= 2 ) { + mul64To128( bSig, zSig, &term0, &term1 ); + sub128( aSig, 0, term0, term1, &rem0, &rem1 ); + while ( (sbits64) rem0 < 0 ) { + --zSig; + add128( rem0, rem1, 0, bSig, &rem0, &rem1 ); + } + zSig |= ( rem1 != 0 ); + } + return roundAndPackFloat64( zSign, zExp, zSig ); + +} + +/* +------------------------------------------------------------------------------- +Returns the remainder of the double-precision floating-point value `a' +with respect to the corresponding value `b'. The operation is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float64 float64_rem( float64 a, float64 b ) +{ + flag aSign, bSign, zSign; + int16 aExp, bExp, expDiff; + bits64 aSig, bSig; + bits64 q, alternateASig; + sbits64 sigMean; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + bSig = extractFloat64Frac( b ); + bExp = extractFloat64Exp( b ); + bSign = extractFloat64Sign( b ); + if ( aExp == 0x7FF ) { + if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) { + return propagateFloat64NaN( a, b ); + } + float_raise( float_flag_invalid ); + return float64_default_nan; + } + if ( bExp == 0x7FF ) { + if ( bSig ) return propagateFloat64NaN( a, b ); + return a; + } + if ( bExp == 0 ) { + if ( bSig == 0 ) { + float_raise( float_flag_invalid ); + return float64_default_nan; + } + normalizeFloat64Subnormal( bSig, &bExp, &bSig ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return a; + normalizeFloat64Subnormal( aSig, &aExp, &aSig ); + } + expDiff = aExp - bExp; + aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<11; + bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11; + if ( expDiff < 0 ) { + if ( expDiff < -1 ) return a; + aSig >>= 1; + } + q = ( bSig <= aSig ); + if ( q ) aSig -= bSig; + expDiff -= 64; + while ( 0 < expDiff ) { + q = estimateDiv128To64( aSig, 0, bSig ); + q = ( 2 < q ) ? q - 2 : 0; + aSig = - ( ( bSig>>2 ) * q ); + expDiff -= 62; + } + expDiff += 64; + if ( 0 < expDiff ) { + q = estimateDiv128To64( aSig, 0, bSig ); + q = ( 2 < q ) ? q - 2 : 0; + q >>= 64 - expDiff; + bSig >>= 2; + aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q; + } + else { + aSig >>= 2; + bSig >>= 2; + } + do { + alternateASig = aSig; + ++q; + aSig -= bSig; + } while ( 0 <= (sbits64) aSig ); + sigMean = aSig + alternateASig; + if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) { + aSig = alternateASig; + } + zSign = ( (sbits64) aSig < 0 ); + if ( zSign ) aSig = - aSig; + return normalizeRoundAndPackFloat64( aSign ^ zSign, bExp, aSig ); + +} + +/* +------------------------------------------------------------------------------- +Returns the square root of the double-precision floating-point value `a'. +The operation is performed according to the IEC/IEEE Standard for Binary +Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float64 float64_sqrt( float64 a ) +{ + flag aSign; + int16 aExp, zExp; + bits64 aSig, zSig; + bits64 rem0, rem1, term0, term1; //, shiftedRem; + //float64 z; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + if ( aExp == 0x7FF ) { + if ( aSig ) return propagateFloat64NaN( a, a ); + if ( ! aSign ) return a; + float_raise( float_flag_invalid ); + return float64_default_nan; + } + if ( aSign ) { + if ( ( aExp | aSig ) == 0 ) return a; + float_raise( float_flag_invalid ); + return float64_default_nan; + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return 0; + normalizeFloat64Subnormal( aSig, &aExp, &aSig ); + } + zExp = ( ( aExp - 0x3FF )>>1 ) + 0x3FE; + aSig |= LIT64( 0x0010000000000000 ); + zSig = estimateSqrt32( aExp, aSig>>21 ); + zSig <<= 31; + aSig <<= 9 - ( aExp & 1 ); + zSig = estimateDiv128To64( aSig, 0, zSig ) + zSig + 2; + if ( ( zSig & 0x3FF ) <= 5 ) { + if ( zSig < 2 ) { + zSig = LIT64( 0xFFFFFFFFFFFFFFFF ); + } + else { + aSig <<= 2; + mul64To128( zSig, zSig, &term0, &term1 ); + sub128( aSig, 0, term0, term1, &rem0, &rem1 ); + while ( (sbits64) rem0 < 0 ) { + --zSig; + shortShift128Left( 0, zSig, 1, &term0, &term1 ); + term1 |= 1; + add128( rem0, rem1, term0, term1, &rem0, &rem1 ); + } + zSig |= ( ( rem0 | rem1 ) != 0 ); + } + } + shift64RightJamming( zSig, 1, &zSig ); + return roundAndPackFloat64( 0, zExp, zSig ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the double-precision floating-point value `a' is equal to the +corresponding value `b', and 0 otherwise. The comparison is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float64_eq( float64 a, float64 b ) +{ + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid ); + } + return 0; + } + return ( a == b ) || ( (bits64) ( ( a | b )<<1 ) == 0 ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the double-precision floating-point value `a' is less than or +equal to the corresponding value `b', and 0 otherwise. The comparison is +performed according to the IEC/IEEE Standard for Binary Floating-point +Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float64_le( float64 a, float64 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + float_raise( float_flag_invalid ); + return 0; + } + aSign = extractFloat64Sign( a ); + bSign = extractFloat64Sign( b ); + if ( aSign != bSign ) return aSign || ( (bits64) ( ( a | b )<<1 ) == 0 ); + return ( a == b ) || ( aSign ^ ( a < b ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the double-precision floating-point value `a' is less than +the corresponding value `b', and 0 otherwise. The comparison is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float64_lt( float64 a, float64 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + float_raise( float_flag_invalid ); + return 0; + } + aSign = extractFloat64Sign( a ); + bSign = extractFloat64Sign( b ); + if ( aSign != bSign ) return aSign && ( (bits64) ( ( a | b )<<1 ) != 0 ); + return ( a != b ) && ( aSign ^ ( a < b ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the double-precision floating-point value `a' is equal to the +corresponding value `b', and 0 otherwise. The invalid exception is raised +if either operand is a NaN. Otherwise, the comparison is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float64_eq_signaling( float64 a, float64 b ) +{ + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + float_raise( float_flag_invalid ); + return 0; + } + return ( a == b ) || ( (bits64) ( ( a | b )<<1 ) == 0 ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the double-precision floating-point value `a' is less than or +equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not +cause an exception. Otherwise, the comparison is performed according to the +IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float64_le_quiet( float64 a, float64 b ) +{ + flag aSign, bSign; + //int16 aExp, bExp; + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid ); + } + return 0; + } + aSign = extractFloat64Sign( a ); + bSign = extractFloat64Sign( b ); + if ( aSign != bSign ) return aSign || ( (bits64) ( ( a | b )<<1 ) == 0 ); + return ( a == b ) || ( aSign ^ ( a < b ) ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the double-precision floating-point value `a' is less than +the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +exception. Otherwise, the comparison is performed according to the IEC/IEEE +Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float64_lt_quiet( float64 a, float64 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) ) + || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) ) + ) { + if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid ); + } + return 0; + } + aSign = extractFloat64Sign( a ); + bSign = extractFloat64Sign( b ); + if ( aSign != bSign ) return aSign && ( (bits64) ( ( a | b )<<1 ) != 0 ); + return ( a != b ) && ( aSign ^ ( a < b ) ); + +} + +#ifdef FLOATX80 + +/* +------------------------------------------------------------------------------- +Returns the result of converting the extended double-precision floating- +point value `a' to the 32-bit two's complement integer format. The +conversion is performed according to the IEC/IEEE Standard for Binary +Floating-point Arithmetic---which means in particular that the conversion +is rounded according to the current rounding mode. If `a' is a NaN, the +largest positive integer is returned. Otherwise, if the conversion +overflows, the largest integer with the same sign as `a' is returned. +------------------------------------------------------------------------------- +*/ +int32 floatx80_to_int32( floatx80 a ) +{ + flag aSign; + int32 aExp, shiftCount; + bits64 aSig; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0; + shiftCount = 0x4037 - aExp; + if ( shiftCount <= 0 ) shiftCount = 1; + shift64RightJamming( aSig, shiftCount, &aSig ); + return roundAndPackInt32( aSign, aSig ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of converting the extended double-precision floating- +point value `a' to the 32-bit two's complement integer format. The +conversion is performed according to the IEC/IEEE Standard for Binary +Floating-point Arithmetic, except that the conversion is always rounded +toward zero. If `a' is a NaN, the largest positive integer is returned. +Otherwise, if the conversion overflows, the largest integer with the same +sign as `a' is returned. +------------------------------------------------------------------------------- +*/ +int32 floatx80_to_int32_round_to_zero( floatx80 a ) +{ + flag aSign; + int32 aExp, shiftCount; + bits64 aSig, savedASig; + int32 z; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + shiftCount = 0x403E - aExp; + if ( shiftCount < 32 ) { + if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0; + goto invalid; + } + else if ( 63 < shiftCount ) { + if ( aExp || aSig ) float_exception_flags |= float_flag_inexact; + return 0; + } + savedASig = aSig; + aSig >>= shiftCount; + z = aSig; + if ( aSign ) z = - z; + if ( ( z < 0 ) ^ aSign ) { + invalid: + float_exception_flags |= float_flag_invalid; + return aSign ? 0x80000000 : 0x7FFFFFFF; + } + if ( ( aSig<>1; + if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask; + } + else if ( roundingMode != float_round_to_zero ) { + if ( extractFloatx80Sign( z ) ^ ( roundingMode == float_round_up ) ) { + z.low += roundBitsMask; + } + } + z.low &= ~ roundBitsMask; + if ( z.low == 0 ) { + ++z.high; + z.low = LIT64( 0x8000000000000000 ); + } + if ( z.low != a.low ) float_exception_flags |= float_flag_inexact; + return z; + +} + +/* +------------------------------------------------------------------------------- +Returns the result of adding the absolute values of the extended double- +precision floating-point values `a' and `b'. If `zSign' is true, the sum is +negated before being returned. `zSign' is ignored if the result is a NaN. +The addition is performed according to the IEC/IEEE Standard for Binary +Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign ) +{ + int32 aExp, bExp, zExp; + bits64 aSig, bSig, zSig0, zSig1; + int32 expDiff; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + bSig = extractFloatx80Frac( b ); + bExp = extractFloatx80Exp( b ); + expDiff = aExp - bExp; + if ( 0 < expDiff ) { + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b ); + return a; + } + if ( bExp == 0 ) --expDiff; + shift64ExtraRightJamming( bSig, 0, expDiff, &bSig, &zSig1 ); + zExp = aExp; + } + else if ( expDiff < 0 ) { + if ( bExp == 0x7FFF ) { + if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b ); + return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( aExp == 0 ) ++expDiff; + shift64ExtraRightJamming( aSig, 0, - expDiff, &aSig, &zSig1 ); + zExp = bExp; + } + else { + if ( aExp == 0x7FFF ) { + if ( (bits64) ( ( aSig | bSig )<<1 ) ) { + return propagateFloatx80NaN( a, b ); + } + return a; + } + zSig1 = 0; + zSig0 = aSig + bSig; + if ( aExp == 0 ) { + normalizeFloatx80Subnormal( zSig0, &zExp, &zSig0 ); + goto roundAndPack; + } + zExp = aExp; + goto shiftRight1; + } + + zSig0 = aSig + bSig; + + if ( (sbits64) zSig0 < 0 ) goto roundAndPack; + shiftRight1: + shift64ExtraRightJamming( zSig0, zSig1, 1, &zSig0, &zSig1 ); + zSig0 |= LIT64( 0x8000000000000000 ); + ++zExp; + roundAndPack: + return + roundAndPackFloatx80( + floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of subtracting the absolute values of the extended +double-precision floating-point values `a' and `b'. If `zSign' is true, +the difference is negated before being returned. `zSign' is ignored if the +result is a NaN. The subtraction is performed according to the IEC/IEEE +Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign ) +{ + int32 aExp, bExp, zExp; + bits64 aSig, bSig, zSig0, zSig1; + int32 expDiff; + floatx80 z; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + bSig = extractFloatx80Frac( b ); + bExp = extractFloatx80Exp( b ); + expDiff = aExp - bExp; + if ( 0 < expDiff ) goto aExpBigger; + if ( expDiff < 0 ) goto bExpBigger; + if ( aExp == 0x7FFF ) { + if ( (bits64) ( ( aSig | bSig )<<1 ) ) { + return propagateFloatx80NaN( a, b ); + } + float_raise( float_flag_invalid ); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + if ( aExp == 0 ) { + aExp = 1; + bExp = 1; + } + zSig1 = 0; + if ( bSig < aSig ) goto aBigger; + if ( aSig < bSig ) goto bBigger; + return packFloatx80( float_rounding_mode == float_round_down, 0, 0 ); + bExpBigger: + if ( bExp == 0x7FFF ) { + if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b ); + return packFloatx80( zSign ^ 1, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( aExp == 0 ) ++expDiff; + shift128RightJamming( aSig, 0, - expDiff, &aSig, &zSig1 ); + bBigger: + sub128( bSig, 0, aSig, zSig1, &zSig0, &zSig1 ); + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; + aExpBigger: + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b ); + return a; + } + if ( bExp == 0 ) --expDiff; + shift128RightJamming( bSig, 0, expDiff, &bSig, &zSig1 ); + aBigger: + sub128( aSig, 0, bSig, zSig1, &zSig0, &zSig1 ); + zExp = aExp; + normalizeRoundAndPack: + return + normalizeRoundAndPackFloatx80( + floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of adding the extended double-precision floating-point +values `a' and `b'. The operation is performed according to the IEC/IEEE +Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +floatx80 floatx80_add( floatx80 a, floatx80 b ) +{ + flag aSign, bSign; + + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign == bSign ) { + return addFloatx80Sigs( a, b, aSign ); + } + else { + return subFloatx80Sigs( a, b, aSign ); + } + +} + +/* +------------------------------------------------------------------------------- +Returns the result of subtracting the extended double-precision floating- +point values `a' and `b'. The operation is performed according to the +IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +floatx80 floatx80_sub( floatx80 a, floatx80 b ) +{ + flag aSign, bSign; + + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign == bSign ) { + return subFloatx80Sigs( a, b, aSign ); + } + else { + return addFloatx80Sigs( a, b, aSign ); + } + +} + +/* +------------------------------------------------------------------------------- +Returns the result of multiplying the extended double-precision floating- +point values `a' and `b'. The operation is performed according to the +IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +floatx80 floatx80_mul( floatx80 a, floatx80 b ) +{ + flag aSign, bSign, zSign; + int32 aExp, bExp, zExp; + bits64 aSig, bSig, zSig0, zSig1; + floatx80 z; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + bSig = extractFloatx80Frac( b ); + bExp = extractFloatx80Exp( b ); + bSign = extractFloatx80Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig<<1 ) + || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) { + return propagateFloatx80NaN( a, b ); + } + if ( ( bExp | bSig ) == 0 ) goto invalid; + return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( bExp == 0x7FFF ) { + if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b ); + if ( ( aExp | aSig ) == 0 ) { + invalid: + float_raise( float_flag_invalid ); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 ); + normalizeFloatx80Subnormal( aSig, &aExp, &aSig ); + } + if ( bExp == 0 ) { + if ( bSig == 0 ) return packFloatx80( zSign, 0, 0 ); + normalizeFloatx80Subnormal( bSig, &bExp, &bSig ); + } + zExp = aExp + bExp - 0x3FFE; + mul64To128( aSig, bSig, &zSig0, &zSig1 ); + if ( 0 < (sbits64) zSig0 ) { + shortShift128Left( zSig0, zSig1, 1, &zSig0, &zSig1 ); + --zExp; + } + return + roundAndPackFloatx80( + floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of dividing the extended double-precision floating-point +value `a' by the corresponding value `b'. The operation is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +floatx80 floatx80_div( floatx80 a, floatx80 b ) +{ + flag aSign, bSign, zSign; + int32 aExp, bExp, zExp; + bits64 aSig, bSig, zSig0, zSig1; + bits64 rem0, rem1, rem2, term0, term1, term2; + floatx80 z; + + aSig = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + bSig = extractFloatx80Frac( b ); + bExp = extractFloatx80Exp( b ); + bSign = extractFloatx80Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b ); + if ( bExp == 0x7FFF ) { + if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b ); + goto invalid; + } + return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + if ( bExp == 0x7FFF ) { + if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b ); + return packFloatx80( zSign, 0, 0 ); + } + if ( bExp == 0 ) { + if ( bSig == 0 ) { + if ( ( aExp | aSig ) == 0 ) { + invalid: + float_raise( float_flag_invalid ); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + float_raise( float_flag_divbyzero ); + return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + } + normalizeFloatx80Subnormal( bSig, &bExp, &bSig ); + } + if ( aExp == 0 ) { + if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 ); + normalizeFloatx80Subnormal( aSig, &aExp, &aSig ); + } + zExp = aExp - bExp + 0x3FFE; + rem1 = 0; + if ( bSig <= aSig ) { + shift128Right( aSig, 0, 1, &aSig, &rem1 ); + ++zExp; + } + zSig0 = estimateDiv128To64( aSig, rem1, bSig ); + mul64To128( bSig, zSig0, &term0, &term1 ); + sub128( aSig, rem1, term0, term1, &rem0, &rem1 ); + while ( (sbits64) rem0 < 0 ) { + --zSig0; + add128( rem0, rem1, 0, bSig, &rem0, &rem1 ); + } + zSig1 = estimateDiv128To64( rem1, 0, bSig ); + if ( (bits64) ( zSig1<<1 ) <= 8 ) { + mul64To128( bSig, zSig1, &term1, &term2 ); + sub128( rem1, 0, term1, term2, &rem1, &rem2 ); + while ( (sbits64) rem1 < 0 ) { + --zSig1; + add128( rem1, rem2, 0, bSig, &rem1, &rem2 ); + } + zSig1 |= ( ( rem1 | rem2 ) != 0 ); + } + return + roundAndPackFloatx80( + floatx80_rounding_precision, zSign, zExp, zSig0, zSig1 ); + +} + +/* +------------------------------------------------------------------------------- +Returns the remainder of the extended double-precision floating-point value +`a' with respect to the corresponding value `b'. The operation is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +floatx80 floatx80_rem( floatx80 a, floatx80 b ) +{ + flag aSign, bSign, zSign; + int32 aExp, bExp, expDiff; + bits64 aSig0, aSig1, bSig; + bits64 q, term0, term1, alternateASig0, alternateASig1; + floatx80 z; + + aSig0 = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + bSig = extractFloatx80Frac( b ); + bExp = extractFloatx80Exp( b ); + bSign = extractFloatx80Sign( b ); + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig0<<1 ) + || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) { + return propagateFloatx80NaN( a, b ); + } + goto invalid; + } + if ( bExp == 0x7FFF ) { + if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b ); + return a; + } + if ( bExp == 0 ) { + if ( bSig == 0 ) { + invalid: + float_raise( float_flag_invalid ); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + normalizeFloatx80Subnormal( bSig, &bExp, &bSig ); + } + if ( aExp == 0 ) { + if ( (bits64) ( aSig0<<1 ) == 0 ) return a; + normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 ); + } + bSig |= LIT64( 0x8000000000000000 ); + zSign = aSign; + expDiff = aExp - bExp; + aSig1 = 0; + if ( expDiff < 0 ) { + if ( expDiff < -1 ) return a; + shift128Right( aSig0, 0, 1, &aSig0, &aSig1 ); + expDiff = 0; + } + q = ( bSig <= aSig0 ); + if ( q ) aSig0 -= bSig; + expDiff -= 64; + while ( 0 < expDiff ) { + q = estimateDiv128To64( aSig0, aSig1, bSig ); + q = ( 2 < q ) ? q - 2 : 0; + mul64To128( bSig, q, &term0, &term1 ); + sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 ); + shortShift128Left( aSig0, aSig1, 62, &aSig0, &aSig1 ); + expDiff -= 62; + } + expDiff += 64; + if ( 0 < expDiff ) { + q = estimateDiv128To64( aSig0, aSig1, bSig ); + q = ( 2 < q ) ? q - 2 : 0; + q >>= 64 - expDiff; + mul64To128( bSig, q<<( 64 - expDiff ), &term0, &term1 ); + sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 ); + shortShift128Left( 0, bSig, 64 - expDiff, &term0, &term1 ); + while ( le128( term0, term1, aSig0, aSig1 ) ) { + ++q; + sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 ); + } + } + else { + term1 = 0; + term0 = bSig; + } + sub128( term0, term1, aSig0, aSig1, &alternateASig0, &alternateASig1 ); + if ( lt128( alternateASig0, alternateASig1, aSig0, aSig1 ) + || ( eq128( alternateASig0, alternateASig1, aSig0, aSig1 ) + && ( q & 1 ) ) + ) { + aSig0 = alternateASig0; + aSig1 = alternateASig1; + zSign = ! zSign; + } + return + normalizeRoundAndPackFloatx80( + 80, zSign, bExp + expDiff, aSig0, aSig1 ); + +} + +/* +------------------------------------------------------------------------------- +Returns the square root of the extended double-precision floating-point +value `a'. The operation is performed according to the IEC/IEEE Standard +for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +floatx80 floatx80_sqrt( floatx80 a ) +{ + flag aSign; + int32 aExp, zExp; + bits64 aSig0, aSig1, zSig0, zSig1; + bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; + bits64 shiftedRem0, shiftedRem1; + floatx80 z; + + aSig0 = extractFloatx80Frac( a ); + aExp = extractFloatx80Exp( a ); + aSign = extractFloatx80Sign( a ); + if ( aExp == 0x7FFF ) { + if ( (bits64) ( aSig0<<1 ) ) return propagateFloatx80NaN( a, a ); + if ( ! aSign ) return a; + goto invalid; + } + if ( aSign ) { + if ( ( aExp | aSig0 ) == 0 ) return a; + invalid: + float_raise( float_flag_invalid ); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + if ( aExp == 0 ) { + if ( aSig0 == 0 ) return packFloatx80( 0, 0, 0 ); + normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 ); + } + zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFF; + zSig0 = estimateSqrt32( aExp, aSig0>>32 ); + zSig0 <<= 31; + aSig1 = 0; + shift128Right( aSig0, 0, ( aExp & 1 ) + 2, &aSig0, &aSig1 ); + zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0 ) + zSig0 + 4; + if ( 0 <= (sbits64) zSig0 ) zSig0 = LIT64( 0xFFFFFFFFFFFFFFFF ); + shortShift128Left( aSig0, aSig1, 2, &aSig0, &aSig1 ); + mul64To128( zSig0, zSig0, &term0, &term1 ); + sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 ); + while ( (sbits64) rem0 < 0 ) { + --zSig0; + shortShift128Left( 0, zSig0, 1, &term0, &term1 ); + term1 |= 1; + add128( rem0, rem1, term0, term1, &rem0, &rem1 ); + } + shortShift128Left( rem0, rem1, 63, &shiftedRem0, &shiftedRem1 ); + zSig1 = estimateDiv128To64( shiftedRem0, shiftedRem1, zSig0 ); + if ( (bits64) ( zSig1<<1 ) <= 10 ) { + if ( zSig1 == 0 ) zSig1 = 1; + mul64To128( zSig0, zSig1, &term1, &term2 ); + shortShift128Left( term1, term2, 1, &term1, &term2 ); + sub128( rem1, 0, term1, term2, &rem1, &rem2 ); + mul64To128( zSig1, zSig1, &term2, &term3 ); + sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 ); + while ( (sbits64) rem1 < 0 ) { + --zSig1; + shortShift192Left( 0, zSig0, zSig1, 1, &term1, &term2, &term3 ); + term3 |= 1; + add192( + rem1, rem2, rem3, term1, term2, term3, &rem1, &rem2, &rem3 ); + } + zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 ); + } + return + roundAndPackFloatx80( + floatx80_rounding_precision, 0, zExp, zSig0, zSig1 ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the extended double-precision floating-point value `a' is +equal to the corresponding value `b', and 0 otherwise. The comparison is +performed according to the IEC/IEEE Standard for Binary Floating-point +Arithmetic. +------------------------------------------------------------------------------- +*/ +flag floatx80_eq( floatx80 a, floatx80 b ) +{ + + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + ) { + if ( floatx80_is_signaling_nan( a ) + || floatx80_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid ); + } + return 0; + } + return + ( a.low == b.low ) + && ( ( a.high == b.high ) + || ( ( a.low == 0 ) + && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) ) + ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the extended double-precision floating-point value `a' is +less than or equal to the corresponding value `b', and 0 otherwise. The +comparison is performed according to the IEC/IEEE Standard for Binary +Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag floatx80_le( floatx80 a, floatx80 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + ) { + float_raise( float_flag_invalid ); + return 0; + } + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign != bSign ) { + return + aSign + || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + == 0 ); + } + return + aSign ? le128( b.high, b.low, a.high, a.low ) + : le128( a.high, a.low, b.high, b.low ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the extended double-precision floating-point value `a' is +less than the corresponding value `b', and 0 otherwise. The comparison +is performed according to the IEC/IEEE Standard for Binary Floating-point +Arithmetic. +------------------------------------------------------------------------------- +*/ +flag floatx80_lt( floatx80 a, floatx80 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + ) { + float_raise( float_flag_invalid ); + return 0; + } + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign != bSign ) { + return + aSign + && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + != 0 ); + } + return + aSign ? lt128( b.high, b.low, a.high, a.low ) + : lt128( a.high, a.low, b.high, b.low ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the extended double-precision floating-point value `a' is equal +to the corresponding value `b', and 0 otherwise. The invalid exception is +raised if either operand is a NaN. Otherwise, the comparison is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag floatx80_eq_signaling( floatx80 a, floatx80 b ) +{ + + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + ) { + float_raise( float_flag_invalid ); + return 0; + } + return + ( a.low == b.low ) + && ( ( a.high == b.high ) + || ( ( a.low == 0 ) + && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) ) + ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the extended double-precision floating-point value `a' is less +than or equal to the corresponding value `b', and 0 otherwise. Quiet NaNs +do not cause an exception. Otherwise, the comparison is performed according +to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag floatx80_le_quiet( floatx80 a, floatx80 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + ) { + if ( floatx80_is_signaling_nan( a ) + || floatx80_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid ); + } + return 0; + } + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign != bSign ) { + return + aSign + || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + == 0 ); + } + return + aSign ? le128( b.high, b.low, a.high, a.low ) + : le128( a.high, a.low, b.high, b.low ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the extended double-precision floating-point value `a' is less +than the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause +an exception. Otherwise, the comparison is performed according to the +IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag floatx80_lt_quiet( floatx80 a, floatx80 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloatx80Exp( a ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( a )<<1 ) ) + || ( ( extractFloatx80Exp( b ) == 0x7FFF ) + && (bits64) ( extractFloatx80Frac( b )<<1 ) ) + ) { + if ( floatx80_is_signaling_nan( a ) + || floatx80_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid ); + } + return 0; + } + aSign = extractFloatx80Sign( a ); + bSign = extractFloatx80Sign( b ); + if ( aSign != bSign ) { + return + aSign + && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + != 0 ); + } + return + aSign ? lt128( b.high, b.low, a.high, a.low ) + : lt128( a.high, a.low, b.high, b.low ); + +} + +#endif + +#ifdef FLOAT128 + +/* +------------------------------------------------------------------------------- +Returns the result of converting the quadruple-precision floating-point +value `a' to the 32-bit two's complement integer format. The conversion +is performed according to the IEC/IEEE Standard for Binary Floating-point +Arithmetic---which means in particular that the conversion is rounded +according to the current rounding mode. If `a' is a NaN, the largest +positive integer is returned. Otherwise, if the conversion overflows, the +largest integer with the same sign as `a' is returned. +------------------------------------------------------------------------------- +*/ +int32 float128_to_int32( float128 a ) +{ + flag aSign; + int32 aExp, shiftCount; + bits64 aSig0, aSig1; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + if ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) aSign = 0; + if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 ); + aSig0 |= ( aSig1 != 0 ); + shiftCount = 0x4028 - aExp; + if ( 0 < shiftCount ) shift64RightJamming( aSig0, shiftCount, &aSig0 ); + return roundAndPackInt32( aSign, aSig0 ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of converting the quadruple-precision floating-point +value `a' to the 32-bit two's complement integer format. The conversion +is performed according to the IEC/IEEE Standard for Binary Floating-point +Arithmetic, except that the conversion is always rounded toward zero. If +`a' is a NaN, the largest positive integer is returned. Otherwise, if the +conversion overflows, the largest integer with the same sign as `a' is +returned. +------------------------------------------------------------------------------- +*/ +int32 float128_to_int32_round_to_zero( float128 a ) +{ + flag aSign; + int32 aExp, shiftCount; + bits64 aSig0, aSig1, savedASig; + int32 z; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + aSig0 |= ( aSig1 != 0 ); + shiftCount = 0x402F - aExp; + if ( shiftCount < 17 ) { + if ( ( aExp == 0x7FFF ) && aSig0 ) aSign = 0; + goto invalid; + } + else if ( 48 < shiftCount ) { + if ( aExp || aSig0 ) float_exception_flags |= float_flag_inexact; + return 0; + } + aSig0 |= LIT64( 0x0001000000000000 ); + savedASig = aSig0; + aSig0 >>= shiftCount; + z = aSig0; + if ( aSign ) z = - z; + if ( ( z < 0 ) ^ aSign ) { + invalid: + float_exception_flags |= float_flag_invalid; + return aSign ? 0x80000000 : 0x7FFFFFFF; + } + if ( ( aSig0<>1, &z.high, &z.low ); + if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask; + } + else { + if ( (sbits64) z.low < 0 ) { + ++z.high; + if ( (bits64) ( z.low<<1 ) == 0 ) z.high &= ~1; + } + } + } + else if ( roundingMode != float_round_to_zero ) { + if ( extractFloat128Sign( z ) + ^ ( roundingMode == float_round_up ) ) { + add128( z.high, z.low, 0, roundBitsMask, &z.high, &z.low ); + } + } + z.low &= ~ roundBitsMask; + } + else { + if ( aExp <= 0x3FFE ) { + if ( ( ( (bits64) ( a.high<<1 ) ) | a.low ) == 0 ) return a; + float_exception_flags |= float_flag_inexact; + aSign = extractFloat128Sign( a ); + switch ( float_rounding_mode ) { + case float_round_nearest_even: + if ( ( aExp == 0x3FFE ) + && ( extractFloat128Frac0( a ) + | extractFloat128Frac1( a ) ) + ) { + return packFloat128( aSign, 0x3FFF, 0, 0 ); + } + break; + case float_round_down: + return + aSign ? packFloat128( 1, 0x3FFF, 0, 0 ) + : packFloat128( 0, 0, 0, 0 ); + case float_round_up: + return + aSign ? packFloat128( 1, 0, 0, 0 ) + : packFloat128( 0, 0x3FFF, 0, 0 ); + } + return packFloat128( aSign, 0, 0, 0 ); + } + lastBitMask = 1; + lastBitMask <<= 0x402F - aExp; + roundBitsMask = lastBitMask - 1; + z.low = 0; + z.high = a.high; + roundingMode = float_rounding_mode; + if ( roundingMode == float_round_nearest_even ) { + z.high += lastBitMask>>1; + if ( ( ( z.high & roundBitsMask ) | a.low ) == 0 ) { + z.high &= ~ lastBitMask; + } + } + else if ( roundingMode != float_round_to_zero ) { + if ( extractFloat128Sign( z ) + ^ ( roundingMode == float_round_up ) ) { + z.high |= ( a.low != 0 ); + z.high += roundBitsMask; + } + } + z.high &= ~ roundBitsMask; + } + if ( ( z.low != a.low ) || ( z.high != a.high ) ) { + float_exception_flags |= float_flag_inexact; + } + return z; + +} + +/* +------------------------------------------------------------------------------- +Returns the result of adding the absolute values of the quadruple-precision +floating-point values `a' and `b'. If `zSign' is true, the sum is negated +before being returned. `zSign' is ignored if the result is a NaN. The +addition is performed according to the IEC/IEEE Standard for Binary +Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +static float128 addFloat128Sigs( float128 a, float128 b, flag zSign ) +{ + int32 aExp, bExp, zExp; + bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; + int32 expDiff; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + bSig1 = extractFloat128Frac1( b ); + bSig0 = extractFloat128Frac0( b ); + bExp = extractFloat128Exp( b ); + expDiff = aExp - bExp; + if ( 0 < expDiff ) { + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b ); + return a; + } + if ( bExp == 0 ) { + --expDiff; + } + else { + bSig0 |= LIT64( 0x0001000000000000 ); + } + shift128ExtraRightJamming( + bSig0, bSig1, 0, expDiff, &bSig0, &bSig1, &zSig2 ); + zExp = aExp; + } + else if ( expDiff < 0 ) { + if ( bExp == 0x7FFF ) { + if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b ); + return packFloat128( zSign, 0x7FFF, 0, 0 ); + } + if ( aExp == 0 ) { + ++expDiff; + } + else { + aSig0 |= LIT64( 0x0001000000000000 ); + } + shift128ExtraRightJamming( + aSig0, aSig1, 0, - expDiff, &aSig0, &aSig1, &zSig2 ); + zExp = bExp; + } + else { + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 | bSig0 | bSig1 ) { + return propagateFloat128NaN( a, b ); + } + return a; + } + add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); + if ( aExp == 0 ) return packFloat128( zSign, 0, zSig0, zSig1 ); + zSig2 = 0; + zSig0 |= LIT64( 0x0002000000000000 ); + zExp = aExp; + goto shiftRight1; + } + aSig0 |= LIT64( 0x0001000000000000 ); + add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); + --zExp; + if ( zSig0 < LIT64( 0x0002000000000000 ) ) goto roundAndPack; + ++zExp; + shiftRight1: + shift128ExtraRightJamming( + zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 ); + roundAndPack: + return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of subtracting the absolute values of the quadruple- +precision floating-point values `a' and `b'. If `zSign' is true, the +difference is negated before being returned. `zSign' is ignored if the +result is a NaN. The subtraction is performed according to the IEC/IEEE +Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +static float128 subFloat128Sigs( float128 a, float128 b, flag zSign ) +{ + int32 aExp, bExp, zExp; + bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1; + int32 expDiff; + float128 z; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + bSig1 = extractFloat128Frac1( b ); + bSig0 = extractFloat128Frac0( b ); + bExp = extractFloat128Exp( b ); + expDiff = aExp - bExp; + shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 ); + shortShift128Left( bSig0, bSig1, 14, &bSig0, &bSig1 ); + if ( 0 < expDiff ) goto aExpBigger; + if ( expDiff < 0 ) goto bExpBigger; + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 | bSig0 | bSig1 ) { + return propagateFloat128NaN( a, b ); + } + float_raise( float_flag_invalid ); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + if ( aExp == 0 ) { + aExp = 1; + bExp = 1; + } + if ( bSig0 < aSig0 ) goto aBigger; + if ( aSig0 < bSig0 ) goto bBigger; + if ( bSig1 < aSig1 ) goto aBigger; + if ( aSig1 < bSig1 ) goto bBigger; + return packFloat128( float_rounding_mode == float_round_down, 0, 0, 0 ); + bExpBigger: + if ( bExp == 0x7FFF ) { + if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b ); + return packFloat128( zSign ^ 1, 0x7FFF, 0, 0 ); + } + if ( aExp == 0 ) { + ++expDiff; + } + else { + aSig0 |= LIT64( 0x4000000000000000 ); + } + shift128RightJamming( aSig0, aSig1, - expDiff, &aSig0, &aSig1 ); + bSig0 |= LIT64( 0x4000000000000000 ); + bBigger: + sub128( bSig0, bSig1, aSig0, aSig1, &zSig0, &zSig1 ); + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; + aExpBigger: + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b ); + return a; + } + if ( bExp == 0 ) { + --expDiff; + } + else { + bSig0 |= LIT64( 0x4000000000000000 ); + } + shift128RightJamming( bSig0, bSig1, expDiff, &bSig0, &bSig1 ); + aSig0 |= LIT64( 0x4000000000000000 ); + aBigger: + sub128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 ); + zExp = aExp; + normalizeRoundAndPack: + --zExp; + return normalizeRoundAndPackFloat128( zSign, zExp - 14, zSig0, zSig1 ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of adding the quadruple-precision floating-point values +`a' and `b'. The operation is performed according to the IEC/IEEE Standard +for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float128 float128_add( float128 a, float128 b ) +{ + flag aSign, bSign; + + aSign = extractFloat128Sign( a ); + bSign = extractFloat128Sign( b ); + if ( aSign == bSign ) { + return addFloat128Sigs( a, b, aSign ); + } + else { + return subFloat128Sigs( a, b, aSign ); + } + +} + +/* +------------------------------------------------------------------------------- +Returns the result of subtracting the quadruple-precision floating-point +values `a' and `b'. The operation is performed according to the IEC/IEEE +Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float128 float128_sub( float128 a, float128 b ) +{ + flag aSign, bSign; + + aSign = extractFloat128Sign( a ); + bSign = extractFloat128Sign( b ); + if ( aSign == bSign ) { + return subFloat128Sigs( a, b, aSign ); + } + else { + return addFloat128Sigs( a, b, aSign ); + } + +} + +/* +------------------------------------------------------------------------------- +Returns the result of multiplying the quadruple-precision floating-point +values `a' and `b'. The operation is performed according to the IEC/IEEE +Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float128 float128_mul( float128 a, float128 b ) +{ + flag aSign, bSign, zSign; + int32 aExp, bExp, zExp; + bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3; + float128 z; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + bSig1 = extractFloat128Frac1( b ); + bSig0 = extractFloat128Frac0( b ); + bExp = extractFloat128Exp( b ); + bSign = extractFloat128Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0x7FFF ) { + if ( ( aSig0 | aSig1 ) + || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) { + return propagateFloat128NaN( a, b ); + } + if ( ( bExp | bSig0 | bSig1 ) == 0 ) goto invalid; + return packFloat128( zSign, 0x7FFF, 0, 0 ); + } + if ( bExp == 0x7FFF ) { + if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b ); + if ( ( aExp | aSig0 | aSig1 ) == 0 ) { + invalid: + float_raise( float_flag_invalid ); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + return packFloat128( zSign, 0x7FFF, 0, 0 ); + } + if ( aExp == 0 ) { + if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 ); + normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); + } + if ( bExp == 0 ) { + if ( ( bSig0 | bSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 ); + normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 ); + } + zExp = aExp + bExp - 0x4000; + aSig0 |= LIT64( 0x0001000000000000 ); + shortShift128Left( bSig0, bSig1, 16, &bSig0, &bSig1 ); + mul128To256( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1, &zSig2, &zSig3 ); + add128( zSig0, zSig1, aSig0, aSig1, &zSig0, &zSig1 ); + zSig2 |= ( zSig3 != 0 ); + if ( LIT64( 0x0002000000000000 ) <= zSig0 ) { + shift128ExtraRightJamming( + zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 ); + ++zExp; + } + return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 ); + +} + +/* +------------------------------------------------------------------------------- +Returns the result of dividing the quadruple-precision floating-point value +`a' by the corresponding value `b'. The operation is performed according to +the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float128 float128_div( float128 a, float128 b ) +{ + flag aSign, bSign, zSign; + int32 aExp, bExp, zExp; + bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; + bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; + float128 z; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + bSig1 = extractFloat128Frac1( b ); + bSig0 = extractFloat128Frac0( b ); + bExp = extractFloat128Exp( b ); + bSign = extractFloat128Sign( b ); + zSign = aSign ^ bSign; + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b ); + if ( bExp == 0x7FFF ) { + if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b ); + goto invalid; + } + return packFloat128( zSign, 0x7FFF, 0, 0 ); + } + if ( bExp == 0x7FFF ) { + if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b ); + return packFloat128( zSign, 0, 0, 0 ); + } + if ( bExp == 0 ) { + if ( ( bSig0 | bSig1 ) == 0 ) { + if ( ( aExp | aSig0 | aSig1 ) == 0 ) { + invalid: + float_raise( float_flag_invalid ); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + float_raise( float_flag_divbyzero ); + return packFloat128( zSign, 0x7FFF, 0, 0 ); + } + normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 ); + } + if ( aExp == 0 ) { + if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 ); + normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); + } + zExp = aExp - bExp + 0x3FFD; + shortShift128Left( + aSig0 | LIT64( 0x0001000000000000 ), aSig1, 15, &aSig0, &aSig1 ); + shortShift128Left( + bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 ); + if ( le128( bSig0, bSig1, aSig0, aSig1 ) ) { + shift128Right( aSig0, aSig1, 1, &aSig0, &aSig1 ); + ++zExp; + } + zSig0 = estimateDiv128To64( aSig0, aSig1, bSig0 ); + mul128By64To192( bSig0, bSig1, zSig0, &term0, &term1, &term2 ); + sub192( aSig0, aSig1, 0, term0, term1, term2, &rem0, &rem1, &rem2 ); + while ( (sbits64) rem0 < 0 ) { + --zSig0; + add192( rem0, rem1, rem2, 0, bSig0, bSig1, &rem0, &rem1, &rem2 ); + } + zSig1 = estimateDiv128To64( rem1, rem2, bSig0 ); + if ( ( zSig1 & 0x3FFF ) <= 4 ) { + mul128By64To192( bSig0, bSig1, zSig1, &term1, &term2, &term3 ); + sub192( rem1, rem2, 0, term1, term2, term3, &rem1, &rem2, &rem3 ); + while ( (sbits64) rem1 < 0 ) { + --zSig1; + add192( rem1, rem2, rem3, 0, bSig0, bSig1, &rem1, &rem2, &rem3 ); + } + zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 ); + } + shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 ); + return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 ); + +} + +/* +------------------------------------------------------------------------------- +Returns the remainder of the quadruple-precision floating-point value `a' +with respect to the corresponding value `b'. The operation is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float128 float128_rem( float128 a, float128 b ) +{ + flag aSign, bSign, zSign; + int32 aExp, bExp, expDiff; + bits64 aSig0, aSig1, bSig0, bSig1; + bits64 q, term0, term1, term2, allZero, alternateASig0, alternateASig1; + bits64 sigMean1; + sbits64 sigMean0; + float128 z; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + bSig1 = extractFloat128Frac1( b ); + bSig0 = extractFloat128Frac0( b ); + bExp = extractFloat128Exp( b ); + bSign = extractFloat128Sign( b ); + if ( aExp == 0x7FFF ) { + if ( ( aSig0 | aSig1 ) + || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) { + return propagateFloat128NaN( a, b ); + } + goto invalid; + } + if ( bExp == 0x7FFF ) { + if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b ); + return a; + } + if ( bExp == 0 ) { + if ( ( bSig0 | bSig1 ) == 0 ) { + invalid: + float_raise( float_flag_invalid ); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 ); + } + if ( aExp == 0 ) { + if ( ( aSig0 | aSig1 ) == 0 ) return a; + normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); + } + expDiff = aExp - bExp; + if ( expDiff < -1 ) return a; + shortShift128Left( + aSig0 | LIT64( 0x0001000000000000 ), + aSig1, + 15 - ( expDiff < 0 ), + &aSig0, + &aSig1 + ); + shortShift128Left( + bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 ); + q = le128( bSig0, bSig1, aSig0, aSig1 ); + if ( q ) sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 ); + expDiff -= 64; + while ( 0 < expDiff ) { + q = estimateDiv128To64( aSig0, aSig1, bSig0 ); + q = ( 4 < q ) ? q - 4 : 0; + mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 ); + shortShift192Left( term0, term1, term2, 61, &term1, &term2, &allZero ); + shortShift128Left( aSig0, aSig1, 61, &aSig0, &allZero ); + sub128( aSig0, 0, term1, term2, &aSig0, &aSig1 ); + expDiff -= 61; + } + if ( -64 < expDiff ) { + q = estimateDiv128To64( aSig0, aSig1, bSig0 ); + q = ( 4 < q ) ? q - 4 : 0; + q >>= - expDiff; + shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 ); + expDiff += 52; + if ( expDiff < 0 ) { + shift128Right( aSig0, aSig1, - expDiff, &aSig0, &aSig1 ); + } + else { + shortShift128Left( aSig0, aSig1, expDiff, &aSig0, &aSig1 ); + } + mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 ); + sub128( aSig0, aSig1, term1, term2, &aSig0, &aSig1 ); + } + else { + shift128Right( aSig0, aSig1, 12, &aSig0, &aSig1 ); + shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 ); + } + do { + alternateASig0 = aSig0; + alternateASig1 = aSig1; + ++q; + sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 ); + } while ( 0 <= (sbits64) aSig0 ); + add128( + aSig0, aSig1, alternateASig0, alternateASig1, &sigMean0, &sigMean1 ); + if ( ( sigMean0 < 0 ) + || ( ( ( sigMean0 | sigMean1 ) == 0 ) && ( q & 1 ) ) ) { + aSig0 = alternateASig0; + aSig1 = alternateASig1; + } + zSign = ( (sbits64) aSig0 < 0 ); + if ( zSign ) sub128( 0, 0, aSig0, aSig1, &aSig0, &aSig1 ); + return + normalizeRoundAndPackFloat128( aSign ^ zSign, bExp - 4, aSig0, aSig1 ); + +} + +/* +------------------------------------------------------------------------------- +Returns the square root of the quadruple-precision floating-point value `a'. +The operation is performed according to the IEC/IEEE Standard for Binary +Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +float128 float128_sqrt( float128 a ) +{ + flag aSign; + int32 aExp, zExp; + bits64 aSig0, aSig1, zSig0, zSig1, zSig2; + bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3; + bits64 shiftedRem0, shiftedRem1; + float128 z; + + aSig1 = extractFloat128Frac1( a ); + aSig0 = extractFloat128Frac0( a ); + aExp = extractFloat128Exp( a ); + aSign = extractFloat128Sign( a ); + if ( aExp == 0x7FFF ) { + if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, a ); + if ( ! aSign ) return a; + goto invalid; + } + if ( aSign ) { + if ( ( aExp | aSig0 | aSig1 ) == 0 ) return a; + invalid: + float_raise( float_flag_invalid ); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + if ( aExp == 0 ) { + if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( 0, 0, 0, 0 ); + normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 ); + } + zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFE; + aSig0 |= LIT64( 0x0001000000000000 ); + zSig0 = estimateSqrt32( aExp, aSig0>>17 ); + zSig0 <<= 31; + shortShift128Left( aSig0, aSig1, 13 - ( aExp & 1 ), &aSig0, &aSig1 ); + zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0 ) + zSig0 + 4; + if ( 0 <= (sbits64) zSig0 ) zSig0 = LIT64( 0xFFFFFFFFFFFFFFFF ); + shortShift128Left( aSig0, aSig1, 2, &aSig0, &aSig1 ); + mul64To128( zSig0, zSig0, &term0, &term1 ); + sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 ); + while ( (sbits64) rem0 < 0 ) { + --zSig0; + shortShift128Left( 0, zSig0, 1, &term0, &term1 ); + term1 |= 1; + add128( rem0, rem1, term0, term1, &rem0, &rem1 ); + } + shortShift128Left( rem0, rem1, 63, &shiftedRem0, &shiftedRem1 ); + zSig1 = estimateDiv128To64( shiftedRem0, shiftedRem1, zSig0 ); + if ( ( zSig1 & 0x3FFF ) <= 5 ) { + if ( zSig1 == 0 ) zSig1 = 1; + mul64To128( zSig0, zSig1, &term1, &term2 ); + shortShift128Left( term1, term2, 1, &term1, &term2 ); + sub128( rem1, 0, term1, term2, &rem1, &rem2 ); + mul64To128( zSig1, zSig1, &term2, &term3 ); + sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 ); + while ( (sbits64) rem1 < 0 ) { + --zSig1; + shortShift192Left( 0, zSig0, zSig1, 1, &term1, &term2, &term3 ); + term3 |= 1; + add192( + rem1, rem2, rem3, term1, term2, term3, &rem1, &rem2, &rem3 ); + } + zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 ); + } + shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 ); + return roundAndPackFloat128( 0, zExp, zSig0, zSig1, zSig2 ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the quadruple-precision floating-point value `a' is equal to +the corresponding value `b', and 0 otherwise. The comparison is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float128_eq( float128 a, float128 b ) +{ + + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + if ( float128_is_signaling_nan( a ) + || float128_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid ); + } + return 0; + } + return + ( a.low == b.low ) + && ( ( a.high == b.high ) + || ( ( a.low == 0 ) + && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) ) + ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the quadruple-precision floating-point value `a' is less than +or equal to the corresponding value `b', and 0 otherwise. The comparison +is performed according to the IEC/IEEE Standard for Binary Floating-point +Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float128_le( float128 a, float128 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + float_raise( float_flag_invalid ); + return 0; + } + aSign = extractFloat128Sign( a ); + bSign = extractFloat128Sign( b ); + if ( aSign != bSign ) { + return + aSign + || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + == 0 ); + } + return + aSign ? le128( b.high, b.low, a.high, a.low ) + : le128( a.high, a.low, b.high, b.low ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the quadruple-precision floating-point value `a' is less than +the corresponding value `b', and 0 otherwise. The comparison is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float128_lt( float128 a, float128 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + float_raise( float_flag_invalid ); + return 0; + } + aSign = extractFloat128Sign( a ); + bSign = extractFloat128Sign( b ); + if ( aSign != bSign ) { + return + aSign + && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + != 0 ); + } + return + aSign ? lt128( b.high, b.low, a.high, a.low ) + : lt128( a.high, a.low, b.high, b.low ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the quadruple-precision floating-point value `a' is equal to +the corresponding value `b', and 0 otherwise. The invalid exception is +raised if either operand is a NaN. Otherwise, the comparison is performed +according to the IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float128_eq_signaling( float128 a, float128 b ) +{ + + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + float_raise( float_flag_invalid ); + return 0; + } + return + ( a.low == b.low ) + && ( ( a.high == b.high ) + || ( ( a.low == 0 ) + && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) ) + ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the quadruple-precision floating-point value `a' is less than +or equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not +cause an exception. Otherwise, the comparison is performed according to the +IEC/IEEE Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float128_le_quiet( float128 a, float128 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + if ( float128_is_signaling_nan( a ) + || float128_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid ); + } + return 0; + } + aSign = extractFloat128Sign( a ); + bSign = extractFloat128Sign( b ); + if ( aSign != bSign ) { + return + aSign + || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + == 0 ); + } + return + aSign ? le128( b.high, b.low, a.high, a.low ) + : le128( a.high, a.low, b.high, b.low ); + +} + +/* +------------------------------------------------------------------------------- +Returns 1 if the quadruple-precision floating-point value `a' is less than +the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +exception. Otherwise, the comparison is performed according to the IEC/IEEE +Standard for Binary Floating-point Arithmetic. +------------------------------------------------------------------------------- +*/ +flag float128_lt_quiet( float128 a, float128 b ) +{ + flag aSign, bSign; + + if ( ( ( extractFloat128Exp( a ) == 0x7FFF ) + && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) + || ( ( extractFloat128Exp( b ) == 0x7FFF ) + && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) ) + ) { + if ( float128_is_signaling_nan( a ) + || float128_is_signaling_nan( b ) ) { + float_raise( float_flag_invalid ); + } + return 0; + } + aSign = extractFloat128Sign( a ); + bSign = extractFloat128Sign( b ); + if ( aSign != bSign ) { + return + aSign + && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low ) + != 0 ); + } + return + aSign ? lt128( b.high, b.low, a.high, a.low ) + : lt128( a.high, a.low, b.high, b.low ); + +} + +#endif + diff -u --recursive --new-file v2.3.6/linux/arch/arm/nwfpe/softfloat.h linux/arch/arm/nwfpe/softfloat.h --- v2.3.6/linux/arch/arm/nwfpe/softfloat.h Wed Dec 31 16:00:00 1969 +++ linux/arch/arm/nwfpe/softfloat.h Thu Jun 17 01:11:35 1999 @@ -0,0 +1,290 @@ + +/* +=============================================================================== + +This C header file is part of the SoftFloat IEC/IEEE Floating-point +Arithmetic Package, Release 2. + +Written by John R. Hauser. This work was made possible in part by the +International Computer Science Institute, located at Suite 600, 1947 Center +Street, Berkeley, California 94704. Funding was partially provided by the +National Science Foundation under grant MIP-9311980. The original version +of this code was written as part of a project to build a fixed-point vector +processor in collaboration with the University of California at Berkeley, +overseen by Profs. Nelson Morgan and John Wawrzynek. More information +is available through the Web page `http://HTTP.CS.Berkeley.EDU/~jhauser/ +arithmetic/softfloat.html'. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort +has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT +TIMES RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO +PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY +AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE. + +Derivative works are acceptable, even for commercial purposes, so long as +(1) they include prominent notice that the work is derivative, and (2) they +include prominent notice akin to these three paragraphs for those parts of +this code that are retained. + +=============================================================================== +*/ + +#ifndef __SOFTFLOAT_H__ +#define __SOFTFLOAT_H__ + +/* +------------------------------------------------------------------------------- +The macro `FLOATX80' must be defined to enable the extended double-precision +floating-point format `floatx80'. If this macro is not defined, the +`floatx80' type will not be defined, and none of the functions that either +input or output the `floatx80' type will be defined. The same applies to +the `FLOAT128' macro and the quadruple-precision format `float128'. +------------------------------------------------------------------------------- +*/ +#define FLOATX80 +/* #define FLOAT128 */ + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE floating-point types. +------------------------------------------------------------------------------- +*/ +typedef unsigned long int float32; +typedef unsigned long long float64; +#ifdef FLOATX80 +typedef struct { + unsigned short high; + unsigned long long low; +} floatx80; +#endif +#ifdef FLOAT128 +typedef struct { + unsigned long long high, low; +} float128; +#endif + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE floating-point underflow tininess-detection mode. +------------------------------------------------------------------------------- +*/ +extern signed char float_detect_tininess; +enum { + float_tininess_after_rounding = 0, + float_tininess_before_rounding = 1 +}; + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE floating-point rounding mode. +------------------------------------------------------------------------------- +*/ +extern signed char float_rounding_mode; +enum { + float_round_nearest_even = 0, + float_round_to_zero = 1, + float_round_down = 2, + float_round_up = 3 +}; + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE floating-point exception flags. +------------------------------------------------------------------------------- +extern signed char float_exception_flags; +enum { + float_flag_inexact = 1, + float_flag_underflow = 2, + float_flag_overflow = 4, + float_flag_divbyzero = 8, + float_flag_invalid = 16 +}; + +ScottB: November 4, 1998 +Changed the enumeration to match the bit order in the FPA11. +*/ + +extern signed char float_exception_flags; +enum { + float_flag_invalid = 1, + float_flag_divbyzero = 2, + float_flag_overflow = 4, + float_flag_underflow = 8, + float_flag_inexact = 16 +}; + +/* +------------------------------------------------------------------------------- +Routine to raise any or all of the software IEC/IEEE floating-point +exception flags. +------------------------------------------------------------------------------- +*/ +void float_raise( signed char ); + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE integer-to-floating-point conversion routines. +------------------------------------------------------------------------------- +*/ +float32 int32_to_float32( signed int ); +float64 int32_to_float64( signed int ); +#ifdef FLOATX80 +floatx80 int32_to_floatx80( signed int ); +#endif +#ifdef FLOAT128 +float128 int32_to_float128( signed int ); +#endif + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE single-precision conversion routines. +------------------------------------------------------------------------------- +*/ +signed int float32_to_int32( float32 ); +signed int float32_to_int32_round_to_zero( float32 ); +float64 float32_to_float64( float32 ); +#ifdef FLOATX80 +floatx80 float32_to_floatx80( float32 ); +#endif +#ifdef FLOAT128 +float128 float32_to_float128( float32 ); +#endif + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE single-precision operations. +------------------------------------------------------------------------------- +*/ +float32 float32_round_to_int( float32 ); +float32 float32_add( float32, float32 ); +float32 float32_sub( float32, float32 ); +float32 float32_mul( float32, float32 ); +float32 float32_div( float32, float32 ); +float32 float32_rem( float32, float32 ); +float32 float32_sqrt( float32 ); +char float32_eq( float32, float32 ); +char float32_le( float32, float32 ); +char float32_lt( float32, float32 ); +char float32_eq_signaling( float32, float32 ); +char float32_le_quiet( float32, float32 ); +char float32_lt_quiet( float32, float32 ); +char float32_is_signaling_nan( float32 ); + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE double-precision conversion routines. +------------------------------------------------------------------------------- +*/ +signed int float64_to_int32( float64 ); +signed int float64_to_int32_round_to_zero( float64 ); +float32 float64_to_float32( float64 ); +#ifdef FLOATX80 +floatx80 float64_to_floatx80( float64 ); +#endif +#ifdef FLOAT128 +float128 float64_to_float128( float64 ); +#endif + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE double-precision operations. +------------------------------------------------------------------------------- +*/ +float64 float64_round_to_int( float64 ); +float64 float64_add( float64, float64 ); +float64 float64_sub( float64, float64 ); +float64 float64_mul( float64, float64 ); +float64 float64_div( float64, float64 ); +float64 float64_rem( float64, float64 ); +float64 float64_sqrt( float64 ); +char float64_eq( float64, float64 ); +char float64_le( float64, float64 ); +char float64_lt( float64, float64 ); +char float64_eq_signaling( float64, float64 ); +char float64_le_quiet( float64, float64 ); +char float64_lt_quiet( float64, float64 ); +char float64_is_signaling_nan( float64 ); + +#ifdef FLOATX80 + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE extended double-precision conversion routines. +------------------------------------------------------------------------------- +*/ +signed int floatx80_to_int32( floatx80 ); +signed int floatx80_to_int32_round_to_zero( floatx80 ); +float32 floatx80_to_float32( floatx80 ); +float64 floatx80_to_float64( floatx80 ); +#ifdef FLOAT128 +float128 floatx80_to_float128( floatx80 ); +#endif + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE extended double-precision rounding precision. Valid +values are 32, 64, and 80. +------------------------------------------------------------------------------- +*/ +extern signed char floatx80_rounding_precision; + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE extended double-precision operations. +------------------------------------------------------------------------------- +*/ +floatx80 floatx80_round_to_int( floatx80 ); +floatx80 floatx80_add( floatx80, floatx80 ); +floatx80 floatx80_sub( floatx80, floatx80 ); +floatx80 floatx80_mul( floatx80, floatx80 ); +floatx80 floatx80_div( floatx80, floatx80 ); +floatx80 floatx80_rem( floatx80, floatx80 ); +floatx80 floatx80_sqrt( floatx80 ); +char floatx80_eq( floatx80, floatx80 ); +char floatx80_le( floatx80, floatx80 ); +char floatx80_lt( floatx80, floatx80 ); +char floatx80_eq_signaling( floatx80, floatx80 ); +char floatx80_le_quiet( floatx80, floatx80 ); +char floatx80_lt_quiet( floatx80, floatx80 ); +char floatx80_is_signaling_nan( floatx80 ); + +#endif + +#ifdef FLOAT128 + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE quadruple-precision conversion routines. +------------------------------------------------------------------------------- +*/ +signed int float128_to_int32( float128 ); +signed int float128_to_int32_round_to_zero( float128 ); +float32 float128_to_float32( float128 ); +float64 float128_to_float64( float128 ); +#ifdef FLOATX80 +floatx80 float128_to_floatx80( float128 ); +#endif + +/* +------------------------------------------------------------------------------- +Software IEC/IEEE quadruple-precision operations. +------------------------------------------------------------------------------- +*/ +float128 float128_round_to_int( float128 ); +float128 float128_add( float128, float128 ); +float128 float128_sub( float128, float128 ); +float128 float128_mul( float128, float128 ); +float128 float128_div( float128, float128 ); +float128 float128_rem( float128, float128 ); +float128 float128_sqrt( float128 ); +char float128_eq( float128, float128 ); +char float128_le( float128, float128 ); +char float128_lt( float128, float128 ); +char float128_eq_signaling( float128, float128 ); +char float128_le_quiet( float128, float128 ); +char float128_lt_quiet( float128, float128 ); +char float128_is_signaling_nan( float128 ); + +#endif + +#endif diff -u --recursive --new-file v2.3.6/linux/arch/arm/vmlinux-armv.lds linux/arch/arm/vmlinux-armv.lds --- v2.3.6/linux/arch/arm/vmlinux-armv.lds Sun Sep 6 10:44:47 1998 +++ linux/arch/arm/vmlinux-armv.lds Thu Jun 17 01:11:35 1999 @@ -7,50 +7,64 @@ ENTRY(_start) SECTIONS { - _text = .; /* Text and read-only data */ - .text : { + _text = .; /* Text and read-only data */ + .text : { } /* Set text start address */ + + __init_begin = .; /* Init code and data */ + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __ebsa285_begin = .; + .text.ebsa285 : { *(.text.ebsa285) } + .data.ebsa285 : { *(.data.ebsa285) } + . = ALIGN(4096); + __ebsa285_end = .; + + __netwinder_begin = .; + .text.netwinder : { *(.text.netwinder) } + .data.netwinder : { *(.data.netwinder) } + . = ALIGN(4096); + __netwinder_end = .; + + .text.real : { /* Real text segment */ *(.text) *(.fixup) *(.gnu.warning) - } = 0x9090 + } + .text.lock : { *(.text.lock) } /* out-of-line lock text */ .rodata : { *(.rodata) } .kstrtab : { *(.kstrtab) } - . = ALIGN(16); /* Exception table */ + . = ALIGN(16); /* Exception table */ __start___ex_table = .; __ex_table : { *(__ex_table) } __stop___ex_table = .; - __start___ksymtab = .; /* Kernel symbol table */ + __start___ksymtab = .; /* Kernel symbol table */ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; - _etext = .; /* End of text section */ + _etext = .; /* End of text section */ . = ALIGN(8192); - .data : { /* Data */ + .data : { /* Data */ *(.init.task) *(.data) CONSTRUCTORS } - _edata = .; /* End of data section */ - - . = ALIGN(4096); /* Init code and data */ - __init_begin = .; - .text.init : { *(.text.init) } - .data.init : { *(.data.init) } - . = ALIGN(4096); - __init_end = .; + _edata = .; /* End of data section */ - __bss_start = .; /* BSS */ + __bss_start = .; /* BSS */ .bss : { *(.bss) } _end = . ; - /* Stabs debugging sections. */ + /* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } diff -u --recursive --new-file v2.3.6/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v2.3.6/linux/arch/i386/defconfig Mon Jun 7 22:12:01 1999 +++ linux/arch/i386/defconfig Thu Jun 17 01:27:19 1999 @@ -95,10 +95,9 @@ # CONFIG_BLK_DEV_CMD640_ENHANCED is not set CONFIG_BLK_DEV_RZ1000=y CONFIG_BLK_DEV_IDEPCI=y -CONFIG_BLK_DEV_IDEDMA=y +# CONFIG_BLK_DEV_IDEDMA_PCI is not set # CONFIG_BLK_DEV_OFFBOARD is not set # CONFIG_BLK_DEV_AEC6210 is not set -CONFIG_IDEDMA_AUTO=y # CONFIG_IDE_CHIPSETS is not set # @@ -355,7 +354,6 @@ # # CONFIG_CODA_FS is not set CONFIG_NFS_FS=y -# CONFIG_NFSD_SUN is not set CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set diff -u --recursive --new-file v2.3.6/linux/arch/i386/kernel/mca.c linux/arch/i386/kernel/mca.c --- v2.3.6/linux/arch/i386/kernel/mca.c Mon Jun 7 16:17:59 1999 +++ linux/arch/i386/kernel/mca.c Sat Jun 19 11:45:28 1999 @@ -148,7 +148,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; #endif diff -u --recursive --new-file v2.3.6/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- v2.3.6/linux/arch/i386/kernel/smp.c Wed Jun 2 11:29:13 1999 +++ linux/arch/i386/kernel/smp.c Wed Jun 16 19:26:27 1999 @@ -1164,6 +1164,7 @@ } unsigned int prof_multiplier[NR_CPUS]; +unsigned int prof_old_multiplier[NR_CPUS]; unsigned int prof_counter[NR_CPUS]; /* @@ -1187,6 +1188,7 @@ for (i = 0; i < NR_CPUS; i++) { cpu_number_map[i] = -1; prof_counter[i] = 1; + prof_old_multiplier[i] = 1; prof_multiplier[i] = 1; } @@ -1733,6 +1735,10 @@ return 0; } +static unsigned int calibration_result; + +void setup_APIC_timer(unsigned int clocks); + /* * Local timer interrupt handler. It does both profiling and * process statistics/rescheduling. @@ -1745,6 +1751,7 @@ void smp_local_timer_interrupt(struct pt_regs * regs) { + int user = (user_mode(regs) != 0); int cpu = smp_processor_id(); /* @@ -1753,25 +1760,34 @@ * updated with atomic operations). This is especially * useful with a profiling multiplier != 1 */ - if (!user_mode(regs)) + if (!user) x86_do_profile(regs->eip); if (!--prof_counter[cpu]) { - int user=0,system=0; + int system = 1 - user; struct task_struct * p = current; /* + * The multiplier may have changed since the last time we got + * to this point as a result of the user writing to + * /proc/profile. In this case we need to adjust the APIC + * timer accordingly. + * + * Interrupts are already masked off at this point. + */ + prof_counter[cpu] = prof_multiplier[cpu]; + if (prof_counter[cpu] != prof_old_multiplier[cpu]) { + setup_APIC_timer(calibration_result/prof_counter[cpu]); + prof_old_multiplier[cpu] = prof_counter[cpu]; + } + + /* * After doing the above, we need to make like * a normal interrupt - otherwise timer interrupts * ignore the global interrupt lock, which is the * WrongThing (tm) to do. */ - if (user_mode(regs)) - user=1; - else - system=1; - irq_enter(cpu, 0); update_one_process(p, 1, user, system, cpu); if (p->pid) { @@ -1791,7 +1807,6 @@ kstat.per_cpu_system[cpu] += system; } - prof_counter[cpu]=prof_multiplier[cpu]; irq_exit(cpu, 0); } @@ -2064,8 +2079,6 @@ return calibration_result; } -static unsigned int calibration_result; - void __init setup_APIC_clock(void) { unsigned long flags; @@ -2117,13 +2130,10 @@ /* * the frequency of the profiling timer can be changed * by writing a multiplier value into /proc/profile. - * - * usually you want to run this on all CPUs ;) */ int setup_profiling_timer(unsigned int multiplier) { - int cpu = smp_processor_id(); - unsigned long flags; + int i; /* * Sanity check. [at least 500 APIC cycles should be @@ -2133,11 +2143,14 @@ if ( (!multiplier) || (calibration_result/multiplier < 500)) return -EINVAL; - save_flags(flags); - cli(); - setup_APIC_timer(calibration_result/multiplier); - prof_multiplier[cpu]=multiplier; - restore_flags(flags); + /* + * Set the new multiplier for each CPU. CPUs don't start using the + * new values until the next timer interrupt in which they do process + * accounting. At that time they also adjust their APIC timers + * accordingly. + */ + for (i = 0; i < NR_CPUS; ++i) + prof_multiplier[i] = multiplier; return 0; } diff -u --recursive --new-file v2.3.6/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c --- v2.3.6/linux/arch/i386/mm/init.c Tue Jun 8 10:49:48 1999 +++ linux/arch/i386/mm/init.c Wed Jun 16 19:26:27 1999 @@ -159,10 +159,10 @@ reserved++; else if (PageSwapCache(mem_map+i)) cached++; - else if (!atomic_read(&mem_map[i].count)) + else if (!page_count(mem_map+i)) free++; else - shared += atomic_read(&mem_map[i].count) - 1; + shared += page_count(mem_map+i) - 1; } printk("%d pages of RAM\n",total); printk("%d reserved pages\n",reserved); @@ -449,7 +449,7 @@ reservedpages++; continue; } - atomic_set(&mem_map[MAP_NR(tmp)].count, 1); + set_page_count(mem_map+MAP_NR(tmp), 1); #ifdef CONFIG_BLK_DEV_INITRD if (!initrd_start || (tmp < initrd_start || tmp >= initrd_end)) @@ -475,7 +475,7 @@ addr = (unsigned long)(&__init_begin); for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); - atomic_set(&mem_map[MAP_NR(addr)].count, 1); + set_page_count(mem_map+MAP_NR(addr), 1); free_page(addr); } printk ("Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10); @@ -494,9 +494,9 @@ if (PageReserved(mem_map+i)) continue; val->totalram++; - if (!atomic_read(&mem_map[i].count)) + if (!page_count(mem_map+i)) continue; - val->sharedram += atomic_read(&mem_map[i].count) - 1; + val->sharedram += page_count(mem_map+i) - 1; } val->totalram <<= PAGE_SHIFT; val->sharedram <<= PAGE_SHIFT; diff -u --recursive --new-file v2.3.6/linux/arch/sparc/defconfig linux/arch/sparc/defconfig --- v2.3.6/linux/arch/sparc/defconfig Mon May 31 22:08:10 1999 +++ linux/arch/sparc/defconfig Thu Jun 17 01:08:50 1999 @@ -259,7 +259,6 @@ CONFIG_SUNRPC=y CONFIG_LOCKD=y CONFIG_SMB_FS=m -CONFIG_SMB_WIN95=y CONFIG_NCP_FS=m # CONFIG_NCPFS_PACKET_SIGNING is not set # CONFIG_NCPFS_IOCTL_LOCKING is not set diff -u --recursive --new-file v2.3.6/linux/arch/sparc/kernel/signal.c linux/arch/sparc/kernel/signal.c --- v2.3.6/linux/arch/sparc/kernel/signal.c Wed Mar 10 16:53:36 1999 +++ linux/arch/sparc/kernel/signal.c Thu Jun 17 01:08:50 1999 @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.91 1999/01/26 11:00:44 jj Exp $ +/* $Id: signal.c,v 1.92 1999/06/14 05:23:53 davem Exp $ * linux/arch/sparc/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -1194,6 +1194,7 @@ default: lock_kernel(); sigaddset(¤t->signal, signr); + recalc_sigpending(current); current->flags |= PF_SIGNALED; do_exit(exit_code); /* NOT REACHED */ diff -u --recursive --new-file v2.3.6/linux/arch/sparc/kernel/sys_sunos.c linux/arch/sparc/kernel/sys_sunos.c --- v2.3.6/linux/arch/sparc/kernel/sys_sunos.c Wed Jun 9 14:44:25 1999 +++ linux/arch/sparc/kernel/sys_sunos.c Thu Jun 17 01:08:50 1999 @@ -1,4 +1,4 @@ -/* $Id: sys_sunos.c,v 1.98 1999/06/09 08:23:39 davem Exp $ +/* $Id: sys_sunos.c,v 1.99 1999/06/11 11:40:39 davem Exp $ * sys_sunos.c: SunOS specific syscall compatibility support. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -197,7 +197,7 @@ * fool it, but this should catch most mistakes. */ freepages = buffermem >> PAGE_SHIFT; - freepages += page_cache_size; + freepages += atomic_read(&page_cache_size); freepages >>= 1; freepages += nr_free_pages; freepages += nr_swap_pages; @@ -209,7 +209,7 @@ * Ok, we have probably got enough memory - let it rip. */ current->mm->brk = brk; - do_brk(oldbrk, newbrk-oldbrk) + do_brk(oldbrk, newbrk-oldbrk); retval = 0; out: up(¤t->mm->mmap_sem); diff -u --recursive --new-file v2.3.6/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig --- v2.3.6/linux/arch/sparc64/defconfig Wed Jun 9 14:44:25 1999 +++ linux/arch/sparc64/defconfig Thu Jun 17 01:08:50 1999 @@ -299,7 +299,6 @@ CONFIG_SUNRPC=y CONFIG_LOCKD=y CONFIG_SMB_FS=m -CONFIG_SMB_WIN95=y CONFIG_NCP_FS=m # CONFIG_NCPFS_PACKET_SIGNING is not set # CONFIG_NCPFS_IOCTL_LOCKING is not set diff -u --recursive --new-file v2.3.6/linux/arch/sparc64/kernel/ptrace.c linux/arch/sparc64/kernel/ptrace.c --- v2.3.6/linux/arch/sparc64/kernel/ptrace.c Mon Jun 7 11:15:33 1999 +++ linux/arch/sparc64/kernel/ptrace.c Thu Jun 17 01:08:50 1999 @@ -34,7 +34,7 @@ * and that it is in the task area before calling this: this routine does * no checking. */ -static pte_t *get_page(struct task_struct * tsk, +static pte_t *ptrace_get_page(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long addr, int write) { pgd_t * pgdir; @@ -121,7 +121,7 @@ pte_t * pgtable; unsigned long page, retval; - if (!(pgtable = get_page (tsk, vma, addr, 0))) return 0; + if (!(pgtable = ptrace_get_page (tsk, vma, addr, 0))) return 0; page = pte_page(*pgtable); /* this is a hack for non-kernel-mapped video buffers and similar */ if (MAP_NR(page) >= max_mapnr) @@ -138,7 +138,7 @@ pte_t *pgtable; unsigned long page; - if (!(pgtable = get_page (tsk, vma, addr, 1))) return; + if (!(pgtable = ptrace_get_page (tsk, vma, addr, 1))) return; page = pte_page(*pgtable); /* this is a hack for non-kernel-mapped video buffers and similar */ flush_cache_page(vma, addr); @@ -166,7 +166,7 @@ unsigned long page; unsigned int retval; - if (!(pgtable = get_page (tsk, vma, addr, 0))) return 0; + if (!(pgtable = ptrace_get_page (tsk, vma, addr, 0))) return 0; page = pte_page(*pgtable); /* this is a hack for non-kernel-mapped video buffers and similar */ if (MAP_NR(page) >= max_mapnr) @@ -183,7 +183,7 @@ pte_t *pgtable; unsigned long page; - if (!(pgtable = get_page (tsk, vma, addr, 1))) return; + if (!(pgtable = ptrace_get_page (tsk, vma, addr, 1))) return; page = pte_page(*pgtable); /* this is a hack for non-kernel-mapped video buffers and similar */ flush_cache_page(vma, addr); @@ -941,7 +941,7 @@ pt_error_return(regs, EIO); goto flush_and_out; } - pgtable = get_page (child, vma, src, 0); + pgtable = ptrace_get_page (child, vma, src, 0); up(&child->mm->mmap_sem); if (src & ~PAGE_MASK) { curlen = PAGE_SIZE - (src & ~PAGE_MASK); @@ -988,7 +988,7 @@ pt_error_return(regs, EIO); goto flush_and_out; } - pgtable = get_page (child, vma, dest, 1); + pgtable = ptrace_get_page (child, vma, dest, 1); up(&child->mm->mmap_sem); if (dest & ~PAGE_MASK) { curlen = PAGE_SIZE - (dest & ~PAGE_MASK); diff -u --recursive --new-file v2.3.6/linux/arch/sparc64/kernel/signal.c linux/arch/sparc64/kernel/signal.c --- v2.3.6/linux/arch/sparc64/kernel/signal.c Wed Jun 9 14:44:25 1999 +++ linux/arch/sparc64/kernel/signal.c Thu Jun 17 01:08:50 1999 @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.40 1999/06/02 19:19:52 jj Exp $ +/* $Id: signal.c,v 1.41 1999/06/14 05:23:58 davem Exp $ * arch/sparc64/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -898,6 +898,7 @@ default: lock_kernel(); sigaddset(¤t->signal, signr); + recalc_sigpending(current); current->flags |= PF_SIGNALED; do_exit(exit_code); /* NOT REACHED */ diff -u --recursive --new-file v2.3.6/linux/arch/sparc64/kernel/signal32.c linux/arch/sparc64/kernel/signal32.c --- v2.3.6/linux/arch/sparc64/kernel/signal32.c Tue Oct 27 09:52:20 1998 +++ linux/arch/sparc64/kernel/signal32.c Thu Jun 17 01:08:50 1999 @@ -1,4 +1,4 @@ -/* $Id: signal32.c,v 1.47 1998/10/13 09:07:40 davem Exp $ +/* $Id: signal32.c,v 1.48 1999/06/14 05:24:01 davem Exp $ * arch/sparc64/kernel/signal32.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -1336,6 +1336,7 @@ default: lock_kernel(); sigaddset(¤t->signal, signr); + recalc_sigpending(current); current->flags |= PF_SIGNALED; do_exit(exit_code); /* NOT REACHED */ diff -u --recursive --new-file v2.3.6/linux/arch/sparc64/kernel/sys_sunos32.c linux/arch/sparc64/kernel/sys_sunos32.c --- v2.3.6/linux/arch/sparc64/kernel/sys_sunos32.c Wed Jun 9 14:44:25 1999 +++ linux/arch/sparc64/kernel/sys_sunos32.c Thu Jun 17 01:08:50 1999 @@ -164,7 +164,7 @@ * fool it, but this should catch most mistakes. */ freepages = buffermem >> PAGE_SHIFT; - freepages += page_cache_size; + freepages += atomic_read(&page_cache_size); freepages >>= 1; freepages += nr_free_pages; freepages += nr_swap_pages; diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/Config.in linux/drivers/acorn/block/Config.in --- v2.3.6/linux/drivers/acorn/block/Config.in Fri May 8 00:42:38 1998 +++ linux/drivers/acorn/block/Config.in Thu Jun 17 01:11:35 1999 @@ -4,15 +4,12 @@ mainmenu_option next_comment comment 'Acorn-specific block devices' -bool ' Support expansion card IDE interfaces' CONFIG_BLK_DEV_IDE_CARDS -if [ "$CONFIG_BLK_DEV_IDE_CARDS" = "y" ]; then - dep_tristate ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE $CONFIG_BLK_DEV_IDE - dep_tristate ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_BLK_DEV_IDE -fi - -tristate 'MFM harddisk support' CONFIG_BLK_DEV_MFM -if [ "$CONFIG_BLK_DEV_MFM" != "n" ]; then - bool ' Autodetect hard drive geometry' CONFIG_BLK_DEV_MFM_AUTODETECT +if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then + tristate 'Old Archimedes floppy (1772) support' CONFIG_BLK_DEV_FD1772 + tristate 'MFM harddisk support' CONFIG_BLK_DEV_MFM + if [ "$CONFIG_BLK_DEV_MFM" != "n" ]; then + bool ' Autodetect hard drive geometry' CONFIG_BLK_DEV_MFM_AUTODETECT + fi fi endmenu diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/Makefile linux/drivers/acorn/block/Makefile --- v2.3.6/linux/drivers/acorn/block/Makefile Mon Feb 16 13:49:36 1998 +++ linux/drivers/acorn/block/Makefile Thu Jun 17 01:11:35 1999 @@ -14,29 +14,11 @@ M_OBJS := MOD_LIST_NAME := ACORN_BLOCK_MODULES -ifeq ($(CONFIG_ARCH_ARC),y) - ifeq ($(CONFIG_BLK_DEV_FD),y) - L_OBJS += fd1772.o fd1772dma.o - else - ifeq ($(CONFIG_BLK_DEV_FD),m) - M_OBJS += fd1772_mod.o - endif - endif -endif - -ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),y) - L_OBJS += ide-ics.o +ifeq ($(CONFIG_BLK_DEV_FD1772),y) + L_OBJS += fd1772.o fd1772dma.o else - ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),m) - M_OBJS += ide-ics.o - endif -endif - -ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),y) - L_OBJS += ide-rapide.o -else - ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),m) - M_OBJS += ide-rapide.o + ifeq ($(CONFIG_BLK_DEV_FD1772),m) + M_OBJS += fd1772_mod.o endif endif diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/fd1772.c linux/drivers/acorn/block/fd1772.c --- v2.3.6/linux/drivers/acorn/block/fd1772.c Sat May 15 15:05:35 1999 +++ linux/drivers/acorn/block/fd1772.c Thu Jun 17 01:11:35 1999 @@ -114,6 +114,8 @@ * I wish I knew why that timer didn't work..... * * 16/11/96 - Fiddled and frigged for 2.0.18 + * + * DAG 30/01/99 - Started frobbing for 2.2.1 */ #include @@ -136,14 +138,14 @@ #include #include #include +#include #include -#include #include #include #define MAJOR_NR FLOPPY_MAJOR #define FLOPPY_DMA 0 -#include "blk.h" +#include /* Note: FD_MAX_UNITS could be redefined to 2 for the Atari (with * little additional rework in this file). But I'm not yet sure if diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/fd1772dma.S linux/drivers/acorn/block/fd1772dma.S --- v2.3.6/linux/drivers/acorn/block/fd1772dma.S Fri May 8 00:42:38 1998 +++ linux/drivers/acorn/block/fd1772dma.S Thu Jun 17 01:11:35 1999 @@ -4,45 +4,45 @@ .text - .global _fdc1772_dataaddr -_fdc1772_fiqdata: + .global fdc1772_dataaddr +fdc1772_fiqdata: @ Number of bytes left to DMA - .global _fdc1772_bytestogo -_fdc1772_bytestogo: + .global fdc1772_bytestogo +fdc1772_bytestogo: .word 0 @ Place to put/get data from in DMA - .global _fdc1772_dataaddr -_fdc1772_dataaddr: + .global fdc1772_dataaddr +fdc1772_dataaddr: .word 0 - .global _fdc1772_fdc_int_done -_fdc1772_fdc_int_done: + .global fdc1772_fdc_int_done +fdc1772_fdc_int_done: .word 0 - .global _fdc1772_comendstatus -_fdc1772_comendstatus: + .global fdc1772_comendstatus +fdc1772_comendstatus: .word 0 @ We hang this off DMA channel 1 - .global _fdc1772_comendhandler -_fdc1772_comendhandler: + .global fdc1772_comendhandler +fdc1772_comendhandler: mov r8,#IOC_BASE ldrb r9,[r8,#0x34] @ IOC FIQ status tst r9,#2 subeqs pc,r14,#4 @ should I leave a space here orr r9,r8,#0x10000 @ FDC base - adr r8,_fdc1772_fdc_int_done + adr r8,fdc1772_fdc_int_done ldrb r10,[r9,#0] @ FDC status mov r9,#1 @ Got a FIQ flag stmia r8,{r9,r10} subs pc,r14,#4 - .global _fdc1772_dma_read -_fdc1772_dma_read: + .global fdc1772_dma_read +fdc1772_dma_read: mov r8,#IOC_BASE ldrb r9,[r8,#0x34] @ IOC FIQ status tst r9,#1 - beq _fdc1772_dma_read_notours + beq fdc1772_dma_read_notours orr r8,r8,#0x10000 @ FDC base ldrb r10,[r8,#0xc] @ Read from FDC data reg (also clears interrupt) ldmia r11,{r8,r9} @@ -51,19 +51,19 @@ strplb r10,[r9],#1 @ Store the data and increment the pointer stmplia r11,{r8,r9} @ Update count/pointers @ Handle any other interrupts if there are any -_fdc1772_dma_read_notours: +fdc1772_dma_read_notours: @ Cant branch because this code has been copied down to the FIQ vector ldr pc,[pc,#-4] - .word _fdc1772_comendhandler - .global _fdc1772_dma_read_end -_fdc1772_dma_read_end: + .word fdc1772_comendhandler + .global fdc1772_dma_read_end +fdc1772_dma_read_end: - .global _fdc1772_dma_write -_fdc1772_dma_write: + .global fdc1772_dma_write +fdc1772_dma_write: mov r8,#IOC_BASE ldrb r9,[r8,#0x34] @ IOC FIQ status tst r9,#1 - beq _fdc1772_dma_write_notours + beq fdc1772_dma_write_notours orr r8,r8,#0x10000 @ FDC base ldmia r11,{r9,r10} subs r9,r9,#1 @ One less byte to go @@ -72,23 +72,23 @@ strplb r12,[r8,#0xc] @ write it to FDC data reg stmplia r11,{r9,r10} @ Update count and pointer - should clear interrupt @ Handle any other interrupts -_fdc1772_dma_write_notours: +fdc1772_dma_write_notours: @ Cant branch because this code has been copied down to the FIQ vector ldr pc,[pc,#-4] - .word _fdc1772_comendhandler + .word fdc1772_comendhandler - .global _fdc1772_dma_write_end -_fdc1772_dma_write_end: + .global fdc1772_dma_write_end +fdc1772_dma_write_end: @ Setup the FIQ R11 to point to the data and store the count, address @ for this dma @ R0=count @ R1=address - .global _fdc1772_setupdma -_fdc1772_setupdma: + .global fdc1772_setupdma +fdc1772_setupdma: @ The big job is flipping in and out of FIQ mode - adr r2,_fdc1772_fiqdata @ This is what we really came here for + adr r2,fdc1772_fiqdata @ This is what we really came here for stmia r2,{r0,r1} mov r3, pc teqp pc,#0x0c000001 @ Disable FIQs, IRQs and switch to FIQ mode diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/ide-ics.c linux/drivers/acorn/block/ide-ics.c --- v2.3.6/linux/drivers/acorn/block/ide-ics.c Tue Jun 1 23:25:48 1999 +++ linux/drivers/acorn/block/ide-ics.c Wed Dec 31 16:00:00 1969 @@ -1,295 +0,0 @@ -/* - * linux/arch/arm/drivers/block/ide-ics.c - * - * Copyright (c) 1996,1997 Russell King. - * - * Changelog: - * 08-06-1996 RMK Created - * 12-09-1997 RMK Added interrupt enable/disable - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../../block/ide.h" - -/* - * Maximum number of interfaces per card - */ -#define MAX_IFS 2 - -#define ICS_IDENT_OFFSET 0x8a0 - -#define ICS_ARCIN_V5_INTRSTAT 0x000 -#define ICS_ARCIN_V5_INTROFFSET 0x001 -#define ICS_ARCIN_V5_IDEOFFSET 0xa00 -#define ICS_ARCIN_V5_IDEALTOFFSET 0xae0 -#define ICS_ARCIN_V5_IDESTEPPING 4 - -#define ICS_ARCIN_V6_IDEOFFSET_1 0x800 -#define ICS_ARCIN_V6_INTROFFSET_1 0x880 -#define ICS_ARCIN_V6_INTRSTAT_1 0x8a4 -#define ICS_ARCIN_V6_IDEALTOFFSET_1 0x8e0 -#define ICS_ARCIN_V6_IDEOFFSET_2 0xc00 -#define ICS_ARCIN_V6_INTROFFSET_2 0xc80 -#define ICS_ARCIN_V6_INTRSTAT_2 0xca4 -#define ICS_ARCIN_V6_IDEALTOFFSET_2 0xce0 -#define ICS_ARCIN_V6_IDESTEPPING 4 - -static const card_ids icside_cids[] = { - { MANU_ICS, PROD_ICS_IDE }, - { 0xffff, 0xffff } -}; - -typedef enum { - ics_if_unknown, - ics_if_arcin_v5, - ics_if_arcin_v6 -} iftype_t; - -static struct expansion_card *ec[MAX_ECARDS]; -static int result[MAX_ECARDS][MAX_IFS]; - - -/* ---------------- Version 5 PCB Support Functions --------------------- */ -/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) - * Purpose : enable interrupts from card - */ -static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) -{ - unsigned int memc_port = (unsigned int)ec->irq_data; - outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET); -} - -/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) - * Purpose : disable interrupts from card - */ -static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) -{ - unsigned int memc_port = (unsigned int)ec->irq_data; - inb (memc_port + ICS_ARCIN_V5_INTROFFSET); -} - -static const expansioncard_ops_t icside_ops_arcin_v5 = { - icside_irqenable_arcin_v5, - icside_irqdisable_arcin_v5, - NULL, - NULL -}; - - -/* ---------------- Version 6 PCB Support Functions --------------------- */ -/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) - * Purpose : enable interrupts from card - */ -static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) -{ - unsigned int ide_base_port = (unsigned int)ec->irq_data; - outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); - outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); -} - -/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) - * Purpose : disable interrupts from card - */ -static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) -{ - unsigned int ide_base_port = (unsigned int)ec->irq_data; - inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); - inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); -} - -static const expansioncard_ops_t icside_ops_arcin_v6 = { - icside_irqenable_arcin_v6, - icside_irqdisable_arcin_v6, - NULL, - NULL -}; - - - -/* Prototype: icside_identifyif (struct expansion_card *ec) - * Purpose : identify IDE interface type - * Notes : checks the description string - */ -static iftype_t icside_identifyif (struct expansion_card *ec) -{ - unsigned int addr; - iftype_t iftype; - int id = 0; - - iftype = ics_if_unknown; - - addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET; - - id = inb (addr) & 1; - id |= (inb (addr + 1) & 1) << 1; - id |= (inb (addr + 2) & 1) << 2; - id |= (inb (addr + 3) & 1) << 3; - - switch (id) { - case 0: /* A3IN */ - printk ("icside: A3IN unsupported\n"); - break; - - case 1: /* A3USER */ - printk ("icside: A3USER unsupported\n"); - break; - - case 3: /* ARCIN V6 */ - printk ("icside: detected ARCIN V6 in slot %d\n", ec->slot_no); - iftype = ics_if_arcin_v6; - break; - - case 15:/* ARCIN V5 (no id) */ - printk ("icside: detected ARCIN V5 in slot %d\n", ec->slot_no); - iftype = ics_if_arcin_v5; - break; - - default:/* we don't know - complain very loudly */ - printk ("icside: ***********************************\n"); - printk ("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id); - printk ("icside: ***********************************\n"); - printk ("icside: please report this to: linux@arm.uk.linux.org\n"); - printk ("icside: defaulting to ARCIN V5\n"); - iftype = ics_if_arcin_v5; - break; - } - - return iftype; -} - -static int icside_register_port(unsigned long dataport, unsigned long ctrlport, int stepping, int irq) -{ - hw_regs_t hw; - int i; - - memset(&hw, 0, sizeof(hw)); - - for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { - hw.io_ports[i] = (ide_ioreg_t)dataport; - dataport += 1 << stepping; - } - hw.io_ports[IDE_CONTROL_OFFSET] = ctrlport; - hw.irq = irq; - - return ide_register_hw(&hw, NULL); -} - -/* Prototype: icside_register (struct expansion_card *ec) - * Purpose : register an ICS IDE card with the IDE driver - * Notes : we make sure that interrupts are disabled from the card - */ -static inline void icside_register (struct expansion_card *ec, int index) -{ - unsigned long port; - - result[index][0] = -1; - result[index][1] = -1; - - switch (icside_identifyif (ec)) { - case ics_if_unknown: - default: - printk ("** Warning: ICS IDE Interface unrecognised! **\n"); - break; - - case ics_if_arcin_v5: - port = ecard_address (ec, ECARD_MEMC, 0); - ec->irqaddr = ioaddr(port + ICS_ARCIN_V5_INTRSTAT); - ec->irqmask = 1; - ec->irq_data = (void *)port; - ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5; - - /* - * Be on the safe side - disable interrupts - */ - inb (port + ICS_ARCIN_V5_INTROFFSET); - result[index][0] = icside_register_port(port + ICS_ARCIN_V5_IDEOFFSET, - port + ICS_ARCIN_V5_IDEALTOFFSET, - ICS_ARCIN_V5_IDESTEPPING, - ec->irq); - result[index][1] = -1; - break; - - case ics_if_arcin_v6: - port = ecard_address (ec, ECARD_IOC, ECARD_FAST); - ec->irqaddr = ioaddr(port + ICS_ARCIN_V6_INTRSTAT_1); - ec->irqmask = 1; - ec->irq_data = (void *)port; - ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6; - - /* - * Be on the safe side - disable interrupts - */ - inb (port + ICS_ARCIN_V6_INTROFFSET_1); - inb (port + ICS_ARCIN_V6_INTROFFSET_2); - - result[index][0] = icside_register_port(port + ICS_ARCIN_V6_IDEOFFSET_1, - port + ICS_ARCIN_V6_IDEALTOFFSET_1, - ICS_ARCIN_V6_IDESTEPPING, - ec->irq); - result[index][1] = icside_register_port(port + ICS_ARCIN_V6_IDEOFFSET_2, - port + ICS_ARCIN_V6_IDEALTOFFSET_2, - ICS_ARCIN_V6_IDESTEPPING, - ec->irq); - break; - } -} - -int icside_init (void) -{ - int i; - - for (i = 0; i < MAX_ECARDS; i++) - ec[i] = NULL; - - ecard_startfind (); - - for (i = 0; ; i++) { - if ((ec[i] = ecard_find (0, icside_cids)) == NULL) - break; - - ecard_claim (ec[i]); - icside_register (ec[i], i); - } - - for (i = 0; i < MAX_ECARDS; i++) - if (ec[i] && result[i][0] < 0 && result[i][1] < 0) { - ecard_release (ec[i]); - ec[i] = NULL; - } - return 0; -} - -#ifdef MODULE -int init_module (void) -{ - return icside_init(); -} - -void cleanup_module (void) -{ - int i; - - for (i = 0; i < MAX_ECARDS; i++) - if (ec[i]) { - if (result[i][0] >= 0) - ide_unregister (result[i][0]); - - if (result[i][1] >= 0) - ide_unregister (result[i][1]); - - ecard_release (ec[i]); - ec[i] = NULL; - } -} -#endif - diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/ide-rapide.c linux/drivers/acorn/block/ide-rapide.c --- v2.3.6/linux/drivers/acorn/block/ide-rapide.c Sun Sep 6 10:46:07 1998 +++ linux/drivers/acorn/block/ide-rapide.c Wed Dec 31 16:00:00 1969 @@ -1,87 +0,0 @@ -/* - * linux/arch/arm/drivers/block/ide-rapide.c - * - * Copyright (c) 1996-1998 Russell King. - * - * Changelog: - * 08-06-1996 RMK Created - * 13-04-1998 RMK Added manufacturer and product IDs - */ - -#include -#include -#include -#include -#include -#include - -#include "../../block/ide.h" - -static const card_ids rapide_cids[] = { - { MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 }, - { 0xffff, 0xffff } -}; - -static struct expansion_card *ec[MAX_ECARDS]; -static int result[MAX_ECARDS]; - -static inline int rapide_register(struct expansion_card *ec) -{ - unsigned long port = ecard_address (ec, ECARD_MEMC, 0); - ide_ioregspec_t spec; - - spec.base = port; - spec.ctrl = port + 0x206; - spec.offset = 1 << 4; - spec.irq = ec->irq; - - return ide_register_port(&spec); -} - -int rapide_init(void) -{ - int i; - - for (i = 0; i < MAX_ECARDS; i++) - ec[i] = NULL; - - ecard_startfind(); - - for (i = 0; ; i++) { - if ((ec[i] = ecard_find(0, rapide_cids)) == NULL) - break; - - ecard_claim(ec[i]); - result[i] = rapide_register(ec[i]); - } - for (i = 0; i < MAX_ECARDS; i++) - if (ec[i] && result[i] < 0) { - ecard_release(ec[i]); - ec[i] = NULL; - } - return 0; -} - -#ifdef MODULE - -int init_module (void) -{ - return rapide_init(); -} - -void cleanup_module (void) -{ - int i; - - for (i = 0; i < MAX_ECARDS; i++) - if (ec[i]) { - unsigned long port; - port = ecard_address(ec[i], ECARD_MEMC, 0); - - ide_unregister_port(port, ec[i]->irq, 16); - ecard_release(ec[i]); - ec[i] = NULL; - } -} -#endif - diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/mfm.S linux/drivers/acorn/block/mfm.S --- v2.3.6/linux/drivers/acorn/block/mfm.S Wed May 20 18:54:37 1998 +++ linux/drivers/acorn/block/mfm.S Thu Jun 17 01:11:35 1999 @@ -1,44 +1,43 @@ -@ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400 -@ motherboard on ST506 podules. -@ (c) David Alan Gilbert (gilbertd@cs.man.ac.uk) 1996 +@ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400 Acorn Archimedes +@ motherboard and on ST506 expansion podules. +@ (c) David Alan Gilbert (linux@treblig.org) 1996-1999 #include -_hdc63463_irqdata: +hdc63463_irqdata: @ Controller base address - .global _hdc63463_baseaddress -_hdc63463_baseaddress: + .global hdc63463_baseaddress +hdc63463_baseaddress: .word 0 - .global _hdc63463_irqpolladdress -_hdc63463_irqpolladdress: + .global hdc63463_irqpolladdress +hdc63463_irqpolladdress: .word 0 - .global _hdc63463_irqpollmask -_hdc63463_irqpollmask: + .global hdc63463_irqpollmask +hdc63463_irqpollmask: .word 0 @ where to read/write data from the kernel data space - .global _hdc63463_dataptr -_hdc63463_dataptr: + .global hdc63463_dataptr +hdc63463_dataptr: .word 0 @ Number of bytes left to transfer - .global _hdc63463_dataleft -_hdc63463_dataleft: + .global hdc63463_dataleft +hdc63463_dataleft: .word 0 @ ------------------------------------------------------------------------- @ hdc63463_writedma: DMA from host to controller @ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask @ r3=data ptr, r4=data left, r5,r6=temporary - .global _hdc63463_writedma -_hdc63463_writedma: + .global hdc63463_writedma +hdc63463_writedma: stmfd sp!,{r4-r7} - adr r5,_hdc63463_irqdata + adr r5,hdc63463_irqdata ldmia r5,{r0,r1,r2,r3,r4} - writedma_again: @ test number of remaining bytes to transfer @@ -89,12 +88,12 @@ @ If we were too slow we had better go through again - DAG - took out with new interrupt routine @ sub r0,r0,#32+8 - @ adr r2,_hdc63463_irqdata + @ adr r2,hdc63463_irqdata @ ldr r2,[r2,#8] @ b writedma_again writedma_end: - adr r5,_hdc63463_irqdata+12 + adr r5,hdc63463_irqdata+12 stmia r5,{r3,r4} ldmfd sp!,{r4-r7} RETINSTR(mov,pc,lr) @@ -103,10 +102,10 @@ @ hdc63463_readdma: DMA from controller to host @ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask @ r3=data ptr, r4=data left, r5,r6=temporary - .global _hdc63463_readdma -_hdc63463_readdma: + .global hdc63463_readdma +hdc63463_readdma: stmfd sp!,{r4-r7} - adr r5,_hdc63463_irqdata + adr r5,hdc63463_irqdata ldmia r5,{r0,r1,r2,r3,r4} readdma_again: @@ -157,7 +156,7 @@ @ b readdma_again readdma_end: - adr r5,_hdc63463_irqdata+12 + adr r5,hdc63463_irqdata+12 stmia r5,{r3,r4} ldmfd sp!,{r4-r7} RETINSTR(mov,pc,lr) diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/block/mfmhd.c linux/drivers/acorn/block/mfmhd.c --- v2.3.6/linux/drivers/acorn/block/mfmhd.c Sat May 15 15:05:35 1999 +++ linux/drivers/acorn/block/mfmhd.c Thu Jun 17 01:11:35 1999 @@ -123,6 +123,7 @@ #include #include #include +#include /* * This sort of stuff should be in a header file shared with ide.c, hd.c, xd.c etc @@ -261,7 +262,9 @@ void (*done) (int st); /* done handler */ } *cont = NULL; +#if 0 static struct tq_struct mfm_tq = {0, 0, (void (*)(void *)) NULL, 0}; +#endif int number_mfm_drives = 1; diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/char/Config.in linux/drivers/acorn/char/Config.in --- v2.3.6/linux/drivers/acorn/char/Config.in Thu Dec 17 09:07:45 1998 +++ linux/drivers/acorn/char/Config.in Wed Dec 31 16:00:00 1969 @@ -1,15 +0,0 @@ -if [ "$CONFIG_SERIAL" != "n" ]; then - tristate ' Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL - tristate ' Dual serial port support' CONFIG_DUALSP_SERIAL -fi - -if [ "$CONFIG_MOUSE" = "y" ]; then - if [ "$CONFIG_ARCH_ACORN" = "y" ]; then - if [ "$CONFIG_ARCH_RPC" != "y" ]; then - define_bool CONFIG_KBDMOUSE y - else - define_bool CONFIG_RPCMOUSE y - fi - fi -fi - diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/char/Makefile linux/drivers/acorn/char/Makefile --- v2.3.6/linux/drivers/acorn/char/Makefile Thu Dec 17 09:07:45 1998 +++ linux/drivers/acorn/char/Makefile Thu Jun 17 01:11:35 1999 @@ -9,20 +9,21 @@ # parent makes.. # -L_TARGET := acorn-char.a -M_OBJS := -L_OBJS := +L_TARGET := acorn-char.a +M_OBJS := +L_OBJS := -ifeq ($(MACHINE),rpc) - MOUSE_OBJS += mouse_rpc.o - L_OBJS += keyb_ps2.o -endif +L_OBJS_arc := keyb_arc.o +L_OBJS_a5k := keyb_arc.o +L_OBJS_rpc := keyb_ps2.o -ifeq ($(CONFIG_MOUSE),y) - LX_OBJS += $(MOUSE_OBJS) -else - ifeq ($(CONFIG_MOUSE),m) - MX_OBJS += $(MOUSE_OBJS) +ifeq ($(MACHINE),rpc) + ifeq ($(CONFIG_MOUSE),y) + LX_OBJS += mouse_rpc.o + else + ifeq ($(CONFIG_MOUSE),m) + MX_OBJS += mouse_rpc.o + endif endif endif @@ -41,5 +42,7 @@ M_OBJS += serial-dualsp.o endif endif + +L_OBJS += $(L_OBJS_$(MACHINE)) include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/char/keyb_arc.c linux/drivers/acorn/char/keyb_arc.c --- v2.3.6/linux/drivers/acorn/char/keyb_arc.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/acorn/char/keyb_arc.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,451 @@ +/* + * linux/arch/arm/drivers/char1/keyb_arc.c + * + * Acorn keyboard driver for ARM Linux. + * + * The Acorn keyboard appears to have a ***very*** buggy reset protocol - + * every reset behaves differently. We try to get round this by attempting + * a few things... + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../../char/mouse.h" + +extern void kbd_reset_kdown(void); + +#define VERSION 108 + +#define KBD_REPORT_ERR +#define KBD_REPORT_UNKN + +#include +#include + +static char kbd_txval[4]; +static unsigned char kbd_txhead, kbd_txtail; +#define KBD_INCTXPTR(ptr) ((ptr) = ((ptr) + 1) & 3) +static int kbd_id = -1; +static struct wait_queue *kbd_waitq; +#ifdef CONFIG_KBDMOUSE +static int mousedev; +#endif + +/* + * Protocol codes to send the keyboard. + */ +#define HRST 0xff /* reset keyboard */ +#define RAK1 0xfe /* reset response */ +#define RAK2 0xfd /* reset response */ +#define BACK 0x3f /* Ack for first keyboard pair */ +#define SMAK 0x33 /* Last data byte ack (key scanning + mouse movement scanning) */ +#define MACK 0x32 /* Last data byte ack (mouse movement scanning) */ +#define SACK 0x31 /* Last data byte ack (key scanning) */ +#define NACK 0x30 /* Last data byte ack (no scanning, mouse data) */ +#define RQMP 0x22 /* Request mouse data */ +#define PRST 0x21 /* nothing */ +#define RQID 0x20 /* Request ID */ + +#define UP_FLAG 1 + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char a5kkbd_sysrq_xlate[] = +{ + 27, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + '`', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '-', '=', '£', 127, 0, + 0, 0, 0, '/', '*', '#', 9, 'q', + 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', + 'p', '[', ']', '\\', 22, 23, 25, '7', + '8', '9', '-', 0, 'a', 's', 'd', 'f', + 'g', 'h', 'j', 'k', 'l', ';', '\'', 13, + '4', '5', '6', '+', 0, 0, 'z', 'x', + 'c', 'v', 'b', 'n', 'm', ',', '.', '/', + 0, 0, '1', '2', '3', 0, 0, ' ', + 0, 0, 0, 0, 0, '0', '.', 10, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; +#endif + +/* + * This array converts the scancode that we get from the keyboard to the + * real rows/columns on the A5000 keyboard. This might be keyboard specific... + * + * It is these values that we use to maintain the key down array. That way, we + * should pick up on the ghost key presses (which is what happens when you press + * three keys, and the keyboard thinks you have pressed four!) + * + * Row 8 (0x80+c) is actually a column with one key per row. It is isolated from + * the other keys, and can't cause these problems (its used for shift, ctrl, alt etc). + * + * Illegal scancodes are denoted by an 0xff (in other words, we don't know about + * them, and can't process them for ghosts). This does however, cause problems with + * autorepeat processing... + */ +static unsigned char scancode_2_colrow[256] = { + 0x01, 0x42, 0x32, 0x33, 0x43, 0x56, 0x5a, 0x6c, 0x7c, 0x5c, 0x5b, 0x6b, 0x7b, 0x84, 0x70, 0x60, + 0x11, 0x51, 0x62, 0x63, 0x44, 0x54, 0x55, 0x45, 0x46, 0x4a, 0x3c, 0x4b, 0x59, 0x49, 0x69, 0x79, + 0x83, 0x40, 0x30, 0x3b, 0x39, 0x38, 0x31, 0x61, 0x72, 0x73, 0x64, 0x74, 0x75, 0x65, 0x66, 0x6a, + 0x1c, 0x2c, 0x7a, 0x36, 0x48, 0x68, 0x78, 0x20, 0x2b, 0x29, 0x28, 0x81, 0x71, 0x22, 0x23, 0x34, + 0x24, 0x25, 0x35, 0x26, 0x3a, 0x0c, 0x2a, 0x76, 0x10, 0x1b, 0x19, 0x18, 0x82, 0xff, 0x21, 0x12, + 0x13, 0x14, 0x04, 0x05, 0x15, 0x16, 0x1a, 0x0a, 0x85, 0x77, 0x00, 0x0b, 0x09, 0x02, 0x80, 0x03, + 0x87, 0x86, 0x06, 0x17, 0x27, 0x07, 0x37, 0x08, 0xff, +}; + +#define BITS_PER_SHORT (8*sizeof(unsigned short)) +static unsigned short ghost_down[128/BITS_PER_SHORT]; + +static void a5kkbd_key(unsigned int keycode, unsigned int up_flag) +{ + unsigned int real_keycode; + + if (keycode > 0x72) { +#ifdef KBD_REPORT_UNKN + printk ("kbd: unknown scancode 0x%04x\n", keycode); +#endif + return; + } + if (keycode >= 0x70) { +#ifdef CONFIG_KBDMOUSE + if (mousedev >= 0) + switch (keycode) { + case 0x70: /* Left mouse button */ + busmouse_add_buttons(mousedev, 4, up_flag ? 4 : 0); + break; + + case 0x71: /* Middle mouse button */ + busmouse_add_buttons(mousedev, 2, up_flag ? 2 : 0); + break; + + case 0x72:/* Right mouse button */ + busmouse_add_buttons(mousedev, 1, up_flag ? 1 : 0); + break; + } +#endif + return; + } + + /* + * We have to work out if we accept this key press as a real key, or + * if it is a ghost. IE. If you press three keys, the keyboard will think + * that you've pressed a fourth: (@ = key down, # = ghost) + * + * 0 1 2 3 4 5 6 7 + * | | | | | | | | + * 0-+-+-+-+-+-+-+-+- + * | | | | | | | | + * 1-+-@-+-+-+-@-+-+- + * | | | | | | | | + * 2-+-+-+-+-+-+-+-+- + * | | | | | | | | + * 3-+-@-+-+-+-#-+-+- + * | | | | | | | | + * + * This is what happens when you have a matrix keyboard... + */ + + real_keycode = scancode_2_colrow[keycode]; + + if ((real_keycode & 0x80) == 0) { + int rr, kc = (real_keycode >> 4) & 7; + int cc; + unsigned short res, kdownkc; + + kdownkc = ghost_down[kc] | (1 << (real_keycode & 15)); + + for (rr = 0; rr < 128/BITS_PER_SHORT; rr++) + if (rr != kc && (res = ghost_down[rr] & kdownkc)) { + /* + * we have found a second row with at least one key pressed in the + * same column. + */ + for (cc = 0; res; res >>= 1) + cc += (res & 1); + if (cc > 1) + return; /* ignore it */ + } + if (up_flag) + clear_bit (real_keycode, ghost_down); + else + set_bit (real_keycode, ghost_down); + } + + handle_scancode(keycode, !up_flag); +} + +static inline void a5kkbd_sendbyte(unsigned char val) +{ + kbd_txval[kbd_txhead] = val; + KBD_INCTXPTR(kbd_txhead); + enable_irq(IRQ_KEYBOARDTX); +} + +static inline void a5kkbd_reset(void) +{ + int i; + + for (i = 0; i < NR_SCANCODES/BITS_PER_SHORT; i++) + ghost_down[i] = 0; + + kbd_reset_kdown(); +} + +void a5kkbd_leds(unsigned char leds) +{ + leds = ((leds & (1<= 0) + busmouse_add_movement(mousedev, (int)kbd_mousedx, (int)kbd_mousedy); +#endif + } + } + return kbd_state == KBD_IDLE ? 1 : 0; + +kbd_wontreset: +#ifdef KBD_REPORT_ERR + printk ("kbd: keyboard won't reset (kbdstate %d, keyval %02X)\n", + kbd_state, keyval); +#endif + mdelay(1); + inb(IOC_KARTRX); + a5kkbd_sendbyte (HRST); + kbd_state = KBD_INITRST; + return 0; + +kbd_error: +#ifdef KBD_REPORT_ERR + printk ("kbd: keyboard out of sync - resetting\n"); +#endif + a5kkbd_sendbyte (HRST); + kbd_state = KBD_INITRST; + return 0; +} + +static void a5kkbd_rx(int irq, void *dev_id, struct pt_regs *regs) +{ + kbd_pt_regs = regs; + if (handle_rawcode(inb(IOC_KARTRX))) + mark_bh (KEYBOARD_BH); +} + +static void a5kkbd_tx(int irq, void *dev_id, struct pt_regs *regs) +{ + outb (kbd_txval[kbd_txtail], IOC_KARTTX); + KBD_INCTXPTR(kbd_txtail); + if (kbd_txtail == kbd_txhead) + disable_irq(irq); +} + +#ifdef CONFIG_KBDMOUSE +static struct busmouse a5kkbd_mouse = { + 6, "kbdmouse", NULL, NULL, 7 +}; +#endif + +__initfunc(void a5kkbd_init_hw (void)) +{ + unsigned long flags; + + save_flags_cli (flags); + if (request_irq (IRQ_KEYBOARDTX, a5kkbd_tx, 0, "keyboard", NULL) != 0) + panic("Could not allocate keyboard transmit IRQ!"); + disable_irq (IRQ_KEYBOARDTX); + if (request_irq (IRQ_KEYBOARDRX, a5kkbd_rx, 0, "keyboard", NULL) != 0) + panic("Could not allocate keyboard receive IRQ!"); + (void)inb(IOC_KARTRX); + restore_flags (flags); + + a5kkbd_sendbyte (HRST); /* send HRST (expect HRST) */ + + /* wait 1s for keyboard to initialise */ + interruptible_sleep_on_timeout(&kbd_waitq, HZ); + +#ifdef CONFIG_KBDMOUSE + mousedev = register_busmouse(&a5kkbd_mouse); + if (mousedev < 0) + printk(KERN_ERR "Unable to register mouse driver\n"); +#endif + + printk (KERN_INFO "Keyboard driver v%d.%02d. (", VERSION/100, VERSION%100); + if (kbd_id != -1) + printk ("id=%d ", kbd_id); + printk ("English)\n"); +} diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/char/keyb_ps2.c linux/drivers/acorn/char/keyb_ps2.c --- v2.3.6/linux/drivers/acorn/char/keyb_ps2.c Mon Apr 26 13:31:49 1999 +++ linux/drivers/acorn/char/keyb_ps2.c Thu Jun 17 01:11:35 1999 @@ -25,6 +25,7 @@ #include #include #include +#include #include extern void kbd_reset_kdown(void); @@ -221,14 +222,7 @@ }; #endif -int ps2kbd_translate(unsigned char scancode, unsigned char *keycode_p, char *uf_p) -{ - *uf_p = scancode & 0200; - *keycode_p = scancode & 0x7f; - return 1; -} - -static void ps2kbd_key(unsigned int keycode, unsigned int up_flag) +static inline void ps2kbd_key(unsigned int keycode, unsigned int up_flag) { handle_scancode(keycode, !up_flag); } diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/char/mouse_rpc.c linux/drivers/acorn/char/mouse_rpc.c --- v2.3.6/linux/drivers/acorn/char/mouse_rpc.c Thu Dec 17 09:07:45 1998 +++ linux/drivers/acorn/char/mouse_rpc.c Thu Jun 17 01:11:35 1999 @@ -1,5 +1,5 @@ /* - * linux/drivers/char/rpcmouse.c + * linux/drivers/char/mouse_rpc.c * * Copyright (C) 1996-1998 Russell King * @@ -16,6 +16,7 @@ #include #include #include +#include #include "../../char/mouse.h" diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/char/serial-card.c linux/drivers/acorn/char/serial-card.c --- v2.3.6/linux/drivers/acorn/char/serial-card.c Sun Sep 6 10:46:07 1998 +++ linux/drivers/acorn/char/serial-card.c Thu Jun 17 01:11:35 1999 @@ -33,14 +33,20 @@ #ifdef MODULE static int __serial_ports[NUM_SERIALS]; static int __serial_pcount; +static int __serial_addr[NUM_SERIALS]; static struct expansion_card *expcard[MAX_ECARDS]; #define ADD_ECARD(ec,card) expcard[(card)] = (ec) -#define ADD_PORT(port) __serial_ports[__serial_pcount++] = (port) +#define ADD_PORT(port,addr) \ + do { \ + __serial_ports[__serial_pcount] = (port); \ + __serial_addr[__serial_pcount] = (addr); \ + __serial_pcount += 1; \ + } while (0) #undef MY_INIT #define MY_INIT init_module #else #define ADD_ECARD(ec,card) -#define ADD_PORT(port) +#define ADD_PORT(port,addr) #endif static const card_ids serial_cids[] = { MY_CARD_LIST, { 0xffff, 0xffff } }; @@ -75,12 +81,15 @@ cardaddr = MY_BASE_ADDRESS(ec); for (port = 0; port < MY_NUMPORTS; port ++) { + unsigned long address; int line; - line = serial_register_onedev (MY_PORT_ADDRESS(port, cardaddr), ec->irq); + address = MY_PORT_ADDRESS(port, cardaddr); + + line = serial_register_onedev (address, ec->irq); if (line < 0) break; - ADD_PORT(line); + ADD_PORT(line, address); } if (port) { @@ -97,8 +106,10 @@ { int i; - for (i = 0; i < __serial_pcount; i++) - unregister_serial (__serial_ports[i]); + for (i = 0; i < __serial_pcount; i++) { + unregister_serial(__serial_ports[i]); + release_region(__serial_addr[i], 8); + } for (i = 0; i < MAX_ECARDS; i++) if (expcard[i]) diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/net/ether1.c linux/drivers/acorn/net/ether1.c --- v2.3.6/linux/drivers/acorn/net/ether1.c Wed Dec 23 09:44:41 1998 +++ linux/drivers/acorn/net/ether1.c Thu Jun 17 01:11:35 1999 @@ -71,7 +71,7 @@ #define BUS_16 16 #define BUS_8 8 -static const card_ids ether1_cids[] = { +static const card_ids __init ether1_cids[] = { { MANU_ACORN, PROD_ACORN_ETHER1 }, { 0xffff, 0xffff } }; @@ -128,7 +128,7 @@ { int used; - addr = IO_BASE + (addr << 2); + addr = ioaddr(addr); __asm__ __volatile__( "subs %3, %3, #2 @@ -171,7 +171,7 @@ { int used; - addr = IO_BASE + (addr << 2); + addr = ioaddr(addr); __asm__ __volatile__( "subs %3, %3, #2 @@ -659,12 +659,6 @@ /* Fill in the fields of the device structure with ethernet values */ ether_setup (dev); -#ifndef CLAIM_IRQ_AT_OPEN - if (request_irq (dev->irq, ether1_interrupt, 0, "ether1", dev)) { - kfree (dev->priv); - return -EAGAIN; - } -#endif return 0; } @@ -759,18 +753,16 @@ ether1_open (struct device *dev) { struct ether1_priv *priv = (struct ether1_priv *)dev->priv; -#ifdef CLAIM_IRQ_AT_OPEN + if (request_irq (dev->irq, ether1_interrupt, 0, "ether1", dev)) return -EAGAIN; -#endif + MOD_INC_USE_COUNT; memset (&priv->stats, 0, sizeof (struct enet_statistics)); if (ether1_init_for_open (dev)) { -#ifdef CLAIM_IRQ_AT_OPEN free_irq (dev->irq, dev); -#endif MOD_DEC_USE_COUNT; return -EAGAIN; } @@ -1080,12 +1072,10 @@ static int ether1_close (struct device *dev) { -#ifdef CLAIM_IRQ_AT_OPEN - free_irq (dev->irq, dev); -#endif - ether1_reset (dev); + free_irq(dev->irq, dev); + dev->start = 0; dev->tbusy = 0; @@ -1117,56 +1107,46 @@ #ifdef MODULE -static char ethernames[MAX_ECARDS][9]; -static struct device *my_ethers[MAX_ECARDS]; -static struct expansion_card *ec[MAX_ECARDS]; +static struct ether_dev { + struct expansion_card *ec; + char name[9]; + struct device dev; +} ether_devs[MAX_ECARDS]; int init_module (void) { - int i; + struct expansion_card *ec; + int i, ret = -ENODEV; - for (i = 0; i < MAX_ECARDS; i++) { - my_ethers[i] = NULL; - ec[i] = NULL; - strcpy (ethernames[i], " "); - } + memset(ether_devs, 0, sizeof(ether_devs)); + ecard_startfind (); + ec = ecard_find(0, ether1_cids); i = 0; - ecard_startfind (); + while (ec && i < MAX_ECARDS) { + ecard_claim(ec); - do { - if ((ec[i] = ecard_find(0, ether1_cids)) == NULL) + ether_devs[i].ec = ec; + ether_devs[i].dev.irq = ec->irq; + ether_devs[i].dev.base_addr = ecard_address(ec, ECARD_IOC, ECARD_FAST); + ether_devs[i].dev.init = ether1_probe; + ether_devs[i].dev.name = ether_devs[i].name; + + ret = register_netdev(ðer_devs[i].dev); + + if (ret) { + ecard_release(ec); + ether_devs[i].ec = NULL; break; - - my_ethers[i] = (struct device *)kmalloc (sizeof (struct device), GFP_KERNEL); - memset (my_ethers[i], 0, sizeof (struct device)); - - my_ethers[i]->irq = ec[i]->irq; - my_ethers[i]->base_addr = ecard_address (ec[i], ECARD_IOC, ECARD_FAST); - my_ethers[i]->init = ether1_probe; - my_ethers[i]->name = ethernames[i]; - - ecard_claim (ec[i]); - - if (register_netdev (my_ethers[i]) != 0) { - for (i = 0; i < 4; i++) { - if (my_ethers[i]) { - kfree (my_ethers[i]); - my_ethers[i] = NULL; - } - if (ec[i]) { - ecard_release (ec[i]); - ec[i] = NULL; - } - } - return -EIO; } - i++; - } while (i < MAX_ECARDS); - return i != 0 ? 0 : -ENODEV; + i += 1; + ec = ecard_find(0, ether1_cids); + } + + return i != 0 ? 0 : ret; } void @@ -1175,18 +1155,15 @@ int i; for (i = 0; i < MAX_ECARDS; i++) { - if (my_ethers[i]) { - unregister_netdev (my_ethers[i]); - release_region (my_ethers[i]->base_addr, 16); - release_region (my_ethers[i]->base_addr + 0x800, 4096); -#ifndef CLAIM_IRQ_AT_OPEN - free_irq (my_ethers[i]->irq, my_ethers[i]); -#endif - my_ethers[i] = NULL; - } - if (ec[i]) { - ecard_release (ec[i]); - ec[i] = NULL; + if (ether_devs[i].ec) { + unregister_netdev(ðer_devs[i].dev); + + release_region(ether_devs[i].dev.base_addr, 16); + release_region(ether_devs[i].dev.base_addr + 0x800, 4096); + + ecard_release(ether_devs[i].ec); + + ether_devs[i].ec = NULL; } } } diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/net/ether3.c linux/drivers/acorn/net/ether3.c --- v2.3.6/linux/drivers/acorn/net/ether3.c Sun Sep 6 10:46:07 1998 +++ linux/drivers/acorn/net/ether3.c Thu Jun 17 01:11:35 1999 @@ -33,11 +33,12 @@ * packet starts two bytes from the end of the * buffer, it corrupts the receiver chain, and * never updates the transmit status correctly. - * TODO: - * When we detect a fatal error on the interface, we should restart it. + * 1.14 RMK 07/01/1998 Added initial code for ETHERB addressing. + * 1.15 RMK 30/04/1999 More fixes to the transmit routine for buggy + * hardware. */ -static char *version = "ether3 ethernet driver (c) 1995-1998 R.M.King v1.13\n"; +static char *version = "ether3 ethernet driver (c) 1995-1999 R.M.King v1.15\n"; #include #include @@ -66,7 +67,7 @@ #include "ether3.h" static unsigned int net_debug = NET_DEBUG; -static const card_ids ether3_cids[] = { +static const card_ids __init ether3_cids[] = { { MANU_ANT2, PROD_ANT_ETHER3 }, { MANU_ANT, PROD_ANT_ETHER3 }, { MANU_ANT, PROD_ANT_ETHERB }, /* trial - will etherb work? */ @@ -77,9 +78,6 @@ static int ether3_rx(struct device *dev, struct dev_priv *priv, unsigned int maxcnt); static void ether3_tx(struct device *dev, struct dev_priv *priv); -extern int inswb(int reg, void *buffer, int len); -extern int outswb(int reg, void *buffer, int len); - #define BUS_16 2 #define BUS_8 1 #define BUS_UNKNOWN 0 @@ -88,7 +86,7 @@ * I'm not sure what address we should default to if the internal one * is corrupted... */ -unsigned char def_eth_addr[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; +unsigned char def_eth_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; /* --------------------------------------------------------------------------- */ @@ -99,6 +97,8 @@ /* * ether3 read/write. Slow things down a bit... + * The SEEQ8005 doesn't like us writing to it's registers + * too quickly. */ #define ether3_outb(v,r) { outb((v),(r)); udelay(1); } #define ether3_outw(v,r) { outw((v),(r)); udelay(1); } @@ -138,7 +138,7 @@ * write data to the buffer memory */ #define ether3_writebuffer(dev,data,length) \ - outswb(REG_BUFWIN, (data), (length)) + outsw(REG_BUFWIN, (data), (length) >> 1) #define ether3_writeword(dev,data) \ outw((data), REG_BUFWIN) @@ -153,7 +153,7 @@ * read data from the buffer memory */ #define ether3_readbuffer(dev,data,length) \ - inswb(REG_BUFWIN, (data), (length)) + insw(REG_BUFWIN, (data), (length) >> 1) #define ether3_readword(dev) \ inw(REG_BUFWIN) @@ -249,7 +249,7 @@ } } else { if (bad != -1) { - if (bad != i - 1) + if (bad != i - 1) printk(" - 0x%04X\n", i - 1); printk("\n"); bad = -1; @@ -335,7 +335,6 @@ for (i = 0; i < 6; i++) ether3_outb(dev->dev_addr[i], REG_BUFWIN); - priv->tx_used = 0; priv->tx_head = 0; priv->tx_tail = 0; priv->regs.config2 |= CFG2_CTRLO; @@ -471,6 +470,25 @@ return error; } +__initfunc(static void +ether3_get_dev(struct device *dev, struct expansion_card *ec)) +{ + ecard_claim(ec); + + dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); + dev->irq = ec->irq; + + if (ec->cid.manufacturer == MANU_ANT && + ec->cid.product == PROD_ANT_ETHERB) { + dev->base_addr += 0x200; + } + + ec->irqaddr = (volatile unsigned char *)ioaddr(dev->base_addr); + ec->irqmask = 0xf0; + + ether3_addr(dev->dev_addr, ec); +} + #ifndef MODULE __initfunc(int ether3_probe(struct device *dev)) @@ -485,12 +503,8 @@ if ((ec = ecard_find(0, ether3_cids)) == NULL) return ENODEV; - dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); - dev->irq = ec->irq; - - ecard_claim(ec); + ether3_get_dev(dev, ec); - ether3_addr(dev->dev_addr, ec); return ether3_probe1(dev); } #endif @@ -581,33 +595,6 @@ } /* - * Allocate memory in transmitter ring buffer. - */ -static int -ether3_alloc_tx(struct dev_priv *priv, int length, int alloc) -{ - int start, head, tail; - - tail = priv->tx_tail; - start = priv->tx_head; - head = start + length + 4; - - if (head >= TX_END) { - if (tail > priv->tx_head) - return -1; - head -= TX_END - TX_START; - if (tail < head) - return -1; - } else if (start < tail && tail < head) - return -1; - - if (alloc) - priv->tx_head = head; - - return start; -} - -/* * Transmit a packet */ static int @@ -622,7 +609,7 @@ if (!test_and_set_bit(0, (void *)&dev->tbusy)) { unsigned long flags; unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - int ptr; + unsigned int ptr, next_ptr; length = (length + 1) & ~1; @@ -633,23 +620,31 @@ return 0; } + next_ptr = (priv->tx_head + 1) & 15; + save_flags_cli(flags); - ptr = ether3_alloc_tx(priv, length, 1); - if (ptr == -1) + if (priv->tx_tail == next_ptr) { + restore_flags(flags); return 1; /* unable to queue */ + } + + dev->trans_start = jiffies; + ptr = 0x600 * priv->tx_head; + priv->tx_head = next_ptr; + next_ptr *= 0x600; #define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS) - ether3_setbuffer(dev, buffer_write, priv->tx_head); + ether3_setbuffer(dev, buffer_write, next_ptr); ether3_writelong(dev, 0); - ether3_setbuffer(dev, buffer_write, ptr); ether3_writelong(dev, 0); ether3_writebuffer(dev, skb->data, length); - + ether3_writeword(dev, htons(next_ptr)); + ether3_writeword(dev, TXHDR_CHAINCONTINUE >> 16); ether3_setbuffer(dev, buffer_write, ptr); - ether3_writeword(dev, htons(priv->tx_head)); + ether3_writeword(dev, htons((ptr + length + 4))); ether3_writeword(dev, TXHDR_FLAGS >> 16); ether3_ledon(dev, priv); @@ -658,11 +653,10 @@ ether3_outw(priv->regs.command | CMD_TXON, REG_COMMAND); } - if (ether3_alloc_tx(priv, 2044, 0) != -1) + next_ptr = (priv->tx_head + 1) & 15; + if (priv->tx_tail != next_ptr) dev->tbusy = 0; - dev->trans_start = jiffies; - restore_flags(flags); dev_kfree_skb(skb); @@ -689,7 +683,7 @@ ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2)); printk(KERN_ERR "%s: { rpr=%04X rea=%04X tpr=%04X }\n", dev->name, ether3_inw(REG_RECVPTR), ether3_inw(REG_RECVEND), ether3_inw(REG_TRANSMITPTR)); - printk(KERN_ERR "%s: tx head=%04X tx tail=%04X\n", dev->name, + printk(KERN_ERR "%s: tx head=%X tx tail=%X\n", dev->name, priv->tx_head, priv->tx_tail); ether3_setbuffer(dev, buffer_read, priv->tx_tail); printk(KERN_ERR "%s: packet status = %08X\n", dev->name, ether3_readlong(dev)); @@ -698,8 +692,9 @@ dev->tbusy = 0; priv->regs.config2 |= CFG2_CTRLO; priv->stats.tx_errors += 1; - ether3_outw(priv->regs.config2 , REG_CONFIG2); + ether3_outw(priv->regs.config2, REG_CONFIG2); dev->trans_start = jiffies; + priv->tx_head = priv->tx_tail = 0; goto retry; } } @@ -867,6 +862,7 @@ ether3_tx(struct device *dev, struct dev_priv *priv) { unsigned int tx_tail = priv->tx_tail; + int max_work = 14; do { unsigned long status; @@ -874,7 +870,7 @@ /* * Read the packet header */ - ether3_setbuffer(dev, buffer_read, tx_tail); + ether3_setbuffer(dev, buffer_read, tx_tail * 0x600); status = ether3_readlong(dev); /* @@ -895,95 +891,72 @@ if (status & TXSTAT_BABBLED) priv->stats.tx_fifo_errors ++; } - tx_tail = htons(status & TX_NEXT); - if (tx_tail < TX_START || tx_tail >= TX_END) { - printk("%s: transmit error: next pointer = %04X\n", dev->name, tx_tail); - tx_tail = TX_START; - priv->tx_head = TX_START; - priv->tx_tail = TX_END; - } - } while (1); + tx_tail = (tx_tail + 1) & 15; + } while (--max_work); if (priv->tx_tail != tx_tail) { priv->tx_tail = tx_tail; - if (priv->tx_used <= MAX_TX_BUFFERED) { - dev->tbusy = 0; - mark_bh(NET_BH); /* Inform upper layers. */ - } + dev->tbusy = 0; + mark_bh(NET_BH); /* Inform upper layers. */ } } #ifdef MODULE -char ethernames[MAX_ECARDS][9]; - -static struct device *my_ethers[MAX_ECARDS]; -static struct expansion_card *ec[MAX_ECARDS]; +static struct ether_dev { + struct expansion_card *ec; + char name[9]; + struct device dev; +} ether_devs[MAX_ECARDS]; int init_module(void) { - int i; + struct expansion_card *ec; + int i, ret = -ENODEV; - for(i = 0; i < MAX_ECARDS; i++) { - my_ethers[i] = NULL; - ec[i] = NULL; - strcpy(ethernames[i], " "); - } + memset(ether_devs, 0, sizeof(ether_devs)); + ecard_startfind (); + ec = ecard_find(0, ether3_cids); i = 0; - ecard_startfind(); + while (ec && i < MAX_ECARDS) { + ecard_claim(ec); - do { - if ((ec[i] = ecard_find(0, ether3_cids)) == NULL) - break; - - my_ethers[i] = (struct device *)kmalloc(sizeof(struct device), GFP_KERNEL); - memset(my_ethers[i], 0, sizeof(struct device)); + ether_devs[i].ec = ec; + ether_devs[i].dev.init = ether3_probe1; + ether_devs[i].dev.name = ether_devs[i].name; + ether3_get_dev(ðer_devs[i].dev, ec); + + ret = register_netdev(ðer_devs[i].dev); + + if (ret) { + ecard_release(ec); + ether_devs[i].ec = NULL; + } else + i += 1; - my_ethers[i]->irq = ec[i]->irq; - my_ethers[i]->base_addr= ecard_address(ec[i], ECARD_MEMC, 0); - my_ethers[i]->init = ether3_probe1; - my_ethers[i]->name = ethernames[i]; - - ether3_addr(my_ethers[i]->dev_addr, ec[i]); - - ecard_claim(ec[i]); - - if(register_netdev(my_ethers[i]) != 0) { - for (i = 0; i < 4; i++) { - if(my_ethers[i]) { - kfree(my_ethers[i]); - my_ethers[i] = NULL; - } - if(ec[i]) { - ecard_release(ec[i]); - ec[i] = NULL; - } - } - return -EIO; - } - i++; + ec = ecard_find(0, ether3_cids); } - while(i < MAX_ECARDS); - return i != 0 ? 0 : -ENODEV; + return i != 0 ? 0 : ret; } void cleanup_module(void) { int i; + for (i = 0; i < MAX_ECARDS; i++) { - if (my_ethers[i]) { - release_region(my_ethers[i]->base_addr, 128); - unregister_netdev(my_ethers[i]); - my_ethers[i] = NULL; - } - if (ec[i]) { - ecard_release(ec[i]); - ec[i] = NULL; + if (ether_devs[i].ec) { + unregister_netdev(ðer_devs[i].dev); + + release_region(ether_devs[i].dev.base_addr, 128); + + ecard_release(ether_devs[i].ec); + + ether_devs[i].ec = NULL; } } } diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/net/ether3.h linux/drivers/acorn/net/ether3.h --- v2.3.6/linux/drivers/acorn/net/ether3.h Sun Sep 6 10:46:07 1998 +++ linux/drivers/acorn/net/ether3.h Thu Jun 17 01:11:35 1999 @@ -151,9 +151,8 @@ unsigned int config1; unsigned int config2; } regs; - unsigned int tx_head; /* address to insert next packet */ - unsigned int tx_tail; /* address of transmitting packet */ - unsigned int tx_used; /* number of 'slots' used */ + unsigned char tx_head; /* buffer nr to insert next packet */ + unsigned char tx_tail; /* buffer nr of transmitting packet */ unsigned int rx_head; /* address to fetch next packet from */ struct enet_statistics stats; struct timer_list timer; diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/net/etherh.c linux/drivers/acorn/net/etherh.c --- v2.3.6/linux/drivers/acorn/net/etherh.c Thu Dec 17 09:07:45 1998 +++ linux/drivers/acorn/net/etherh.c Thu Jun 17 01:11:35 1999 @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -50,13 +51,16 @@ #define DEBUG_INIT 2 static unsigned int net_debug = NET_DEBUG; -static const card_ids etherh_cids[] = { +static const card_ids __init etherh_cids[] = { { MANU_I3, PROD_I3_ETHERLAN500 }, { MANU_I3, PROD_I3_ETHERLAN600 }, { MANU_I3, PROD_I3_ETHERLAN600A }, { 0xffff, 0xffff } }; +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("i3 EtherH driver"); + static char *version = "etherh [500/600/600A] ethernet driver (c) 1998 R.M.King v1.05\n"; #define ETHERH500_DATAPORT 0x200 /* MEMC */ @@ -80,8 +84,8 @@ * Read the ethernet address string from the on board rom. * This is an ascii string... */ -static int -etherh_addr(char *addr, struct expansion_card *ec) +__initfunc(static int +etherh_addr(char *addr, struct expansion_card *ec)) { struct in_chunk_dir cd; char *s; @@ -216,10 +220,8 @@ if (ei_status.word16) outsw (dma_addr, buf, count >> 1); -#ifdef BIT8 else outsb (dma_addr, buf, count); -#endif dma_start = jiffies; @@ -268,11 +270,8 @@ insw (dma_addr, buf, count >> 1); if (count & 1) buf[count - 1] = inb (dma_addr); - } -#ifdef BIT8 - else + } else insb (dma_addr, buf, count); -#endif outb (ENISR_RDC, addr + EN0_ISR); ei_status.dmaing &= ~1; @@ -307,10 +306,8 @@ if (ei_status.word16) insw (dma_addr, hdr, sizeof (*hdr) >> 1); -#ifdef BIT8 else insb (dma_addr, hdr, sizeof (*hdr)); -#endif outb (ENISR_RDC, addr + EN0_ISR); ei_status.dmaing &= ~1; @@ -355,8 +352,8 @@ /* * This is the real probe routine. */ -static int -etherh_probe1(struct device *dev) +__initfunc(static int +etherh_probe1(struct device *dev)) { static int version_printed; unsigned int addr, i, reg0, tmp; @@ -461,10 +458,13 @@ etherh_irq_enable, etherh_irq_disable, NULL, + NULL, + NULL, NULL }; -static void etherh_initdev (ecard_t *ec, struct device *dev) +__initfunc(static void +etherh_initdev(ecard_t *ec, struct device *dev)) { ecard_claim (ec); @@ -492,27 +492,27 @@ } ec->ops = ðerh_ops; - etherh_addr (dev->dev_addr, ec); + etherh_addr(dev->dev_addr, ec); } #ifndef MODULE -int -etherh_probe(struct device *dev) +__initfunc(int +etherh_probe(struct device *dev)) { if (!dev) return ENODEV; - ecard_startfind (); - - if (!dev->base_addr) { + if (!dev->base_addr || dev->base_addr == 0xffe0) { struct expansion_card *ec; + ecard_startfind(); + if ((ec = ecard_find (0, etherh_cids)) == NULL) return ENODEV; - etherh_initdev (ec, dev); + etherh_initdev(ec, dev); } - return etherh_probe1 (dev); + return etherh_probe1(dev); } #endif @@ -529,12 +529,10 @@ init_all_cards(void) { struct device *dev = NULL; - struct expansion_card *boguscards[MAX_ETHERH_CARDS]; int i, found = 0; for (i = 0; i < MAX_ETHERH_CARDS; i++) { my_ethers[i] = NULL; - boguscards[i] = NULL; ec[i] = NULL; strcpy (ethernames[i], " "); } @@ -571,7 +569,7 @@ if (register_netdev(dev) != 0) { printk (KERN_WARNING "No etherh card found at %08lX\n", dev->base_addr); if (ec[i]) { - boguscards[i] = ec[i]; + ecard_release(ec[i]); ec[i] = NULL; } continue; @@ -582,12 +580,6 @@ if (dev) kfree (dev); - - for (i = 0; i < MAX_ETHERH_CARDS; i++) - if (boguscards[i]) { - boguscards[i]->ops = NULL; - ecard_release (boguscards[i]); - } return found ? 0 : -ENODEV; } diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/Config.in linux/drivers/acorn/scsi/Config.in --- v2.3.6/linux/drivers/acorn/scsi/Config.in Fri May 8 00:42:39 1998 +++ linux/drivers/acorn/scsi/Config.in Thu Jun 17 01:11:35 1999 @@ -7,11 +7,12 @@ bool ' Support SCSI 2 Synchronous Transfers' CONFIG_SCSI_ACORNSCSI_SYNC fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate 'ARXE SCSI support (Experimental)' CONFIG_SCSI_ARXESCSI $CONFIG_SCSI dep_tristate 'CumanaSCSI II support (Experimental)' CONFIG_SCSI_CUMANA_2 $CONFIG_SCSI dep_tristate 'EESOX support (Experimental)' CONFIG_SCSI_EESOXSCSI $CONFIG_SCSI dep_tristate 'PowerTec support (Experimental)' CONFIG_SCSI_POWERTECSCSI $CONFIG_SCSI - comment 'The following drives are not fully supported' + comment 'The following drivers are not fully supported' dep_tristate 'CumanaSCSI I support' CONFIG_SCSI_CUMANA_1 $CONFIG_SCSI if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/Makefile linux/drivers/acorn/scsi/Makefile --- v2.3.6/linux/drivers/acorn/scsi/Makefile Fri May 8 00:42:39 1998 +++ linux/drivers/acorn/scsi/Makefile Thu Jun 17 01:11:35 1999 @@ -24,6 +24,16 @@ endif endif +ifeq ($(CONFIG_SCSI_ARXESCSI),y) + L_OBJS += arxescsi.o + CONFIG_FAS216_BUILTIN=y +else + ifeq ($(CONFIG_SCSI_ARXESCSI),m) + M_OBJS += arxescsi.o + CONFIG_FAS216_MODULE=y + endif +endif + ifeq ($(CONFIG_SCSI_CUMANA_1),y) L_OBJS += cumana_1.o else @@ -34,12 +44,10 @@ ifeq ($(CONFIG_SCSI_CUMANA_2),y) L_OBJS += cumana_2.o - CONFIG_QUEUE_BUILTIN=y CONFIG_FAS216_BUILTIN=y else ifeq ($(CONFIG_SCSI_CUMANA_2),m) M_OBJS += cumana_2.o - CONFIG_QUEUE_MODULE=y CONFIG_FAS216_MODULE=y endif endif @@ -62,41 +70,39 @@ ifeq ($(CONFIG_SCSI_POWERTECSCSI),y) L_OBJS += powertec.o - CONFIG_QUEUE_BUILTIN=y CONFIG_FAS216_BUILTIN=y else ifeq ($(CONFIG_SCSI_POWERTECSCSI),m) M_OBJS += powertec.o - CONFIG_QUEUE_MODULE=y CONFIG_FAS216_MODULE=y endif endif ifeq ($(CONFIG_SCSI_EESOXSCSI),y) L_OBJS += eesox.o - CONFIG_QUEUE_BUILTIN=y CONFIG_FAS216_BUILTIN=y else ifeq ($(CONFIG_SCSI_EESOXSCSI),m) M_OBJS += eesox.o - CONFIG_QUEUE_MODULE=y CONFIG_FAS216_MODULE=y endif endif -ifeq ($(CONFIG_QUEUE_BUILTIN),y) - LX_OBJS += queue.o msgqueue.o -else - ifeq ($(CONFIG_QUEUE_MODULE),y) - MX_OBJS += queue.o msgqueue.o - endif -endif - ifeq ($(CONFIG_FAS216_BUILTIN),y) LX_OBJS += fas216.o + CONFIG_QUEUE_BUILTIN=y else ifeq ($(CONFIG_FAS216_MODULE),y) MX_OBJS += fas216.o + CONFIG_QUEUE_MODULE=y + endif +endif + +ifeq ($(CONFIG_QUEUE_BUILTIN),y) + LX_OBJS += queue.o msgqueue.o +else + ifeq ($(CONFIG_QUEUE_MODULE),y) + MX_OBJS += queue.o msgqueue.o endif endif diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/acornscsi.c linux/drivers/acorn/scsi/acornscsi.c --- v2.3.6/linux/drivers/acorn/scsi/acornscsi.c Wed Dec 23 09:44:41 1998 +++ linux/drivers/acorn/scsi/acornscsi.c Thu Jun 17 01:11:35 1999 @@ -21,6 +21,8 @@ * 12-Oct-1997 RMK Added catch for re-entering interrupt routine. * 15-Oct-1997 RMK Improved handling of commands. * 27-Jun-1998 RMK Changed asm/delay.h to linux/delay.h. + * 13-Dec-1998 RMK Better abort code and command handling. Extra state + * transitions added to allow dodgy devices to work. */ #define DEBUG_NO_WRITE 1 #define DEBUG_QUEUES 2 @@ -35,7 +37,7 @@ #define DEBUG_RESET 1024 #define DEBUG_ALL (DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\ DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\ - DEBUG_DMA|DEBUG_QUEUES|DEBUG_NO_WRITE) + DEBUG_DMA|DEBUG_QUEUES) /* DRIVER CONFIGURATION * @@ -123,8 +125,8 @@ #ifndef STRINGIFY #define STRINGIFY(x) #x #endif -#define STR(x) STRINGIFY(x) -#define NO_WRITE_STR STR(NO_WRITE) +#define STRx(x) STRINGIFY(x) +#define NO_WRITE_STR STRx(NO_WRITE) #include #include @@ -142,6 +144,7 @@ #include #include #include +#include #include #include "../../scsi/scsi.h" @@ -160,10 +163,6 @@ #error "Yippee! ABORT TAG is now defined! Remove this error!" #endif -#ifndef NO_IRQ -#define NO_IRQ 255 -#endif - #ifdef CONFIG_SCSI_ACORNSCSI_LINK #error SCSI2 LINKed commands not supported (yet)! #endif @@ -186,26 +185,7 @@ #define DMAC_BUFFER_SIZE 65536 #endif -/* - * This is used to dump the previous states of the SBIC - */ -static struct status_entry { - unsigned long when; - unsigned char ssr; - unsigned char ph; - unsigned char irq; - unsigned char unused; -} status[9][16]; -static unsigned char status_ptr[9]; - -#define ADD_STATUS(_q,_ssr,_ph,_irq) \ -({ \ - status[(_q)][status_ptr[(_q)]].when = jiffies; \ - status[(_q)][status_ptr[(_q)]].ssr = (_ssr); \ - status[(_q)][status_ptr[(_q)]].ph = (_ph); \ - status[(_q)][status_ptr[(_q)]].irq = (_irq); \ - status_ptr[(_q)] = (status_ptr[(_q)] + 1) & 15; \ -}) +#define STATUS_BUFFER_TO_PRINT 24 unsigned int sdtr_period = SDTR_PERIOD; unsigned int sdtr_size = SDTR_SIZE; @@ -214,31 +194,31 @@ PROC_SCSI_EATA, 9, "acornscsi", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; -static void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result); -static int acornscsi_reconnect_finish (AS_Host *host); -static void acornscsi_dma_cleanup (AS_Host *host); -static void acornscsi_abortcmd (AS_Host *host, unsigned char tag); +static void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result); +static int acornscsi_reconnect_finish(AS_Host *host); +static void acornscsi_dma_cleanup(AS_Host *host); +static void acornscsi_abortcmd(AS_Host *host, unsigned char tag); /* ==================================================================================== * Miscellaneous */ static inline void -sbic_arm_write (unsigned int io_port, int reg, int value) +sbic_arm_write(unsigned int io_port, int reg, int value) { - outb_t (reg, io_port); - outb_t (value, io_port + 4); + outb_t(reg, io_port); + outb_t(value, io_port + 4); } #define sbic_arm_writenext(io,val) \ - outb_t ((val), (io) + 4) + outb_t((val), (io) + 4) static inline -int sbic_arm_read (unsigned int io_port, int reg) +int sbic_arm_read(unsigned int io_port, int reg) { if(reg == ASR) return inl_t(io_port) & 255; - outb_t (reg, io_port); + outb_t(reg, io_port); return inl_t(io_port + 4) & 255; } @@ -247,129 +227,165 @@ #ifdef USE_DMAC #define dmac_read(io_port,reg) \ - inb ((io_port) + (reg)) + inb((io_port) + (reg)) #define dmac_write(io_port,reg,value) \ - ({ outb ((value), (io_port) + (reg)); }) + ({ outb((value), (io_port) + (reg)); }) #define dmac_clearintr(io_port) \ - ({ outb (0, (io_port)); }) + ({ outb(0, (io_port)); }) static inline -unsigned int dmac_address (unsigned int io_port) +unsigned int dmac_address(unsigned int io_port) { - return dmac_read (io_port, TXADRHI) << 16 | - dmac_read (io_port, TXADRMD) << 8 | - dmac_read (io_port, TXADRLO); + return dmac_read(io_port, TXADRHI) << 16 | + dmac_read(io_port, TXADRMD) << 8 | + dmac_read(io_port, TXADRLO); } static -void acornscsi_dumpdma (AS_Host *host, char *where) +void acornscsi_dumpdma(AS_Host *host, char *where) { unsigned int mode, addr, len; - mode = dmac_read (host->dma.io_port, MODECON); - addr = dmac_address (host->dma.io_port); - len = dmac_read (host->dma.io_port, TXCNTHI) << 8 | - dmac_read (host->dma.io_port, TXCNTLO); + mode = dmac_read(host->dma.io_port, MODECON); + addr = dmac_address(host->dma.io_port); + len = dmac_read(host->dma.io_port, TXCNTHI) << 8 | + dmac_read(host->dma.io_port, TXCNTLO); - printk ("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ", + printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ", host->host->host_no, where, mode, addr, (len + 1) & 0xffff, - dmac_read (host->dma.io_port, MASKREG)); + dmac_read(host->dma.io_port, MASKREG)); - printk ("DMA @%06x, ", host->dma.start_addr); - printk ("BH @%p +%04x, ", host->scsi.SCp.ptr, + printk("DMA @%06x, ", host->dma.start_addr); + printk("BH @%p +%04x, ", host->scsi.SCp.ptr, host->scsi.SCp.this_residual); - printk ("DT @+%04x ST @+%04x", host->dma.transferred, + printk("DT @+%04x ST @+%04x", host->dma.transferred, host->scsi.SCp.scsi_xferred); - printk ("\n"); + printk("\n"); } #endif static -unsigned long acornscsi_sbic_xfcount (AS_Host *host) +unsigned long acornscsi_sbic_xfcount(AS_Host *host) { unsigned long length; - length = sbic_arm_read (host->scsi.io_port, TRANSCNTH) << 16; - length |= sbic_arm_readnext (host->scsi.io_port) << 8; - length |= sbic_arm_readnext (host->scsi.io_port); + length = sbic_arm_read(host->scsi.io_port, TRANSCNTH) << 16; + length |= sbic_arm_readnext(host->scsi.io_port) << 8; + length |= sbic_arm_readnext(host->scsi.io_port); return length; } -static -int acornscsi_sbic_issuecmd (AS_Host *host, int command) +static int +acornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg) { - int asr; + int asr; - do { - asr = sbic_arm_read (host->scsi.io_port, ASR); - } while (asr & ASR_CIP); + do { + asr = sbic_arm_read(host->scsi.io_port, ASR); + + if ((asr & stat_mask) == stat) + return 0; + + udelay(1); + } while (--timeout); + + printk("scsi%d: timeout while %s\n", host->host->host_no, msg); + + return -1; +} - sbic_arm_write (host->scsi.io_port, CMND, command); +static +int acornscsi_sbic_issuecmd(AS_Host *host, int command) +{ + if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command")) + return -1; + + sbic_arm_write(host->scsi.io_port, CMND, command); return 0; } static void -acornscsi_csdelay (unsigned int cs) +acornscsi_csdelay(unsigned int cs) { unsigned long target_jiffies, flags; target_jiffies = jiffies + 1 + cs * HZ / 100; - save_flags (flags); - sti (); + save_flags(flags); + sti(); while (time_before(jiffies, target_jiffies)) barrier(); - restore_flags (flags); + restore_flags(flags); } static -void acornscsi_resetcard (AS_Host *host) +void acornscsi_resetcard(AS_Host *host) { - unsigned int i; + unsigned int i, timeout; /* assert reset line */ host->card.page_reg = 0x80; - outb (host->card.page_reg, host->card.io_page); + outb(host->card.page_reg, host->card.io_page); /* wait 3 cs. SCSI standard says 25ms. */ - acornscsi_csdelay (3); + acornscsi_csdelay(3); host->card.page_reg = 0; - outb (host->card.page_reg, host->card.io_page); + outb(host->card.page_reg, host->card.io_page); /* * Should get a reset from the card */ - while (!(inb (host->card.io_intr) & 8)); - sbic_arm_read (host->scsi.io_port, ASR); - sbic_arm_read (host->scsi.io_port, SSR); + timeout = 1000; + do { + if (inb(host->card.io_intr) & 8) + break; + udelay(1); + } while (--timeout); + + if (timeout == 0) + printk("scsi%d: timeout while resetting card\n", + host->host->host_no); + + sbic_arm_read(host->scsi.io_port, ASR); + sbic_arm_read(host->scsi.io_port, SSR); /* setup sbic - WD33C93A */ - sbic_arm_write (host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); - sbic_arm_write (host->scsi.io_port, CMND, CMND_RESET); + sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); + sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET); /* * Command should cause a reset interrupt */ - while (!(inb (host->card.io_intr) & 8)); - sbic_arm_read (host->scsi.io_port, ASR); - if (sbic_arm_read (host->scsi.io_port, SSR) != 0x01) - printk (KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n", + timeout = 1000; + do { + if (inb(host->card.io_intr) & 8) + break; + udelay(1); + } while (--timeout); + + if (timeout == 0) + printk("scsi%d: timeout while resetting card\n", host->host->host_no); - sbic_arm_write (host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); - sbic_arm_write (host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); - sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); - sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); + sbic_arm_read(host->scsi.io_port, ASR); + if (sbic_arm_read(host->scsi.io_port, SSR) != 0x01) + printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n", + host->host->host_no); + + sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); + sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); + sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); host->card.page_reg = 0x40; - outb (host->card.page_reg, host->card.io_page); + outb(host->card.page_reg, host->card.io_page); /* setup dmac - uPC71071 */ dmac_write(host->dma.io_port, INIT, 0); @@ -391,7 +407,7 @@ } /* wait 25 cs. SCSI standard says 250ms. */ - acornscsi_csdelay (25); + acornscsi_csdelay(25); } /*============================================================================================= @@ -461,80 +477,101 @@ }; static -void print_scsi_status (unsigned int ssr) +void print_scsi_status(unsigned int ssr) { if (acornscsi_map[ssr] != -1) - printk ("%s:%s", + printk("%s:%s", acornscsi_interrupttype[(ssr >> 4)], acornscsi_interruptcode[acornscsi_map[ssr]]); else - printk ("%X:%X", ssr >> 4, ssr & 0x0f); + printk("%X:%X", ssr >> 4, ssr & 0x0f); } #endif static -void print_sbic_status (int asr, int ssr, int cmdphase) +void print_sbic_status(int asr, int ssr, int cmdphase) { #ifdef CONFIG_ACORNSCSI_CONSTANTS - printk ("sbic: %c%c%c%c%c%c ", + printk("sbic: %c%c%c%c%c%c ", asr & ASR_INT ? 'I' : 'i', asr & ASR_LCI ? 'L' : 'l', asr & ASR_BSY ? 'B' : 'b', asr & ASR_CIP ? 'C' : 'c', asr & ASR_PE ? 'P' : 'p', asr & ASR_DBR ? 'D' : 'd'); - printk ("scsi: "); - print_scsi_status (ssr); - printk (" ph %02X\n", cmdphase); + printk("scsi: "); + print_scsi_status(ssr); + printk(" ph %02X\n", cmdphase); #else - printk ("sbic: %02X scsi: %X:%X ph: %02X\n", + printk("sbic: %02X scsi: %X:%X ph: %02X\n", asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase); #endif } -static -void acornscsi_dumplog (AS_Host *host, int target) +static void +acornscsi_dumplogline(AS_Host *host, int target, int line) { - unsigned int prev; - do { - signed int statptr; + unsigned long prev; + signed int ptr; - printk ("%c:", target == 8 ? 'H' : ('0' + target)); - statptr = status_ptr[target] - 10; + ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT; + if (ptr < 0) + ptr += STATUS_BUFFER_SIZE; - if (statptr < 0) - statptr += 16; + printk("%c: %3s:", target == 8 ? 'H' : '0' + target, + line == 0 ? "ph" : line == 1 ? "ssr" : "int"); - prev = status[target][statptr].when; + prev = host->status[target][ptr].when; - for (; statptr != status_ptr[target]; statptr = (statptr + 1) & 15) { - if (status[target][statptr].when) { -#ifdef CONFIG_ACORNSCSI_CONSTANTS - printk ("%c%02X:S=", - status[target][statptr].irq ? '-' : ' ', - status[target][statptr].ph); - print_scsi_status (status[target][statptr].ssr); -#else - printk ("%c%02X:%02X", - status[target][statptr].irq ? '-' : ' ', - status[target][statptr].ph, - status[target][statptr].ssr); -#endif - printk ("+%02ld", - (status[target][statptr].when - prev) < 100 ? - (status[target][statptr].when - prev) : 99); - prev = status[target][statptr].when; - } + for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) { + unsigned long time_diff; + + if (!host->status[target][ptr].when) + continue; + + switch (line) { + case 0: + printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ', + host->status[target][ptr].ph); + break; + + case 1: + printk(" %02X", host->status[target][ptr].ssr); + break; + + case 2: + time_diff = host->status[target][ptr].when - prev; + prev = host->status[target][ptr].when; + if (time_diff == 0) + printk("==^"); + else if (time_diff >= 100) + printk(" "); + else + printk(" %02ld", time_diff); + break; + } } - printk ("\n"); + + printk("\n"); +} + +static +void acornscsi_dumplog(AS_Host *host, int target) +{ + do { + acornscsi_dumplogline(host, target, 0); + acornscsi_dumplogline(host, target, 1); + acornscsi_dumplogline(host, target, 2); + if (target == 8) break; + target = 8; } while (1); } static -char acornscsi_target (AS_Host *host) +char acornscsi_target(AS_Host *host) { if (host->SCpnt) return '0' + host->SCpnt->target; @@ -542,7 +579,7 @@ } /* - * Prototype: cmdtype_t acornscsi_cmdtype (int command) + * Prototype: cmdtype_t acornscsi_cmdtype(int command) * Purpose : differentiate READ from WRITE from other commands * Params : command - command to interpret * Returns : CMD_READ - command reads data, @@ -550,7 +587,7 @@ * CMD_MISC - everything else */ static inline -cmdtype_t acornscsi_cmdtype (int command) +cmdtype_t acornscsi_cmdtype(int command) { switch (command) { case WRITE_6: case WRITE_10: case WRITE_12: @@ -563,7 +600,7 @@ } /* - * Prototype: int acornscsi_datadirection (int command) + * Prototype: int acornscsi_datadirection(int command) * Purpose : differentiate between commands that have a DATA IN phase * and a DATA OUT phase * Params : command - command to interpret @@ -571,7 +608,7 @@ * DATADIR_IN - data in phase expected */ static -datadir_t acornscsi_datadirection (int command) +datadir_t acornscsi_datadirection(int command) { switch (command) { case CHANGE_DEFINITION: case COMPARE: case COPY: @@ -605,13 +642,13 @@ }; /* - * Prototype: int acornscsi_getperiod (unsigned char syncxfer) + * Prototype: int acornscsi_getperiod(unsigned char syncxfer) * Purpose : period for the synchronous transfer setting * Params : syncxfer SYNCXFER register value * Returns : period in ns. */ static -int acornscsi_getperiod (unsigned char syncxfer) +int acornscsi_getperiod(unsigned char syncxfer) { int i; @@ -626,14 +663,14 @@ } /* - * Prototype: int round_period (unsigned int period) + * Prototype: int round_period(unsigned int period) * Purpose : return index into above table for a required REQ period * Params : period - time (ns) for REQ * Returns : table index * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting */ static inline -int round_period (unsigned int period) +int round_period(unsigned int period) { int i; @@ -646,7 +683,7 @@ } /* - * Prototype: unsigned char calc_sync_xfer (unsigned int period, unsigned int offset) + * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) * Purpose : calculate value for 33c93s SYNC register * Params : period - time (ns) for REQ * offset - offset in bytes between REQ/ACK @@ -654,7 +691,7 @@ * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting */ static -unsigned char calc_sync_xfer (unsigned int period, unsigned int offset) +unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) { return sync_xfer_table[round_period(period)].reg_value | ((offset < SDTR_SIZE) ? offset : SDTR_SIZE); @@ -664,14 +701,14 @@ * Command functions */ /* - * Function: acornscsi_kick (AS_Host *host) + * Function: acornscsi_kick(AS_Host *host) * Purpose : kick next command to interface * Params : host - host to send command to * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING * Notes : interrupts are always disabled! */ static -intr_ret_t acornscsi_kick (AS_Host *host) +intr_ret_t acornscsi_kick(AS_Host *host) { int from_queue = 0; Scsi_Cmnd *SCpnt; @@ -682,7 +719,7 @@ /* retrieve next command */ if (!SCpnt) { - SCpnt = queue_remove_exclude (&host->queues.issue, host->busyluns); + SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns); if (!SCpnt) return INTR_IDLE; @@ -690,11 +727,11 @@ } if (host->scsi.disconnectable && host->SCpnt) { - queue_add_cmd_tail (&host->queues.disconnected, host->SCpnt); + queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); host->scsi.disconnectable = 0; #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk ("scsi%d.%c: moved command to disconnected queue\n", - host->host->host_no, acornscsi_target (host))); + DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n", + host->host->host_no, acornscsi_target(host))); #endif host->SCpnt = NULL; } @@ -703,9 +740,9 @@ * If we have an interrupt pending, then we may have been reselected. * In this case, we don't want to write to the registers */ - if (!(sbic_arm_read (host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) { - sbic_arm_write (host->scsi.io_port, DESTID, SCpnt->target); - sbic_arm_write (host->scsi.io_port, CMND, CMND_SELWITHATN); + if (!(sbic_arm_read(host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) { + sbic_arm_write(host->scsi.io_port, DESTID, SCpnt->target); + sbic_arm_write(host->scsi.io_port, CMND, CMND_SELWITHATN); } /* @@ -717,9 +754,10 @@ host->scsi.SCp = SCpnt->SCp; host->dma.xfer_setup = 0; host->dma.xfer_required = 0; + host->dma.xfer_done = 0; #if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT)) - DBG(SCpnt,printk ("scsi%d.%c: starting cmd %02X\n", + DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n", host->host->host_no, '0' + SCpnt->target, SCpnt->cmnd[0])); #endif @@ -736,11 +774,11 @@ SCpnt->tag = SCpnt->device->current_tag; } else #endif - set_bit (SCpnt->target * 8 + SCpnt->lun, host->busyluns); + set_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns); host->stats.removes += 1; - switch (acornscsi_cmdtype (SCpnt->cmnd[0])) { + switch (acornscsi_cmdtype(SCpnt->cmnd[0])) { case CMD_WRITE: host->stats.writes += 1; break; @@ -757,25 +795,25 @@ } /* - * Function: void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) + * Function: void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) * Purpose : complete processing for command * Params : host - interface that completed * result - driver byte of result */ static -void acornscsi_done (AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) +void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) { Scsi_Cmnd *SCpnt = *SCpntp; /* clean up */ - sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); + sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); host->stats.fins += 1; if (SCpnt) { *SCpntp = NULL; - acornscsi_dma_cleanup (host); + acornscsi_dma_cleanup(host); SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status; @@ -787,35 +825,63 @@ * It doesn't appear to be set to something meaningful by the higher * levels all the time. */ - if (host->scsi.SCp.ptr && result == DID_OK && - acornscsi_cmdtype (SCpnt->cmnd[0]) != CMD_MISC) { - switch (status_byte (SCpnt->result)) { - case CHECK_CONDITION: - case COMMAND_TERMINATED: - case BUSY: - case QUEUE_FULL: - case RESERVATION_CONFLICT: - break; + if (result == DID_OK) { + int xfer_warn = 0; - default: - printk (KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=", - host->host->host_no, SCpnt->result); - print_command (SCpnt->cmnd); - acornscsi_dumpdma (host, "done"); - acornscsi_dumplog (host, SCpnt->target); - SCpnt->result &= 0xffff; - SCpnt->result |= DID_ERROR << 16; - } + if (SCpnt->underflow == 0) { + if (host->scsi.SCp.ptr && + acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC) + xfer_warn = 1; + } else { + if (host->scsi.SCp.scsi_xferred < SCpnt->underflow || + host->scsi.SCp.scsi_xferred != host->dma.transferred) + xfer_warn = 1; + } + + /* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6) + * Targets which break data transfers into multiple + * connections shall end each successful connection + * (except possibly the last) with a SAVE DATA + * POINTER - DISCONNECT message sequence. + * + * This makes it difficult to ensure that a transfer has + * completed. If we reach the end of a transfer during + * the command, then we can only have finished the transfer. + * therefore, if we seem to have some data remaining, this + * is not a problem. + */ + if (host->dma.xfer_done) + xfer_warn = 0; + + if (xfer_warn) { + switch (status_byte(SCpnt->result)) { + case CHECK_CONDITION: + case COMMAND_TERMINATED: + case BUSY: + case QUEUE_FULL: + case RESERVATION_CONFLICT: + break; + + default: + printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=", + host->host->host_no, SCpnt->result); + print_command(SCpnt->cmnd); + acornscsi_dumpdma(host, "done"); + acornscsi_dumplog(host, SCpnt->target); + SCpnt->result &= 0xffff; + SCpnt->result |= DID_ERROR << 16; + } + } } if (!SCpnt->scsi_done) - panic ("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no); + panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no); - clear_bit (SCpnt->target * 8 + SCpnt->lun, host->busyluns); + clear_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns); - SCpnt->scsi_done (SCpnt); + SCpnt->scsi_done(SCpnt); } else - printk ("scsi%d: null command in acornscsi_done", host->host->host_no); + printk("scsi%d: null command in acornscsi_done", host->host->host_no); host->scsi.phase = PHASE_IDLE; } @@ -828,7 +894,7 @@ * Notes : this will only be one SG entry or less */ static -void acornscsi_data_updateptr (AS_Host *host, Scsi_Pointer *SCp, unsigned int length) +void acornscsi_data_updateptr(AS_Host *host, Scsi_Pointer *SCp, unsigned int length) { SCp->ptr += length; SCp->this_residual -= length; @@ -839,13 +905,15 @@ SCp->buffers_residual--; SCp->ptr = (char *)SCp->buffer->address; SCp->this_residual = SCp->buffer->length; - } else + } else { SCp->ptr = NULL; + host->dma.xfer_done = 1; + } } } /* - * Prototype: void acornscsi_data_read (AS_Host *host, char *ptr, + * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr, * unsigned int start_addr, unsigned int length) * Purpose : read data from DMA RAM * Params : host - host to transfer from @@ -855,16 +923,16 @@ * Notes : this will only be one SG entry or less */ static -void acornscsi_data_read (AS_Host *host, char *ptr, +void acornscsi_data_read(AS_Host *host, char *ptr, unsigned int start_addr, unsigned int length) { - extern void __acornscsi_in (int port, char *buf, int len); + extern void __acornscsi_in(int port, char *buf, int len); unsigned int page, offset, len = length; page = (start_addr >> 12); offset = start_addr & ((1 << 12) - 1); - outb ((page & 0x3f) | host->card.page_reg, host->card.io_page); + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); while (len > 0) { unsigned int this_len; @@ -874,7 +942,7 @@ else this_len = len; - __acornscsi_in (host->card.io_ram + (offset << 1), ptr, this_len); + __acornscsi_in(host->card.io_ram + (offset << 1), ptr, this_len); offset += this_len; ptr += this_len; @@ -883,14 +951,14 @@ if (offset == (1 << 12)) { offset = 0; page ++; - outb ((page & 0x3f) | host->card.page_reg, host->card.io_page); + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); } } - outb (host->card.page_reg, host->card.io_page); + outb(host->card.page_reg, host->card.io_page); } /* - * Prototype: void acornscsi_data_write (AS_Host *host, char *ptr, + * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr, * unsigned int start_addr, unsigned int length) * Purpose : write data to DMA RAM * Params : host - host to transfer from @@ -900,16 +968,16 @@ * Notes : this will only be one SG entry or less */ static -void acornscsi_data_write (AS_Host *host, char *ptr, +void acornscsi_data_write(AS_Host *host, char *ptr, unsigned int start_addr, unsigned int length) { - extern void __acornscsi_out (int port, char *buf, int len); + extern void __acornscsi_out(int port, char *buf, int len); unsigned int page, offset, len = length; page = (start_addr >> 12); offset = start_addr & ((1 << 12) - 1); - outb ((page & 0x3f) | host->card.page_reg, host->card.io_page); + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); while (len > 0) { unsigned int this_len; @@ -919,7 +987,7 @@ else this_len = len; - __acornscsi_out (host->card.io_ram + (offset << 1), ptr, this_len); + __acornscsi_out(host->card.io_ram + (offset << 1), ptr, this_len); offset += this_len; ptr += this_len; @@ -928,10 +996,10 @@ if (offset == (1 << 12)) { offset = 0; page ++; - outb ((page & 0x3f) | host->card.page_reg, host->card.io_page); + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); } } - outb (host->card.page_reg, host->card.io_page); + outb(host->card.page_reg, host->card.io_page); } /* ========================================================================================= @@ -939,25 +1007,25 @@ */ #ifdef USE_DMAC /* - * Prototype: void acornscsi_dmastop (AS_Host *host) + * Prototype: void acornscsi_dmastop(AS_Host *host) * Purpose : stop all DMA * Params : host - host on which to stop DMA * Notes : This is called when leaving DATA IN/OUT phase, * or when interface is RESET */ static inline -void acornscsi_dma_stop (AS_Host *host) +void acornscsi_dma_stop(AS_Host *host) { - dmac_write (host->dma.io_port, MASKREG, MASK_ON); - dmac_clearintr (host->dma.io_intr_clear); + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + dmac_clearintr(host->dma.io_intr_clear); #if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma (host, "stop")); + DBG(host->SCpnt, acornscsi_dumpdma(host, "stop")); #endif } /* - * Function: void acornscsi_dma_setup (AS_Host *host, dmadir_t direction) + * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) * Purpose : setup DMA controller for data transfer * Params : host - host to setup * direction - data transfer direction @@ -965,19 +1033,19 @@ * while we're in a DATA I/O phase */ static -void acornscsi_dma_setup (AS_Host *host, dmadir_t direction) +void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) { unsigned int address, length, mode; host->dma.direction = direction; - dmac_write (host->dma.io_port, MASKREG, MASK_ON); + dmac_write(host->dma.io_port, MASKREG, MASK_ON); if (direction == DMA_OUT) { #if (DEBUG & DEBUG_NO_WRITE) if (NO_WRITE & (1 << host->SCpnt->target)) { - printk (KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n", - host->host->host_no, acornscsi_target (host)); + printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n", + host->host->host_no, acornscsi_target(host)); return; } #endif @@ -988,7 +1056,7 @@ /* * Allocate some buffer space, limited to half the buffer size */ - length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); + length = min(host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); if (length) { host->dma.start_addr = address = host->dma.free_addr; host->dma.free_addr = (host->dma.free_addr + length) & @@ -998,27 +1066,27 @@ * Transfer data to DMA memory */ if (direction == DMA_OUT) - acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr, + acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, length); length -= 1; - dmac_write (host->dma.io_port, TXCNTLO, length); - dmac_write (host->dma.io_port, TXCNTHI, length >> 8); - dmac_write (host->dma.io_port, TXADRLO, address); - dmac_write (host->dma.io_port, TXADRMD, address >> 8); - dmac_write (host->dma.io_port, TXADRHI, 0); - dmac_write (host->dma.io_port, MODECON, mode); - dmac_write (host->dma.io_port, MASKREG, MASK_OFF); + dmac_write(host->dma.io_port, TXCNTLO, length); + dmac_write(host->dma.io_port, TXCNTHI, length >> 8); + dmac_write(host->dma.io_port, TXADRLO, address); + dmac_write(host->dma.io_port, TXADRMD, address >> 8); + dmac_write(host->dma.io_port, TXADRHI, 0); + dmac_write(host->dma.io_port, MODECON, mode); + dmac_write(host->dma.io_port, MASKREG, MASK_OFF); #if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma (host, "strt")); + DBG(host->SCpnt, acornscsi_dumpdma(host, "strt")); #endif host->dma.xfer_setup = 1; } } /* - * Function: void acornscsi_dma_cleanup (AS_Host *host) + * Function: void acornscsi_dma_cleanup(AS_Host *host) * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct * Params : host - host to finish * Notes : This is called when a command is: @@ -1026,10 +1094,10 @@ * : This must not return until all transfers are completed. */ static -void acornscsi_dma_cleanup (AS_Host *host) +void acornscsi_dma_cleanup(AS_Host *host) { - dmac_write (host->dma.io_port, MASKREG, MASK_ON); - dmac_clearintr (host->dma.io_intr_clear); + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + dmac_clearintr(host->dma.io_intr_clear); /* * Check for a pending transfer @@ -1037,7 +1105,7 @@ if (host->dma.xfer_required) { host->dma.xfer_required = 0; if (host->dma.direction == DMA_IN) - acornscsi_data_read (host, host->dma.xfer_ptr, + acornscsi_data_read(host, host->dma.xfer_ptr, host->dma.xfer_start, host->dma.xfer_length); } @@ -1056,17 +1124,17 @@ /* * Calculate number of bytes transferred from DMA. */ - transferred = dmac_address (host->dma.io_port) - host->dma.start_addr; + transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; host->dma.transferred += transferred; if (host->dma.direction == DMA_IN) - acornscsi_data_read (host, host->scsi.SCp.ptr, + acornscsi_data_read(host, host->scsi.SCp.ptr, host->dma.start_addr, transferred); /* * Update SCSI pointers */ - acornscsi_data_updateptr (host, &host->scsi.SCp, transferred); + acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); #if (DEBUG & DEBUG_DMA) DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo")); #endif @@ -1074,7 +1142,7 @@ } /* - * Function: void acornscsi_dmacintr (AS_Host *host) + * Function: void acornscsi_dmacintr(AS_Host *host) * Purpose : handle interrupts from DMAC device * Params : host - host to process * Notes : If reading, we schedule the read to main memory & @@ -1084,21 +1152,21 @@ * : Called whenever DMAC finished it's current transfer. */ static -void acornscsi_dma_intr (AS_Host *host) +void acornscsi_dma_intr(AS_Host *host) { unsigned int address, length, transferred; #if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma (host, "inti")); + DBG(host->SCpnt, acornscsi_dumpdma(host, "inti")); #endif - dmac_write (host->dma.io_port, MASKREG, MASK_ON); - dmac_clearintr (host->dma.io_intr_clear); + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + dmac_clearintr(host->dma.io_intr_clear); /* * Calculate amount transferred via DMA */ - transferred = dmac_address (host->dma.io_port) - host->dma.start_addr; + transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; host->dma.transferred += transferred; /* @@ -1111,12 +1179,12 @@ host->dma.xfer_required = 1; } - acornscsi_data_updateptr (host, &host->scsi.SCp, transferred); + acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); /* * Allocate some buffer space, limited to half the on-board RAM size */ - length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); + length = min(host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); if (length) { host->dma.start_addr = address = host->dma.free_addr; host->dma.free_addr = (host->dma.free_addr + length) & @@ -1126,19 +1194,19 @@ * Transfer data to DMA memory */ if (host->dma.direction == DMA_OUT) - acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr, + acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, length); length -= 1; - dmac_write (host->dma.io_port, TXCNTLO, length); - dmac_write (host->dma.io_port, TXCNTHI, length >> 8); - dmac_write (host->dma.io_port, TXADRLO, address); - dmac_write (host->dma.io_port, TXADRMD, address >> 8); - dmac_write (host->dma.io_port, TXADRHI, 0); - dmac_write (host->dma.io_port, MASKREG, MASK_OFF); + dmac_write(host->dma.io_port, TXCNTLO, length); + dmac_write(host->dma.io_port, TXCNTHI, length >> 8); + dmac_write(host->dma.io_port, TXADRLO, address); + dmac_write(host->dma.io_port, TXADRMD, address >> 8); + dmac_write(host->dma.io_port, TXADRHI, 0); + dmac_write(host->dma.io_port, MASKREG, MASK_OFF); #if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma (host, "into")); + DBG(host->SCpnt, acornscsi_dumpdma(host, "into")); #endif } else { host->dma.xfer_setup = 0; @@ -1149,48 +1217,48 @@ * attention condition. We continue giving one byte until * the device recognises the attention. */ - if (dmac_read (host->dma.io_port, STATUS) & STATUS_RQ0) { - acornscsi_abortcmd (host, host->SCpnt->tag); + if (dmac_read(host->dma.io_port, STATUS) & STATUS_RQ0) { + acornscsi_abortcmd(host, host->SCpnt->tag); - dmac_write (host->dma.io_port, TXCNTLO, 0); - dmac_write (host->dma.io_port, TXCNTHI, 0); - dmac_write (host->dma.io_port, TXADRLO, 0); - dmac_write (host->dma.io_port, TXADRMD, 0); - dmac_write (host->dma.io_port, TXADRHI, 0); - dmac_write (host->dma.io_port, MASKREG, MASK_OFF); + dmac_write(host->dma.io_port, TXCNTLO, 0); + dmac_write(host->dma.io_port, TXCNTHI, 0); + dmac_write(host->dma.io_port, TXADRLO, 0); + dmac_write(host->dma.io_port, TXADRMD, 0); + dmac_write(host->dma.io_port, TXADRHI, 0); + dmac_write(host->dma.io_port, MASKREG, MASK_OFF); } #endif } } /* - * Function: void acornscsi_dma_xfer (AS_Host *host) + * Function: void acornscsi_dma_xfer(AS_Host *host) * Purpose : transfer data between AcornSCSI and memory * Params : host - host to process */ static -void acornscsi_dma_xfer (AS_Host *host) +void acornscsi_dma_xfer(AS_Host *host) { host->dma.xfer_required = 0; if (host->dma.direction == DMA_IN) - acornscsi_data_read (host, host->dma.xfer_ptr, + acornscsi_data_read(host, host->dma.xfer_ptr, host->dma.xfer_start, host->dma.xfer_length); } /* - * Function: void acornscsi_dma_adjust (AS_Host *host) + * Function: void acornscsi_dma_adjust(AS_Host *host) * Purpose : adjust DMA pointers & count for bytes transfered to * SBIC but not SCSI bus. * Params : host - host to adjust DMA count for */ static -void acornscsi_dma_adjust (AS_Host *host) +void acornscsi_dma_adjust(AS_Host *host) { if (host->dma.xfer_setup) { signed long transferred; #if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) - DBG(host->SCpnt, acornscsi_dumpdma (host, "adji")); + DBG(host->SCpnt, acornscsi_dumpdma(host, "adji")); #endif /* * Calculate correct DMA address - DMA is ahead of SCSI bus while @@ -1205,17 +1273,17 @@ */ transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred; if (transferred < 0) - printk ("scsi%d.%c: Ack! DMA write correction %ld < 0!\n", - host->host->host_no, acornscsi_target (host), transferred); + printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n", + host->host->host_no, acornscsi_target(host), transferred); else if (transferred == 0) host->dma.xfer_setup = 0; else { transferred += host->dma.start_addr; - dmac_write (host->dma.io_port, TXADRLO, transferred); - dmac_write (host->dma.io_port, TXADRMD, transferred >> 8); - dmac_write (host->dma.io_port, TXADRHI, transferred >> 16); + dmac_write(host->dma.io_port, TXADRLO, transferred); + dmac_write(host->dma.io_port, TXADRMD, transferred >> 8); + dmac_write(host->dma.io_port, TXADRHI, transferred >> 16); #if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) - DBG(host->SCpnt, acornscsi_dumpdma (host, "adjo")); + DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo")); #endif } } @@ -1225,66 +1293,88 @@ /* ========================================================================================= * Data I/O */ +static int +acornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout) +{ + unsigned int asr, timeout = max_timeout; + int my_ptr = *ptr; + + while (my_ptr < len) { + asr = sbic_arm_read(host->scsi.io_port, ASR); + + if (asr & ASR_DBR) { + timeout = max_timeout; + + sbic_arm_write(host->scsi.io_port, DATA, bytes[my_ptr++]); + } else if (asr & ASR_INT) + break; + else if (--timeout == 0) + break; + udelay(1); + } + + *ptr = my_ptr; + + return (timeout == 0) ? -1 : 0; +} + /* - * Function: void acornscsi_sendcommand (AS_Host *host) + * Function: void acornscsi_sendcommand(AS_Host *host) * Purpose : send a command to a target * Params : host - host which is connected to target */ -static -void acornscsi_sendcommand (AS_Host *host) +static void +acornscsi_sendcommand(AS_Host *host) { Scsi_Cmnd *SCpnt = host->SCpnt; - unsigned int asr; - unsigned char *cmdptr, *cmdend; - sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0); - sbic_arm_writenext (host->scsi.io_port, 0); - sbic_arm_writenext (host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command); - acornscsi_sbic_issuecmd (host, CMND_XFERINFO); - - cmdptr = SCpnt->cmnd + host->scsi.SCp.sent_command; - cmdend = SCpnt->cmnd + SCpnt->cmd_len; - - while (cmdptr < cmdend) { - asr = sbic_arm_read (host->scsi.io_port, ASR); - if (asr & ASR_DBR) - sbic_arm_write (host->scsi.io_port, DATA, *cmdptr++); - else if (asr & ASR_INT) - break; - } - if (cmdptr >= cmdend) - host->scsi.SCp.sent_command = cmdptr - SCpnt->cmnd; + sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0); + sbic_arm_writenext(host->scsi.io_port, 0); + sbic_arm_writenext(host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command); + + acornscsi_sbic_issuecmd(host, CMND_XFERINFO); + + if (acornscsi_write_pio(host, SCpnt->cmnd, + (int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000)) + printk("scsi%d: timeout while sending command\n", host->host->host_no); + host->scsi.phase = PHASE_COMMAND; } static -void acornscsi_sendmessage (AS_Host *host) +void acornscsi_sendmessage(AS_Host *host) { - unsigned int message_length = msgqueue_msglength (&host->scsi.msgs); - int msgnr; + unsigned int message_length = msgqueue_msglength(&host->scsi.msgs); + unsigned int msgnr; struct message *msg; #if (DEBUG & DEBUG_MESSAGES) - printk ("scsi%d.%c: sending message ", - host->host->host_no, acornscsi_target (host)); + printk("scsi%d.%c: sending message ", + host->host->host_no, acornscsi_target(host)); #endif switch (message_length) { case 0: - acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT); - while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0); - sbic_arm_write (host->scsi.io_port, DATA, NOP); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); + + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1"); + + sbic_arm_write(host->scsi.io_port, DATA, NOP); + host->scsi.last_message = NOP; #if (DEBUG & DEBUG_MESSAGES) - printk ("NOP"); + printk("NOP"); #endif break; case 1: - acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); msg = msgqueue_getmsg(&host->scsi.msgs, 0); - while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0); - sbic_arm_write (host->scsi.io_port, DATA, msg->msg[0]); + + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2"); + + sbic_arm_write(host->scsi.io_port, DATA, msg->msg[0]); + host->scsi.last_message = msg->msg[0]; #if (DEBUG & DEBUG_MESSAGES) print_msg(msg->msg); @@ -1300,86 +1390,85 @@ * initiator. This provides an interlock so that the * initiator can determine which message byte is rejected. */ - sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0); - sbic_arm_writenext (host->scsi.io_port, 0); - sbic_arm_writenext (host->scsi.io_port, message_length); - acornscsi_sbic_issuecmd (host, CMND_XFERINFO); + sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0); + sbic_arm_writenext(host->scsi.io_port, 0); + sbic_arm_writenext(host->scsi.io_port, message_length); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO); msgnr = 0; while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) { - unsigned int asr, i; + unsigned int i; #if (DEBUG & DEBUG_MESSAGES) - print_msg (msg); + print_msg(msg); #endif - for (i = 0; i < msg->length;) { - asr = sbic_arm_read (host->scsi.io_port, ASR); - if (asr & ASR_DBR) - sbic_arm_write (host->scsi.io_port, DATA, msg->msg[i++]); - if (asr & ASR_INT) - break; - } + i = 0; + if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000)) + printk("scsi%d: timeout while sending message\n", host->host->host_no); + host->scsi.last_message = msg->msg[0]; if (msg->msg[0] == EXTENDED_MESSAGE) host->scsi.last_message |= msg->msg[2] << 8; - if (asr & ASR_INT) + + if (i != msg->length) break; } break; } #if (DEBUG & DEBUG_MESSAGES) - printk ("\n"); + printk("\n"); #endif } /* - * Function: void acornscsi_readstatusbyte (AS_Host *host) + * Function: void acornscsi_readstatusbyte(AS_Host *host) * Purpose : Read status byte from connected target * Params : host - host connected to target */ static -void acornscsi_readstatusbyte (AS_Host *host) +void acornscsi_readstatusbyte(AS_Host *host) { - acornscsi_sbic_issuecmd (host, CMND_XFERINFO|CMND_SBT); - while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0); - - host->scsi.SCp.Status = sbic_arm_read (host->scsi.io_port, DATA); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT); + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte"); + host->scsi.SCp.Status = sbic_arm_read(host->scsi.io_port, DATA); } /* - * Function: unsigned char acornscsi_readmessagebyte (AS_Host *host) + * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host) * Purpose : Read one message byte from connected target * Params : host - host connected to target */ static -unsigned char acornscsi_readmessagebyte (AS_Host *host) +unsigned char acornscsi_readmessagebyte(AS_Host *host) { unsigned char message; - acornscsi_sbic_issuecmd (host, CMND_XFERINFO | CMND_SBT); - while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); + + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte"); - message = sbic_arm_read (host->scsi.io_port, DATA); + message = sbic_arm_read(host->scsi.io_port, DATA); /* wait for MSGIN-XFER-PAUSED */ - while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_INT) == 0); - sbic_arm_read (host->scsi.io_port, SSR); + acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte"); + + sbic_arm_read(host->scsi.io_port, SSR); return message; } /* - * Function: void acornscsi_message (AS_Host *host) + * Function: void acornscsi_message(AS_Host *host) * Purpose : Read complete message from connected target & action message * Params : host - host connected to target */ static -void acornscsi_message (AS_Host *host) +void acornscsi_message(AS_Host *host) { unsigned char message[16]; unsigned int msgidx = 0, msglen = 1; do { - message[msgidx] = acornscsi_readmessagebyte (host); + message[msgidx] = acornscsi_readmessagebyte(host); switch (msgidx) { case 0: @@ -1395,17 +1484,17 @@ } msgidx += 1; if (msgidx < msglen) { - acornscsi_sbic_issuecmd (host, CMND_NEGATEACK); + acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); /* wait for next msg-in */ - while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_INT) == 0); - sbic_arm_read (host->scsi.io_port, SSR); + acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack"); + sbic_arm_read(host->scsi.io_port, SSR); } } while (msgidx < msglen); #if (DEBUG & DEBUG_MESSAGES) printk("scsi%d.%c: message in: ", - host->host->host_no, acornscsi_target (host)); + host->host->host_no, acornscsi_target(host)); print_msg(message); printk("\n"); #endif @@ -1419,7 +1508,7 @@ */ if (message[0] == SIMPLE_QUEUE_TAG) host->scsi.reconnected.tag = message[1]; - if (acornscsi_reconnect_finish (host)) + if (acornscsi_reconnect_finish(host)) host->scsi.phase = PHASE_MSGIN; } @@ -1429,7 +1518,7 @@ case COMMAND_COMPLETE: if (host->scsi.phase != PHASE_STATUSIN) { printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n", - host->host->host_no, acornscsi_target (host)); + host->host->host_no, acornscsi_target(host)); acornscsi_dumplog(host, host->SCpnt->target); } host->scsi.phase = PHASE_DONE; @@ -1443,7 +1532,7 @@ * direct the initiator to copy the active data pointer to * the saved data pointer for the current I/O process. */ - acornscsi_dma_cleanup (host); + acornscsi_dma_cleanup(host); host->SCpnt->SCp = host->scsi.SCp; host->SCpnt->SCp.sent_command = 0; host->scsi.phase = PHASE_MSGIN; @@ -1459,7 +1548,7 @@ * status pointers shall be restored to the beginning of * the present command and status areas.' */ - acornscsi_dma_cleanup (host); + acornscsi_dma_cleanup(host); host->scsi.SCp = host->SCpnt->SCp; host->scsi.phase = PHASE_MSGIN; break; @@ -1474,7 +1563,7 @@ * message. When reconnection is completed, the most recent * saved pointer values are restored.' */ - acornscsi_dma_cleanup (host); + acornscsi_dma_cleanup(host); host->scsi.phase = PHASE_DISCONNECT; break; @@ -1493,8 +1582,8 @@ /* * If we have any messages waiting to go out, then assert ATN now */ - if (msgqueue_msglength (&host->scsi.msgs)) - acornscsi_sbic_issuecmd (host, CMND_ASSERTATN); + if (msgqueue_msglength(&host->scsi.msgs)) + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); switch (host->scsi.last_message) { #ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE @@ -1507,21 +1596,21 @@ * message is received, it shall respond with a MESSAGE REJECT * message and accept the I/O process as if it were untagged. */ - printk (KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n", - host->host->host_no, acornscsi_target (host)); + printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n", + host->host->host_no, acornscsi_target(host)); host->SCpnt->device->tagged_queue = 0; - set_bit (host->SCpnt->target * 8 + host->SCpnt->lun, &host->busyluns); + set_bit(host->SCpnt->target * 8 + host->SCpnt->lun, &host->busyluns); break; #endif case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8): /* * Target can't handle synchronous transfers */ - printk (KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n", - host->host->host_no, acornscsi_target (host)); + printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n", + host->host->host_no, acornscsi_target(host)); host->device[host->SCpnt->target].sync_xfer = SYNCHTRANSFER_2DBA; host->device[host->SCpnt->target].sync_state = SYNC_ASYNCHRONOUS; - sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer); break; default: @@ -1535,8 +1624,8 @@ case SIMPLE_QUEUE_TAG: /* tag queue reconnect... message[1] = queue tag. Print something to indicate something happened! */ - printk ("scsi%d.%c: reconnect queue tag %02X\n", - host->host->host_no, acornscsi_target (host), + printk("scsi%d.%c: reconnect queue tag %02X\n", + host->host->host_no, acornscsi_target(host), message[1]); break; @@ -1552,26 +1641,26 @@ * and the target retries fail, then we fallback to asynchronous mode */ host->device[host->SCpnt->target].sync_state = SYNC_COMPLETED; - printk (KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n", + printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n", host->host->host_no, acornscsi_target(host), message[4], message[3] * 4); host->device[host->SCpnt->target].sync_xfer = - calc_sync_xfer (message[3] * 4, message[4]); + calc_sync_xfer(message[3] * 4, message[4]); } else { unsigned char period, length; /* * Target requested synchronous transfers. The agreement is only * to be in operation AFTER the target leaves message out phase. */ - acornscsi_sbic_issuecmd (host, CMND_ASSERTATN); - period = max (message[3], sdtr_period / 4); - length = min (message[4], sdtr_size); - msgqueue_addmsg (&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3, + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + period = max(message[3], sdtr_period / 4); + length = min(message[4], sdtr_size); + msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3, EXTENDED_SDTR, period, length); host->device[host->SCpnt->target].sync_xfer = - calc_sync_xfer (period * 4, length); + calc_sync_xfer(period * 4, length); } - sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer); break; #else /* We do not accept synchronous transfers. Respond with a @@ -1584,9 +1673,9 @@ * to a wide data transfer request. */ default: - acornscsi_sbic_issuecmd (host, CMND_ASSERTATN); - msgqueue_flush (&host->scsi.msgs); - msgqueue_addmsg (&host->scsi.msgs, 1, MESSAGE_REJECT); + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + msgqueue_flush(&host->scsi.msgs); + msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); break; } break; @@ -1607,19 +1696,19 @@ * if there are more linked commands available. */ if (!host->SCpnt->next_link) { - printk (KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n", - instance->host_no, acornscsi_target (host), host->SCpnt->tag); - acornscsi_sbic_issuecmd (host, CMND_ASSERTATN); - msgqueue_addmsg (&host->scsi.msgs, 1, ABORT); + printk(KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n", + instance->host_no, acornscsi_target(host), host->SCpnt->tag); + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); } else { Scsi_Cmnd *SCpnt = host->SCpnt; - acornscsi_dma_cleanup (host); + acornscsi_dma_cleanup(host); host->SCpnt = host->SCpnt->next_link; host->SCpnt->tag = SCpnt->tag; SCpnt->result = DID_OK | host->scsi.SCp.Message << 8 | host->Scsi.SCp.Status; - SCpnt->done (SCpnt); + SCpnt->done(SCpnt); /* initialise host->SCpnt->SCp */ } @@ -1628,42 +1717,42 @@ #endif default: /* reject message */ - printk (KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n", - host->host->host_no, acornscsi_target (host), + printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n", + host->host->host_no, acornscsi_target(host), message[0]); - acornscsi_sbic_issuecmd (host, CMND_ASSERTATN); - msgqueue_flush (&host->scsi.msgs); - msgqueue_addmsg (&host->scsi.msgs, 1, MESSAGE_REJECT); + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + msgqueue_flush(&host->scsi.msgs); + msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); host->scsi.phase = PHASE_MSGIN; break; } - acornscsi_sbic_issuecmd (host, CMND_NEGATEACK); + acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); } /* - * Function: int acornscsi_buildmessages (AS_Host *host) + * Function: int acornscsi_buildmessages(AS_Host *host) * Purpose : build the connection messages for a host * Params : host - host to add messages to */ static -void acornscsi_buildmessages (AS_Host *host) +void acornscsi_buildmessages(AS_Host *host) { #if 0 /* does the device need resetting? */ if (cmd_reset) { - msgqueue_addmsg (&host->scsi.msgs, 1, BUS_DEVICE_RESET); + msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET); return; } #endif - msgqueue_addmsg (&host->scsi.msgs, 1, + msgqueue_addmsg(&host->scsi.msgs, 1, IDENTIFY(host->device[host->SCpnt->target].disconnect_ok, host->SCpnt->lun)); #if 0 /* does the device need the current command aborted */ if (cmd_aborted) { - acornscsi_abortcmd (host->SCpnt->tag); + acornscsi_abortcmd(host->SCpnt->tag); return; } #endif @@ -1678,14 +1767,14 @@ tag_type = HEAD_OF_QUEUE_TAG; else tag_type = SIMPLE_QUEUE_TAG; - msgqueue_addmsg (&host->scsi.msgs, 2, tag_type, host->SCpnt->tag); + msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag); } #endif #ifdef CONFIG_SCSI_ACORNSCSI_SYNC if (host->device[host->SCpnt->target].sync_state == SYNC_NEGOCIATE) { host->device[host->SCpnt->target].sync_state = SYNC_SENT_REQUEST; - msgqueue_addmsg (&host->scsi.msgs, 5, + msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3, EXTENDED_SDTR, sdtr_period / 4, sdtr_size); } @@ -1693,29 +1782,29 @@ } /* - * Function: int acornscsi_starttransfer (AS_Host *host) + * Function: int acornscsi_starttransfer(AS_Host *host) * Purpose : transfer data to/from connected target * Params : host - host to which target is connected * Returns : 0 if failure */ static -int acornscsi_starttransfer (AS_Host *host) +int acornscsi_starttransfer(AS_Host *host) { int residual; if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) { - printk (KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n", - host->host->host_no, acornscsi_target (host)); + printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n", + host->host->host_no, acornscsi_target(host)); return 0; } residual = host->SCpnt->request_bufflen - host->scsi.SCp.scsi_xferred; - sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer); - sbic_arm_writenext (host->scsi.io_port, residual >> 16); - sbic_arm_writenext (host->scsi.io_port, residual >> 8); - sbic_arm_writenext (host->scsi.io_port, residual); - acornscsi_sbic_issuecmd (host, CMND_XFERINFO); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->target].sync_xfer); + sbic_arm_writenext(host->scsi.io_port, residual >> 16); + sbic_arm_writenext(host->scsi.io_port, residual >> 8); + sbic_arm_writenext(host->scsi.io_port, residual); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO); return 1; } @@ -1723,7 +1812,7 @@ * Connection & Disconnection */ /* - * Function : acornscsi_reconnect (AS_Host *host) + * Function : acornscsi_reconnect(AS_Host *host) * Purpose : reconnect a previously disconnected command * Params : host - host specific data * Remarks : SCSI spec says: @@ -1731,27 +1820,27 @@ * of saved pointers upon reconnection of the I/O process' */ static -int acornscsi_reconnect (AS_Host *host) +int acornscsi_reconnect(AS_Host *host) { unsigned int target, lun, ok = 0; - target = sbic_arm_read (host->scsi.io_port, SOURCEID); + target = sbic_arm_read(host->scsi.io_port, SOURCEID); if (!(target & 8)) - printk (KERN_ERR "scsi%d: invalid source id after reselection " + printk(KERN_ERR "scsi%d: invalid source id after reselection " "- device fault?\n", host->host->host_no); target &= 7; if (host->SCpnt && !host->scsi.disconnectable) { - printk (KERN_ERR "scsi%d.%d: reconnected while command in " + printk(KERN_ERR "scsi%d.%d: reconnected while command in " "progress to target %d?\n", host->host->host_no, target, host->SCpnt->target); host->SCpnt = NULL; } - lun = sbic_arm_read (host->scsi.io_port, DATA) & 7; + lun = sbic_arm_read(host->scsi.io_port, DATA) & 7; host->scsi.reconnected.target = target; host->scsi.reconnected.lun = lun; @@ -1761,7 +1850,7 @@ host->SCpnt->target == target && host->SCpnt->lun == lun) ok = 1; - if (!ok && queue_probetgtlun (&host->queues.disconnected, target, lun)) + if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun)) ok = 1; ADD_STATUS(target, 0x81, host->scsi.phase, 0); @@ -1770,26 +1859,28 @@ host->scsi.phase = PHASE_RECONNECTED; } else { /* this doesn't seem to work */ - printk (KERN_ERR "scsi%d.%c: reselected with no command " + printk(KERN_ERR "scsi%d.%c: reselected with no command " "to reconnect with\n", host->host->host_no, '0' + target); - acornscsi_dumplog (host, target); - acornscsi_sbic_issuecmd (host, CMND_ASSERTATN); - msgqueue_addmsg (&host->scsi.msgs, 1, ABORT); - host->scsi.phase = PHASE_ABORTED; + acornscsi_dumplog(host, target); + acornscsi_abortcmd(host, 0); + if (host->SCpnt) { + queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); + host->SCpnt = NULL; + } } - acornscsi_sbic_issuecmd (host, CMND_NEGATEACK); + acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); return !ok; } /* - * Function: int acornscsi_reconect_finish (AS_Host *host) + * Function: int acornscsi_reconect_finish(AS_Host *host) * Purpose : finish reconnecting a command * Params : host - host to complete * Returns : 0 if failed */ static -int acornscsi_reconnect_finish (AS_Host *host) +int acornscsi_reconnect_finish(AS_Host *host) { if (host->scsi.disconnectable && host->SCpnt) { host->scsi.disconnectable = 0; @@ -1797,45 +1888,44 @@ host->SCpnt->lun == host->scsi.reconnected.lun && host->SCpnt->tag == host->scsi.reconnected.tag) { #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk ("scsi%d.%c: reconnected", - host->host->host_no, acornscsi_target (host))); + DBG(host->SCpnt, printk("scsi%d.%c: reconnected", + host->host->host_no, acornscsi_target(host))); #endif } else { - queue_add_cmd_tail (&host->queues.disconnected, host->SCpnt); + queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk ("scsi%d.%c: had to move command " + DBG(host->SCpnt, printk("scsi%d.%c: had to move command " "to disconnected queue\n", - host->host->host_no, acornscsi_target (host))); + host->host->host_no, acornscsi_target(host))); #endif host->SCpnt = NULL; } } if (!host->SCpnt) { - host->SCpnt = queue_remove_tgtluntag (&host->queues.disconnected, + host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected, host->scsi.reconnected.target, host->scsi.reconnected.lun, host->scsi.reconnected.tag); #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk ("scsi%d.%c: had to get command", - host->host->host_no, acornscsi_target (host))); + DBG(host->SCpnt, printk("scsi%d.%c: had to get command", + host->host->host_no, acornscsi_target(host))); #endif } - if (!host->SCpnt) { - acornscsi_abortcmd (host, host->scsi.reconnected.tag); - host->scsi.phase = PHASE_ABORTED; - } else { + if (!host->SCpnt) + acornscsi_abortcmd(host, host->scsi.reconnected.tag); + else { /* * Restore data pointer from SAVED pointers. */ host->scsi.SCp = host->SCpnt->SCp; #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - printk (", data pointers: [%p, %X]", + printk(", data pointers: [%p, %X]", host->scsi.SCp.ptr, host->scsi.SCp.this_residual); #endif } #if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - printk ("\n"); + printk("\n"); #endif host->dma.transferred = host->scsi.SCp.scsi_xferred; @@ -1844,47 +1934,48 @@ } /* - * Function: void acornscsi_disconnect_unexpected (AS_Host *host) + * Function: void acornscsi_disconnect_unexpected(AS_Host *host) * Purpose : handle an unexpected disconnect * Params : host - host on which disconnect occurred */ static -void acornscsi_disconnect_unexpected (AS_Host *host) +void acornscsi_disconnect_unexpected(AS_Host *host) { - printk (KERN_ERR "scsi%d.%c: unexpected disconnect\n", - host->host->host_no, acornscsi_target (host)); + printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n", + host->host->host_no, acornscsi_target(host)); #if (DEBUG & DEBUG_ABORT) - acornscsi_dumplog (host, 8); + acornscsi_dumplog(host, 8); #endif - acornscsi_done (host, &host->SCpnt, DID_ABORT); + acornscsi_done(host, &host->SCpnt, DID_ERROR); } /* - * Function: void acornscsi_abortcmd (AS_host *host, unsigned char tag) + * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag) * Purpose : abort a currently executing command * Params : host - host with connected command to abort * tag - tag to abort */ static -void acornscsi_abortcmd (AS_Host *host, unsigned char tag) +void acornscsi_abortcmd(AS_Host *host, unsigned char tag) { - sbic_arm_write (host->scsi.io_port, CMND, CMND_ASSERTATN); + host->scsi.phase = PHASE_ABORTED; + sbic_arm_write(host->scsi.io_port, CMND, CMND_ASSERTATN); - msgqueue_flush (&host->scsi.msgs); + msgqueue_flush(&host->scsi.msgs); #ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE if (tag) - msgqueue_addmsg (&host->scsi.msgs, 2, ABORT_TAG, tag); + msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag); else #endif - msgqueue_addmsg (&host->scsi.msgs, 1, ABORT); + msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); } /* ========================================================================================== * Interrupt routines. */ /* - * Function: int acornscsi_sbicintr (AS_Host *host) + * Function: int acornscsi_sbicintr(AS_Host *host) * Purpose : handle interrupts from SCSI device * Params : host - host to process * Returns : INTR_PROCESS if expecting another SBIC interrupt @@ -1892,15 +1983,15 @@ * INTR_NEXT_COMMAND if we have finished processing the command */ static -intr_ret_t acornscsi_sbicintr (AS_Host *host, int in_irq) +intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq) { unsigned int asr, ssr; - asr = sbic_arm_read (host->scsi.io_port, ASR); + asr = sbic_arm_read(host->scsi.io_port, ASR); if (!(asr & ASR_INT)) return INTR_IDLE; - ssr = sbic_arm_read (host->scsi.io_port, SSR); + ssr = sbic_arm_read(host->scsi.io_port, SSR); #if (DEBUG & DEBUG_PHASES) print_sbic_status(asr, ssr, host->scsi.phase); @@ -1913,23 +2004,23 @@ switch (ssr) { case 0x00: /* reset state - not advanced */ - printk (KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n", + printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n", host->host->host_no); /* setup sbic - WD33C93A */ - sbic_arm_write (host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); - sbic_arm_write (host->scsi.io_port, CMND, CMND_RESET); + sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); + sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET); return INTR_IDLE; case 0x01: /* reset state - advanced */ - sbic_arm_write (host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); - sbic_arm_write (host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); - sbic_arm_write (host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); - sbic_arm_write (host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); - msgqueue_flush (&host->scsi.msgs); + sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); + sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); + sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); + msgqueue_flush(&host->scsi.msgs); return INTR_IDLE; case 0x41: /* unexpected disconnect aborted command */ - acornscsi_disconnect_unexpected (host); + acornscsi_disconnect_unexpected(host); return INTR_NEXT_COMMAND; } @@ -1939,35 +2030,35 @@ case 0x11: /* -> PHASE_CONNECTED */ /* BUS FREE -> SELECTION */ host->scsi.phase = PHASE_CONNECTED; - msgqueue_flush (&host->scsi.msgs); + msgqueue_flush(&host->scsi.msgs); host->dma.transferred = host->scsi.SCp.scsi_xferred; /* 33C93 gives next interrupt indicating bus phase */ - asr = sbic_arm_read (host->scsi.io_port, ASR); + asr = sbic_arm_read(host->scsi.io_port, ASR); if (!(asr & ASR_INT)) break; - ssr = sbic_arm_read (host->scsi.io_port, SSR); + ssr = sbic_arm_read(host->scsi.io_port, SSR); ADD_STATUS(8, ssr, host->scsi.phase, 1); ADD_STATUS(host->SCpnt->target, ssr, host->scsi.phase, 1); goto connected; case 0x42: /* select timed out */ /* -> PHASE_IDLE */ - acornscsi_done (host, &host->SCpnt, DID_NO_CONNECT); + acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT); return INTR_NEXT_COMMAND; case 0x81: /* -> PHASE_RECONNECTED or PHASE_ABORTED */ /* BUS FREE -> RESELECTION */ host->origSCpnt = host->SCpnt; host->SCpnt = NULL; - msgqueue_flush (&host->scsi.msgs); - acornscsi_reconnect (host); + msgqueue_flush(&host->scsi.msgs); + acornscsi_reconnect(host); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); - acornscsi_abortcmd (host, host->SCpnt->tag); + printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); + acornscsi_abortcmd(host, host->SCpnt->tag); } return INTR_PROCESSING; @@ -1977,12 +2068,12 @@ #ifdef NONSTANDARD case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ /* SELECTION -> COMMAND */ - acornscsi_sendcommand (host); + acornscsi_sendcommand(host); break; case 0x8b: /* -> PHASE_STATUS */ /* SELECTION -> STATUS */ - acornscsi_readstatusbyte (host); + acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; #endif @@ -1990,55 +2081,57 @@ case 0x8e: /* -> PHASE_MSGOUT */ /* SELECTION ->MESSAGE OUT */ host->scsi.phase = PHASE_MSGOUT; - acornscsi_buildmessages (host); - acornscsi_sendmessage (host); + acornscsi_buildmessages(host); + acornscsi_sendmessage(host); break; /* these should not happen */ case 0x85: /* target disconnected */ - acornscsi_done (host, &host->SCpnt, DID_ERROR); + acornscsi_done(host, &host->SCpnt, DID_ERROR); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); - acornscsi_abortcmd (host, host->SCpnt->tag); + printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); + acornscsi_abortcmd(host, host->SCpnt->tag); } return INTR_PROCESSING; case PHASE_MSGOUT: /* STATE: connected & sent IDENTIFY message */ /* - * SCSI standard says th at a MESSAGE OUT phases can be followed by a DATA phase + * SCSI standard says that MESSAGE OUT phases can be followed by a + * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase */ switch (ssr) { - case 0x8a: + case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ case 0x1a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ /* MESSAGE OUT -> COMMAND */ - acornscsi_sendcommand (host); + acornscsi_sendcommand(host); break; + case 0x8b: /* -> PHASE_STATUS */ case 0x1b: /* -> PHASE_STATUS */ /* MESSAGE OUT -> STATUS */ - acornscsi_readstatusbyte (host); + acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; case 0x8e: /* -> PHASE_MSGOUT */ /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */ - acornscsi_sendmessage (host); + acornscsi_sendmessage(host); break; - case 0x4f: + case 0x4f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ /* MESSAGE OUT -> MESSAGE IN */ - acornscsi_message (host); + acornscsi_message(host); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; @@ -2047,43 +2140,43 @@ case 0x18: /* -> PHASE_DATAOUT */ /* COMMAND -> DATA OUT */ if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) - acornscsi_abortcmd (host, host->SCpnt->tag); - acornscsi_dma_setup (host, DMA_OUT); - if (!acornscsi_starttransfer (host)) - acornscsi_abortcmd (host, host->SCpnt->tag); + acornscsi_abortcmd(host, host->SCpnt->tag); + acornscsi_dma_setup(host, DMA_OUT); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); host->scsi.phase = PHASE_DATAOUT; return INTR_IDLE; case 0x19: /* -> PHASE_DATAIN */ /* COMMAND -> DATA IN */ if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) - acornscsi_abortcmd (host, host->SCpnt->tag); - acornscsi_dma_setup (host, DMA_IN); - if (!acornscsi_starttransfer (host)) - acornscsi_abortcmd (host, host->SCpnt->tag); + acornscsi_abortcmd(host, host->SCpnt->tag); + acornscsi_dma_setup(host, DMA_IN); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); host->scsi.phase = PHASE_DATAIN; return INTR_IDLE; case 0x1b: /* -> PHASE_STATUS */ /* COMMAND -> STATUS */ - acornscsi_readstatusbyte (host); + acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; case 0x1e: /* -> PHASE_MSGOUT */ /* COMMAND -> MESSAGE OUT */ - acornscsi_sendmessage (host); + acornscsi_sendmessage(host); break; case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ /* COMMAND -> MESSAGE IN */ - acornscsi_message (host); + acornscsi_message(host); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; @@ -2094,19 +2187,19 @@ host->scsi.phase = PHASE_IDLE; host->stats.disconnects += 1; } else { - printk (KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_NEXT_COMMAND; case PHASE_IDLE: /* STATE: disconnected */ if (ssr == 0x81) /* -> PHASE_RECONNECTED or PHASE_ABORTED */ - acornscsi_reconnect (host); + acornscsi_reconnect(host); else { - printk (KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; @@ -2119,54 +2212,54 @@ * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY, * reconnect I_T_L command */ - if (ssr != 0x8f && !acornscsi_reconnect_finish (host)) + if (ssr != 0x8f && !acornscsi_reconnect_finish(host)) return INTR_IDLE; ADD_STATUS(host->SCpnt->target, ssr, host->scsi.phase, in_irq); switch (ssr) { case 0x88: /* data out phase */ /* -> PHASE_DATAOUT */ /* MESSAGE IN -> DATA OUT */ - acornscsi_dma_setup (host, DMA_OUT); - if (!acornscsi_starttransfer (host)) - acornscsi_abortcmd (host, host->SCpnt->tag); + acornscsi_dma_setup(host, DMA_OUT); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); host->scsi.phase = PHASE_DATAOUT; return INTR_IDLE; case 0x89: /* data in phase */ /* -> PHASE_DATAIN */ /* MESSAGE IN -> DATA IN */ - acornscsi_dma_setup (host, DMA_IN); - if (!acornscsi_starttransfer (host)) - acornscsi_abortcmd (host, host->SCpnt->tag); + acornscsi_dma_setup(host, DMA_IN); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); host->scsi.phase = PHASE_DATAIN; return INTR_IDLE; case 0x8a: /* command out */ /* MESSAGE IN -> COMMAND */ - acornscsi_sendcommand (host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ + acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ break; case 0x8b: /* status in */ /* -> PHASE_STATUSIN */ /* MESSAGE IN -> STATUS */ - acornscsi_readstatusbyte (host); + acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; case 0x8e: /* message out */ /* -> PHASE_MSGOUT */ /* MESSAGE IN -> MESSAGE OUT */ - acornscsi_sendmessage (host); + acornscsi_sendmessage(host); break; case 0x8f: /* message in */ - acornscsi_message (host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; @@ -2177,41 +2270,45 @@ */ switch (ssr) { case 0x19: /* -> PHASE_DATAIN */ - acornscsi_abortcmd (host, host->SCpnt->tag); + case 0x89: /* -> PHASE_DATAIN */ + acornscsi_abortcmd(host, host->SCpnt->tag); return INTR_IDLE; - case 0x4b: /* -> PHASE_STATUSIN */ case 0x1b: /* -> PHASE_STATUSIN */ + case 0x4b: /* -> PHASE_STATUSIN */ + case 0x8b: /* -> PHASE_STATUSIN */ /* DATA IN -> STATUS */ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount (host); - acornscsi_dma_stop (host); - acornscsi_readstatusbyte (host); + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; case 0x1e: /* -> PHASE_MSGOUT */ case 0x4e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ /* DATA IN -> MESSAGE OUT */ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount (host); - acornscsi_dma_stop (host); - acornscsi_sendmessage (host); + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_sendmessage(host); break; case 0x1f: /* message in */ case 0x4f: /* message in */ + case 0x8f: /* message in */ /* DATA IN -> MESSAGE IN */ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount (host); - acornscsi_dma_stop (host); - acornscsi_message (host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; @@ -2222,58 +2319,69 @@ */ switch (ssr) { case 0x18: /* -> PHASE_DATAOUT */ - acornscsi_abortcmd (host, host->SCpnt->tag); + case 0x88: /* -> PHASE_DATAOUT */ + acornscsi_abortcmd(host, host->SCpnt->tag); return INTR_IDLE; - case 0x4b: /* -> PHASE_STATUSIN */ case 0x1b: /* -> PHASE_STATUSIN */ + case 0x4b: /* -> PHASE_STATUSIN */ + case 0x8b: /* -> PHASE_STATUSIN */ /* DATA OUT -> STATUS */ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount (host); - acornscsi_dma_stop (host); - acornscsi_dma_adjust (host); - acornscsi_readstatusbyte (host); + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_dma_adjust(host); + acornscsi_readstatusbyte(host); host->scsi.phase = PHASE_STATUSIN; break; case 0x1e: /* -> PHASE_MSGOUT */ case 0x4e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ /* DATA OUT -> MESSAGE OUT */ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount (host); - acornscsi_dma_stop (host); - acornscsi_dma_adjust (host); - acornscsi_sendmessage (host); + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_dma_adjust(host); + acornscsi_sendmessage(host); break; case 0x1f: /* message in */ case 0x4f: /* message in */ + case 0x8f: /* message in */ /* DATA OUT -> MESSAGE IN */ host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount (host); - acornscsi_dma_stop (host); - acornscsi_dma_adjust (host); - acornscsi_message (host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_dma_adjust(host); + acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; case PHASE_STATUSIN: /* STATE: status in complete */ - if (ssr == 0x1f) /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ + switch (ssr) { + case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ + case 0x8f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ /* STATUS -> MESSAGE IN */ - acornscsi_message (host); - else if (ssr == 0x1e) /* -> PHASE_MSGOUT */ + acornscsi_message(host); + break; + + case 0x1e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ /* STATUS -> MESSAGE OUT */ - acornscsi_sendmessage (host); - else { - printk (KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + acornscsi_sendmessage(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; @@ -2281,78 +2389,93 @@ switch (ssr) { case 0x1e: /* -> PHASE_MSGOUT */ case 0x4e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ /* MESSAGE IN -> MESSAGE OUT */ - acornscsi_sendmessage (host); + acornscsi_sendmessage(host); break; case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ case 0x2f: case 0x4f: case 0x8f: - acornscsi_message (host); + acornscsi_message(host); + break; + + case 0x85: + printk("scsi%d.%c: strange message in disconnection\n", + host->host->host_no, acornscsi_target(host)); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); + acornscsi_done(host, &host->SCpnt, DID_ERROR); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; case PHASE_DONE: /* STATE: received status & message */ switch (ssr) { case 0x85: /* -> PHASE_IDLE */ - acornscsi_done (host, &host->SCpnt, DID_OK); + acornscsi_done(host, &host->SCpnt, DID_OK); return INTR_NEXT_COMMAND; + case 0x1e: case 0x8e: - acornscsi_sendmessage (host); + acornscsi_sendmessage(host); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; case PHASE_ABORTED: switch (ssr) { case 0x85: - acornscsi_done (host, &host->SCpnt, DID_ABORT); + if (host->SCpnt) + acornscsi_done(host, &host->SCpnt, DID_ABORT); + else { + clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun, + host->busyluns); + host->scsi.phase = PHASE_IDLE; + } return INTR_NEXT_COMMAND; case 0x1e: case 0x2e: case 0x4e: case 0x8e: - acornscsi_sendmessage (host); + acornscsi_sendmessage(host); break; default: - printk (KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; default: - printk (KERN_ERR "scsi%d.%c: unknown driver phase %d\n", - host->host->host_no, acornscsi_target (host), ssr); - acornscsi_dumplog (host, host->SCpnt ? host->SCpnt->target : 8); + printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->target : 8); } return INTR_PROCESSING; } /* - * Prototype: void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs) + * Prototype: void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs) * Purpose : handle interrupts from Acorn SCSI card * Params : irq - interrupt number * dev_id - device specific data (AS_Host structure) * regs - processor registers when interrupt occurred */ static -void acornscsi_intr (int irq, void *dev_id, struct pt_regs *regs) +void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs) { AS_Host *host = (AS_Host *)dev_id; intr_ret_t ret; @@ -2360,21 +2483,21 @@ int in_irq = 0; if (host->scsi.interrupt) - printk ("scsi%d: interrupt re-entered\n", host->host->host_no); + printk("scsi%d: interrupt re-entered\n", host->host->host_no); host->scsi.interrupt = 1; do { ret = INTR_IDLE; - iostatus = inb (host->card.io_intr); + iostatus = inb(host->card.io_intr); if (iostatus & 2) { - acornscsi_dma_intr (host); - iostatus = inb (host->card.io_intr); + acornscsi_dma_intr(host); + iostatus = inb(host->card.io_intr); } if (iostatus & 8) - ret = acornscsi_sbicintr (host, in_irq); + ret = acornscsi_sbicintr(host, in_irq); /* * If we have a transfer pending, start it. @@ -2382,10 +2505,10 @@ * it's data */ if (host->dma.xfer_required) - acornscsi_dma_xfer (host); + acornscsi_dma_xfer(host); if (ret == INTR_NEXT_COMMAND) - ret = acornscsi_kick (host); + ret = acornscsi_kick(host); in_irq = 1; } while (ret != INTR_IDLE); @@ -2398,29 +2521,29 @@ */ /* - * Function : acornscsi_queuecmd (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) + * Function : acornscsi_queuecmd(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) * Purpose : queues a SCSI command * Params : cmd - SCSI command * done - function called on completion, with pointer to command descriptor * Returns : 0, or < 0 on error. */ -int acornscsi_queuecmd (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +int acornscsi_queuecmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { AS_Host *host = (AS_Host *)SCpnt->host->hostdata; if (!done) { /* there should be some way of rejecting errors like this without panicing... */ - panic ("scsi%d: queuecommand called with NULL done function [cmd=%p]", + panic("scsi%d: queuecommand called with NULL done function [cmd=%p]", SCpnt->host->host_no, SCpnt); return -EINVAL; } #if (DEBUG & DEBUG_NO_WRITE) - if (acornscsi_cmdtype (SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->target))) { - printk (KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n", + if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->target))) { + printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n", SCpnt->host->host_no, '0' + SCpnt->target); SCpnt->result = DID_NO_CONNECT << 16; - done (SCpnt); + done(SCpnt); return 0; } #endif @@ -2429,7 +2552,7 @@ SCpnt->host_scribble = NULL; SCpnt->result = 0; SCpnt->tag = 0; - SCpnt->SCp.phase = (int)acornscsi_datadirection (SCpnt->cmnd[0]); + SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]); SCpnt->SCp.sent_command = 0; SCpnt->SCp.scsi_xferred = 0; SCpnt->SCp.Status = 0; @@ -2452,21 +2575,21 @@ { unsigned long flags; - if (!queue_add_cmd_ordered (&host->queues.issue, SCpnt)) { + if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) { SCpnt->result = DID_ERROR << 16; - done (SCpnt); + done(SCpnt); return 0; } - save_flags_cli (flags); + save_flags_cli(flags); if (host->scsi.phase == PHASE_IDLE) - acornscsi_kick (host); - restore_flags (flags); + acornscsi_kick(host); + restore_flags(flags); } return 0; } /* - * Prototype: void acornscsi_reportstatus (Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) + * Prototype: void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) * Purpose : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2 * Params : SCpntp1 - pointer to command to return * SCpntp2 - pointer to command to check @@ -2474,7 +2597,7 @@ * Returns : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2. */ static inline -void acornscsi_reportstatus (Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) +void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) { Scsi_Cmnd *SCpnt = *SCpntp1; @@ -2482,80 +2605,203 @@ *SCpntp1 = NULL; SCpnt->result = result; - SCpnt->scsi_done (SCpnt); + SCpnt->scsi_done(SCpnt); } if (SCpnt == *SCpntp2) *SCpntp2 = NULL; } +enum res_abort { res_not_running, res_success, res_success_clear, res_snooze }; + +/* + * Prototype: enum res acornscsi_do_abort(Scsi_Cmnd *SCpnt) + * Purpose : abort a command on this host + * Params : SCpnt - command to abort + * Returns : our abort status + */ +static enum res_abort +acornscsi_do_abort(AS_Host *host, Scsi_Cmnd *SCpnt) +{ + enum res_abort res = res_not_running; + + if (queue_removecmd(&host->queues.issue, SCpnt)) { + /* + * The command was on the issue queue, and has not been + * issued yet. We can remove the command from the queue, + * and acknowledge the abort. Neither the devices nor the + * interface know about the command. + */ +//#if (DEBUG & DEBUG_ABORT) + printk("on issue queue "); +//#endif + res = res_success; + } else if (queue_removecmd(&host->queues.disconnected, SCpnt)) { + /* + * The command was on the disconnected queue. Simply + * acknowledge the abort condition, and when the target + * reconnects, we will give it an ABORT message. The + * target should then disconnect, and we will clear + * the busylun bit. + */ +//#if (DEBUG & DEBUG_ABORT) + printk("on disconnected queue "); +//#endif + res = res_success; + } else if (host->SCpnt == SCpnt) { + unsigned long flags; + +//#if (DEBUG & DEBUG_ABORT) + printk("executing "); +//#endif + + save_flags(flags); + cli(); + switch (host->scsi.phase) { + /* + * If the interface is idle, and the command is 'disconnectable', + * then it is the same as on the disconnected queue. We simply + * remove all traces of the command. When the target reconnects, + * we will give it an ABORT message since the command could not + * be found. When the target finally disconnects, we will clear + * the busylun bit. + */ + case PHASE_IDLE: + if (host->scsi.disconnectable) { + host->scsi.disconnectable = 0; + host->SCpnt = NULL; + res = res_success; + } + break; + + /* + * If the command has connected and done nothing further, + * simply force a disconnect. We also need to clear the + * busylun bit. + */ + case PHASE_CONNECTED: + sbic_arm_write(host->scsi.io_port, CMND, CMND_DISCONNECT); + host->SCpnt = NULL; + res = res_success_clear; + break; + + default: + acornscsi_abortcmd(host, host->SCpnt->tag); + res = res_snooze; + } + restore_flags(flags); + } else if (host->origSCpnt == SCpnt) { + /* + * The command will be executed next, but a command + * is currently using the interface. This is similar to + * being on the issue queue, except the busylun bit has + * been set. + */ + host->origSCpnt = NULL; +//#if (DEBUG & DEBUG_ABORT) + printk("waiting for execution "); +//#endif + res = res_success_clear; + } else + printk("unknown "); + + return res; +} + /* - * Prototype: int acornscsi_abort (Scsi_Cmnd *SCpnt) + * Prototype: int acornscsi_abort(Scsi_Cmnd *SCpnt) * Purpose : abort a command on this host * Params : SCpnt - command to abort * Returns : one of SCSI_ABORT_ macros */ -int acornscsi_abort (Scsi_Cmnd *SCpnt) +int acornscsi_abort(Scsi_Cmnd *SCpnt) { - AS_Host *host = (AS_Host *) SCpnt->host->hostdata; - int result = SCSI_ABORT_NOT_RUNNING; + AS_Host *host = (AS_Host *) SCpnt->host->hostdata; + int result; - host->stats.aborts += 1; + host->stats.aborts += 1; #if (DEBUG & DEBUG_ABORT) - { - int asr, ssr; - asr = sbic_arm_read (host->scsi.io_port, ASR); - ssr = sbic_arm_read (host->scsi.io_port, SSR); + { + int asr, ssr; + asr = sbic_arm_read(host->scsi.io_port, ASR); + ssr = sbic_arm_read(host->scsi.io_port, SSR); - printk (KERN_WARNING "acornscsi_abort: "); - print_sbic_status(asr, ssr, host->scsi.phase); - acornscsi_dumplog (host, SCpnt->target); - } + printk(KERN_WARNING "acornscsi_abort: "); + print_sbic_status(asr, ssr, host->scsi.phase); + acornscsi_dumplog(host, SCpnt->target); + } #endif - if (queue_removecmd (&host->queues.issue, SCpnt)) { - SCpnt->result = DID_ABORT << 16; - SCpnt->scsi_done (SCpnt); -#if (DEBUG & DEBUG_ABORT) - printk ("scsi%d: command on issue queue\n", host->host->host_no); -#endif - result = SCSI_ABORT_SUCCESS; - } else if (queue_cmdonqueue (&host->queues.disconnected, SCpnt)) { - printk ("scsi%d: command on disconnected queue\n", host->host->host_no); - result = SCSI_ABORT_SNOOZE; - } else if (host->SCpnt == SCpnt) { - acornscsi_abortcmd (host, host->SCpnt->tag); - printk ("scsi%d: command executing\n", host->host->host_no); - result = SCSI_ABORT_SNOOZE; - } else if (host->origSCpnt == SCpnt) { - host->origSCpnt = NULL; - SCpnt->result = DID_ABORT << 16; - SCpnt->scsi_done (SCpnt); -#if (DEBUG & DEBUG_ABORT) - printk ("scsi%d: command waiting for execution\n", host->host->host_no); -#endif - result = SCSI_ABORT_SUCCESS; - } + printk("scsi%d: ", host->host->host_no); + + switch (acornscsi_do_abort(host, SCpnt)) { + /* + * We managed to find the command and cleared it out. + * We do not expect the command to be executing on the + * target, but we have set the busylun bit. + */ + case res_success_clear: +//#if (DEBUG & DEBUG_ABORT) + printk("clear "); +//#endif + clear_bit(SCpnt->target * 8 + SCpnt->lun, host->busyluns); + + /* + * We found the command, and cleared it out. Either + * the command is still known to be executing on the + * target, or the busylun bit is not set. + */ + case res_success: +//#if (DEBUG & DEBUG_ABORT) + printk("success\n"); +//#endif + SCpnt->result = DID_ABORT << 16; + SCpnt->scsi_done(SCpnt); + result = SCSI_ABORT_SUCCESS; + break; - if (result == SCSI_ABORT_NOT_RUNNING) { - printk ("scsi%d: abort(): command not running\n", host->host->host_no); - acornscsi_dumplog (host, SCpnt->target); + /* + * We did find the command, but unfortunately we couldn't + * unhook it from ourselves. Wait some more, and if it + * still doesn't complete, reset the interface. + */ + case res_snooze: +//#if (DEBUG & DEBUG_ABORT) + printk("snooze\n"); +//#endif + result = SCSI_ABORT_SNOOZE; + break; + + /* + * The command could not be found (either because it completed, + * or it got dropped. + */ + default: + case res_not_running: + acornscsi_dumplog(host, SCpnt->target); #if (DEBUG & DEBUG_ABORT) - result = SCSI_ABORT_SNOOZE; + result = SCSI_ABORT_SNOOZE; +#else + result = SCSI_ABORT_NOT_RUNNING; #endif - } - return result; +//#if (DEBUG & DEBUG_ABORT) + printk("not running\n"); +//#endif + break; + } + + return result; } /* - * Prototype: int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) + * Prototype: int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) * Purpose : reset a command on this host/reset this host * Params : SCpnt - command causing reset * result - what type of reset to perform * Returns : one of SCSI_RESET_ macros */ -int acornscsi_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) +int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) { AS_Host *host = (AS_Host *)SCpnt->host->hostdata; Scsi_Cmnd *SCptr; @@ -2566,16 +2812,16 @@ { int asr, ssr; - asr = sbic_arm_read (host->scsi.io_port, ASR); - ssr = sbic_arm_read (host->scsi.io_port, SSR); + asr = sbic_arm_read(host->scsi.io_port, ASR); + ssr = sbic_arm_read(host->scsi.io_port, SSR); - printk (KERN_WARNING "acornscsi_reset: "); + printk(KERN_WARNING "acornscsi_reset: "); print_sbic_status(asr, ssr, host->scsi.phase); - acornscsi_dumplog (host, SCpnt->target); + acornscsi_dumplog(host, SCpnt->target); } #endif - acornscsi_dma_stop (host); + acornscsi_dma_stop(host); SCptr = host->SCpnt; @@ -2583,19 +2829,19 @@ * do hard reset. This resets all devices on this host, and so we * must set the reset status on all commands. */ - acornscsi_resetcard (host); + acornscsi_resetcard(host); /* * report reset on commands current connected/disconnected */ - acornscsi_reportstatus (&host->SCpnt, &SCptr, DID_RESET); + acornscsi_reportstatus(&host->SCpnt, &SCptr, DID_RESET); - while ((SCptr = queue_remove (&host->queues.disconnected)) != NULL) - acornscsi_reportstatus (&SCptr, &SCpnt, DID_RESET); + while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL) + acornscsi_reportstatus(&SCptr, &SCpnt, DID_RESET); if (SCpnt) { SCpnt->result = DID_RESET << 16; - SCpnt->scsi_done (SCpnt); + SCpnt->scsi_done(SCpnt); } return SCSI_RESET_BUS_RESET | SCSI_RESET_HOST_RESET | SCSI_RESET_SUCCESS; @@ -2607,19 +2853,19 @@ static struct expansion_card *ecs[MAX_ECARDS]; /* - * Prototype: void acornscsi_init (AS_Host *host) + * Prototype: void acornscsi_init(AS_Host *host) * Purpose : initialise the AS_Host structure for one interface & setup hardware * Params : host - host to setup */ static -void acornscsi_init (AS_Host *host) +void acornscsi_init(AS_Host *host) { - memset (&host->stats, 0, sizeof (host->stats)); - queue_initialise (&host->queues.issue); - queue_initialise (&host->queues.disconnected); - msgqueue_initialise (&host->scsi.msgs); + memset(&host->stats, 0, sizeof (host->stats)); + queue_initialise(&host->queues.issue); + queue_initialise(&host->queues.disconnected); + msgqueue_initialise(&host->scsi.msgs); - acornscsi_resetcard (host); + acornscsi_resetcard(host); } int acornscsi_detect(Scsi_Host_Template * tpnt) @@ -2634,7 +2880,7 @@ for (i = 0; i < MAX_ECARDS; i++) ecs[i] = NULL; - ecard_startfind (); + ecard_startfind(); while(1) { ecs[count] = ecard_find(0, acornscsi_cids); @@ -2642,37 +2888,37 @@ break; if (ecs[count]->irq == 0xff) { - printk ("scsi: WD33C93 does not have IRQ enabled - ignoring\n"); + printk("scsi: WD33C93 does not have IRQ enabled - ignoring\n"); continue; } ecard_claim(ecs[count]); /* Must claim here - card produces irq on reset */ - instance = scsi_register (tpnt, sizeof(AS_Host)); + instance = scsi_register(tpnt, sizeof(AS_Host)); host = (AS_Host *)instance->hostdata; - instance->io_port = ecard_address (ecs[count], ECARD_MEMC, 0); + instance->io_port = ecard_address(ecs[count], ECARD_MEMC, 0); instance->irq = ecs[count]->irq; host->host = instance; - host->scsi.io_port = ioaddr (instance->io_port + 0x800); + host->scsi.io_port = ioaddr(instance->io_port + 0x800); host->scsi.irq = instance->irq; host->card.io_intr = POD_SPACE(instance->io_port) + 0x800; host->card.io_page = POD_SPACE(instance->io_port) + 0xc00; - host->card.io_ram = ioaddr (instance->io_port); + host->card.io_ram = ioaddr(instance->io_port); host->dma.io_port = instance->io_port + 0xc00; host->dma.io_intr_clear = POD_SPACE(instance->io_port) + 0x800; ecs[count]->irqaddr = (char *)ioaddr(host->card.io_intr); ecs[count]->irqmask = 0x0a; - request_region (instance->io_port + 0x800, 2, "acornscsi(sbic)"); - request_region (host->card.io_intr, 1, "acornscsi(intr)"); - request_region (host->card.io_page, 1, "acornscsi(page)"); + request_region(instance->io_port + 0x800, 2, "acornscsi(sbic)"); + request_region(host->card.io_intr, 1, "acornscsi(intr)"); + request_region(host->card.io_page, 1, "acornscsi(page)"); #ifdef USE_DMAC - request_region (host->dma.io_port, 256, "acornscsi(dmac)"); + request_region(host->dma.io_port, 256, "acornscsi(dmac)"); #endif - request_region (instance->io_port, 2048, "acornscsi(ram)"); + request_region(instance->io_port, 2048, "acornscsi(ram)"); if (request_irq(host->scsi.irq, acornscsi_intr, SA_INTERRUPT, "acornscsi", host)) { printk(KERN_CRIT "scsi%d: IRQ%d not free, interrupts disabled\n", @@ -2680,7 +2926,7 @@ host->scsi.irq = NO_IRQ; } - acornscsi_init (host); + acornscsi_init(host); ++count; } @@ -2688,12 +2934,12 @@ } /* - * Function: int acornscsi_release (struct Scsi_Host *host) + * Function: int acornscsi_release(struct Scsi_Host *host) * Purpose : release all resources used by this adapter * Params : host - driver structure to release * Returns : nothing of any consequence */ -int acornscsi_release (struct Scsi_Host *instance) +int acornscsi_release(struct Scsi_Host *instance) { AS_Host *host = (AS_Host *)instance->hostdata; int i; @@ -2701,30 +2947,30 @@ /* * Put card into RESET state */ - outb (0x80, host->card.io_page); + outb(0x80, host->card.io_page); if (host->scsi.irq != NO_IRQ) - free_irq (host->scsi.irq, host); + free_irq(host->scsi.irq, host); - release_region (instance->io_port + 0x800, 2); - release_region (host->card.io_intr, 1); - release_region (host->card.io_page, 1); - release_region (host->dma.io_port, 256); - release_region (instance->io_port, 2048); + release_region(instance->io_port + 0x800, 2); + release_region(host->card.io_intr, 1); + release_region(host->card.io_page, 1); + release_region(host->dma.io_port, 256); + release_region(instance->io_port, 2048); for (i = 0; i < MAX_ECARDS; i++) - if (ecs[i] && instance->io_port == ecard_address (ecs[i], ECARD_MEMC, 0)) - ecard_release (ecs[i]); + if (ecs[i] && instance->io_port == ecard_address(ecs[i], ECARD_MEMC, 0)) + ecard_release(ecs[i]); - msgqueue_free (&host->scsi.msgs); - queue_free (&host->queues.disconnected); - queue_free (&host->queues.issue); + msgqueue_free(&host->scsi.msgs); + queue_free(&host->queues.disconnected); + queue_free(&host->queues.issue); return 0; } /* - * Function: char *acornscsi_info (struct Scsi_Host *host) + * Function: char *acornscsi_info(struct Scsi_Host *host) * Purpose : return a string describing this interface * Params : host - host to give information on * Returns : a constant string @@ -2736,7 +2982,7 @@ p = string; - p += sprintf (string, "%s at port %lX irq %d v%d.%d.%d" + p += sprintf(string, "%s at port %X irq %d v%d.%d.%d" #ifdef CONFIG_SCSI_ACORNSCSI_SYNC " SYNC" #endif @@ -2772,7 +3018,7 @@ host = (AS_Host *)instance->hostdata; - p += sprintf (p, "AcornSCSI driver v%d.%d.%d" + p += sprintf(p, "AcornSCSI driver v%d.%d.%d" #ifdef CONFIG_SCSI_ACORNSCSI_SYNC " SYNC" #endif @@ -2787,14 +3033,14 @@ #endif "\n\n", VER_MAJOR, VER_MINOR, VER_PATCH); - p += sprintf (p, "SBIC: WD33C93A Address: %08X IRQ : %d\n", + p += sprintf(p, "SBIC: WD33C93A Address: %08X IRQ : %d\n", host->scsi.io_port, host->scsi.irq); #ifdef USE_DMAC - p += sprintf (p, "DMAC: uPC71071 Address: %08X IRQ : %d\n\n", + p += sprintf(p, "DMAC: uPC71071 Address: %08X IRQ : %d\n\n", host->dma.io_port, host->scsi.irq); #endif - p += sprintf (p, "Statistics:\n" + p += sprintf(p, "Statistics:\n" "Queued commands: %-10u Issued commands: %-10u\n" "Done commands : %-10u Reads : %-10u\n" "Writes : %-10u Others : %-10u\n" @@ -2809,47 +3055,47 @@ for (devidx = 0; devidx < 9; devidx ++) { unsigned int statptr, prev; - p += sprintf (p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx)); - statptr = status_ptr[devidx] - 10; + p += sprintf(p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx)); + statptr = host->status_ptr[devidx] - 10; if ((signed int)statptr < 0) - statptr += 16; + statptr += STATUS_BUFFER_SIZE; - prev = status[devidx][statptr].when; + prev = host->status[devidx][statptr].when; - for (; statptr != status_ptr[devidx]; statptr = (statptr + 1) & 15) { - if (status[devidx][statptr].when) { - p += sprintf (p, "%c%02X:%02X+%2ld", - status[devidx][statptr].irq ? '-' : ' ', - status[devidx][statptr].ph, - status[devidx][statptr].ssr, - (status[devidx][statptr].when - prev) < 100 ? - (status[devidx][statptr].when - prev) : 99); - prev = status[devidx][statptr].when; + for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) { + if (host->status[devidx][statptr].when) { + p += sprintf(p, "%c%02X:%02X+%2ld", + host->status[devidx][statptr].irq ? '-' : ' ', + host->status[devidx][statptr].ph, + host->status[devidx][statptr].ssr, + (host->status[devidx][statptr].when - prev) < 100 ? + (host->status[devidx][statptr].when - prev) : 99); + prev = host->status[devidx][statptr].when; } } } - p += sprintf (p, "\nAttached devices:%s\n", instance->host_queue ? "" : " none"); + p += sprintf(p, "\nAttached devices:%s\n", instance->host_queue ? "" : " none"); for (scd = instance->host_queue; scd; scd = scd->next) { int len; - proc_print_scsidevice (scd, p, &len, 0); + proc_print_scsidevice(scd, p, &len, 0); p += len; - p += sprintf (p, "Extensions: "); + p += sprintf(p, "Extensions: "); if (scd->tagged_supported) - p += sprintf (p, "TAG %sabled [%d] ", + p += sprintf(p, "TAG %sabled [%d] ", scd->tagged_queue ? "en" : "dis", scd->current_tag); - p += sprintf (p, "\nTransfers: "); + p += sprintf(p, "\nTransfers: "); if (host->device[scd->id].sync_xfer & 15) - p += sprintf (p, "sync, offset %d, %d ns\n", + p += sprintf(p, "sync, offset %d, %d ns\n", host->device[scd->id].sync_xfer & 15, - acornscsi_getperiod (host->device[scd->id].sync_xfer)); + acornscsi_getperiod(host->device[scd->id].sync_xfer)); else - p += sprintf (p, "async\n"); + p += sprintf(p, "async\n"); pos = p - buffer; if (pos + begin < offset) { diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/acornscsi.h linux/drivers/acorn/scsi/acornscsi.h --- v2.3.6/linux/drivers/acorn/scsi/acornscsi.h Sun Dec 28 09:06:25 1997 +++ linux/drivers/acorn/scsi/acornscsi.h Thu Jun 17 01:11:35 1999 @@ -291,6 +291,27 @@ #include "queue.h" #include "msgqueue.h" +#define STATUS_BUFFER_SIZE 32 +/* + * This is used to dump the previous states of the SBIC + */ +struct status_entry { + unsigned long when; + unsigned char ssr; + unsigned char ph; + unsigned char irq; + unsigned char unused; +}; + +#define ADD_STATUS(_q,_ssr,_ph,_irq) \ +({ \ + host->status[(_q)][host->status_ptr[(_q)]].when = jiffies; \ + host->status[(_q)][host->status_ptr[(_q)]].ssr = (_ssr); \ + host->status[(_q)][host->status_ptr[(_q)]].ph = (_ph); \ + host->status[(_q)][host->status_ptr[(_q)]].irq = (_irq); \ + host->status_ptr[(_q)] = (host->status_ptr[(_q)] + 1) & (STATUS_BUFFER_SIZE - 1); \ +}) + /* * AcornSCSI host specific data */ @@ -303,7 +324,7 @@ /* driver information */ struct { unsigned int io_port; /* base address of WD33C93 */ - unsigned char irq; /* interrupt */ + unsigned int irq; /* interrupt */ phase_t phase; /* current phase */ struct { @@ -361,6 +382,7 @@ char *xfer_ptr; /* pointer to area */ unsigned char xfer_required:1; /* set if we need to transfer something */ unsigned char xfer_setup:1; /* set if DMA is setup */ + unsigned char xfer_done:1; /* set if DMA reached end of BH list */ } dma; /* card info */ @@ -370,6 +392,9 @@ unsigned int io_ram; /* base address of RAM access */ unsigned char page_reg; /* current setting of page reg */ } card; + + unsigned char status_ptr[9]; + struct status_entry status[9][STATUS_BUFFER_SIZE]; } AS_Host; #endif /* ndef HOSTS_C */ diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/arxescsi.c linux/drivers/acorn/scsi/arxescsi.c --- v2.3.6/linux/drivers/acorn/scsi/arxescsi.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/acorn/scsi/arxescsi.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,395 @@ +/* + * linux/arch/arm/drivers/scsi/cumana_2.c + * + * Copyright (C) 1997,1998 Russell King + * + * This driver is based on experimentation. Hence, it may have made + * assumptions about the particular card that I have available, and + * may not be reliable! + * + * Changelog: + * 30-08-1997 RMK 0.0.0 Created, READONLY version as cumana_2.c + * 22-01-1998 RMK 0.0.1 Updated to 2.1.80 + * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. + * 11-06-1998 0.0.2 Changed to support ARXE 16-bit SCSI card, enabled writing + * by Stefan Hanske + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../../scsi/sd.h" +#include "../../scsi/hosts.h" +#include "arxescsi.h" +#include "fas216.h" + +/* Hmm - this should go somewhere else */ +#define BUS_ADDR(x) ((((unsigned long)(x)) << 2) + IO_BASE) + +/* Configuration */ +#define ARXESCSI_XTALFREQ 24 +#define ARXESCSI_ASYNC_PERIOD 200 +#define ARXESCSI_SYNC_DEPTH 0 + +/* + * List of devices that the driver will recognise + */ +#define ARXESCSI_LIST { MANU_ARXE, PROD_ARXE_SCSI } + +/* + * Version + */ +#define VER_MAJOR 0 +#define VER_MINOR 0 +#define VER_PATCH 2 + +static struct expansion_card *ecs[MAX_ECARDS]; + +static struct proc_dir_entry proc_scsi_arxescsi = { + PROC_SCSI_QLOGICFAS, 6, "arxescsi", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; + +/* + * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type) + * Purpose : initialises DMA/PIO + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * min_type - minimum DMA support that we must have for this transfer + * Returns : 0 if we should not set CMD_WITHDMA for transfer info command + */ +static fasdmatype_t +arxescsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, fasdmatype_t min_type) +{ + /* + * We don't do real DMA + */ + return fasdma_pseudo; +} + + + +/* Faster transfer routines, written by SH to speed up the loops */ + +static __inline__ unsigned char getb(unsigned int address, unsigned int reg) +{ + unsigned char value; + + __asm__ __volatile__( + "ldrb %0, [%1, %2, lsl #5]" + : "=r" (value) + : "r" (address), "r" (reg) ); + return value; +} + +static __inline__ unsigned int getw(unsigned int address, unsigned int reg) +{ + unsigned int value; + + __asm__ __volatile__( + "ldr %0, [%1, %2, lsl #5]\n\t" + "mov %0, %0, lsl #16\n\t" + "mov %0, %0, lsr #16" + : "=r" (value) + : "r" (address), "r" (reg) ); + return value; +} + +static __inline__ void putw(unsigned int address, unsigned int reg, unsigned long value) +{ + __asm__ __volatile__( + "mov %0, %0, lsl #16\n\t" + "str %0, [%1, %2, lsl #5]" + : + : "r" (value), "r" (address), "r" (reg) ); +} + + +/* + * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer) + * Purpose : handles pseudo DMA + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * transfer - minimum number of bytes we expect to transfer + */ +void arxescsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, int transfer) +{ + ARXEScsi_Info *info = (ARXEScsi_Info *)host->hostdata; + unsigned int length, io, error=0; + unsigned char *addr; + + length = SCp->this_residual; + addr = SCp->ptr; + io = __ioaddr(host->io_port); + + if (direction == DMA_OUT) { + while (length > 0) { + unsigned long word; + + + word = *addr | *(addr + 1) << 8; + if (getb(io, 4) & STAT_INT) + break; + + if (!(getb(io, 48) & CSTATUS_IRQ)) + continue; + + putw(io, 16, word); + if (length > 1) { + addr += 2; + length -= 2; + } else { + addr += 1; + length -= 1; + } + } + } + else { + if (transfer && (transfer & 255)) { + while (length >= 256) { + if (getb(io, 4) & STAT_INT) { + error=1; + break; + } + + if (!(getb(io, 48) & CSTATUS_IRQ)) + continue; + + insw(info->dmaarea, addr, 256 >> 1); + addr += 256; + length -= 256; + } + } + + if (!(error)) + while (length > 0) { + unsigned long word; + + if (getb(io, 4) & STAT_INT) + break; + + if (!(getb(io, 48) & CSTATUS_IRQ)) + continue; + + word = getw(io, 16); + *addr++ = word; + if (--length > 0) { + *addr++ = word >> 8; + length --; + } + } + } +} + +/* + * Function: int arxescsi_dma_stop(host, SCpnt) + * Purpose : stops DMA/PIO + * Params : host - host + * SCpnt - command + */ +static void arxescsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) +{ + /* + * no DMA to stop + */ +} + +/* + * Function: int arxescsi_detect(Scsi_Host_Template * tpnt) + * Purpose : initialises ARXE SCSI driver + * Params : tpnt - template for this SCSI adapter + * Returns : >0 if host found, 0 otherwise. + */ +int arxescsi_detect(Scsi_Host_Template *tpnt) +{ + static const card_ids arxescsi_cids[] = { ARXESCSI_LIST, { 0xffff, 0xffff} }; + int count = 0; + struct Scsi_Host *host; + + tpnt->proc_dir = &proc_scsi_arxescsi; + memset(ecs, 0, sizeof (ecs)); + + ecard_startfind(); + + while (1) { + ARXEScsi_Info *info; + + ecs[count] = ecard_find(0, arxescsi_cids); + if (!ecs[count]) + break; + + ecard_claim(ecs[count]); + + host = scsi_register(tpnt, sizeof (ARXEScsi_Info)); + if (!host) { + ecard_release(ecs[count]); + break; + } + + host->io_port = ecard_address(ecs[count], ECARD_MEMC, 0) + 0x0800; + host->irq = NO_IRQ; + host->dma_channel = NO_DMA; + host->can_queue = 0; /* no command queueing */ + info = (ARXEScsi_Info *)host->hostdata; + + info->info.scsi.io_port = host->io_port; + info->info.scsi.irq = host->irq; + info->info.scsi.io_shift = 3; + info->info.ifcfg.clockrate = ARXESCSI_XTALFREQ; + info->info.ifcfg.select_timeout = 255; + info->info.ifcfg.asyncperiod = ARXESCSI_ASYNC_PERIOD; + info->info.ifcfg.sync_max_depth = ARXESCSI_SYNC_DEPTH; + info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK; + info->info.ifcfg.disconnect_ok = 0; + info->info.ifcfg.wide_max_size = 0; + info->info.dma.setup = arxescsi_dma_setup; + info->info.dma.pseudo = arxescsi_dma_pseudo; + info->info.dma.stop = arxescsi_dma_stop; + info->dmaarea = host->io_port + 128; + info->cstatus = host->io_port + 384; + + ecs[count]->irqaddr = (unsigned char *)BUS_ADDR(host->io_port); + ecs[count]->irqmask = CSTATUS_IRQ; + + request_region(host->io_port , 120, "arxescsi-fas"); + request_region(host->io_port + 128, 384, "arxescsi-dma"); + + printk("scsi%d: Has no interrupts - using polling mode\n", + host->host_no); + + fas216_init(host); + ++count; + } + return count; +} + +/* + * Function: int arxescsi_release(struct Scsi_Host * host) + * Purpose : releases all resources used by this adapter + * Params : host - driver host structure to return info for. + * Returns : nothing + */ +int arxescsi_release(struct Scsi_Host *host) +{ + int i; + + fas216_release(host); + + release_region(host->io_port, 120); + release_region(host->io_port + 128, 384); + + for (i = 0; i < MAX_ECARDS; i++) + if (ecs[i] && host->io_port == (ecard_address(ecs[i], ECARD_MEMC, 0) + 0x0800)) + ecard_release(ecs[i]); + return 0; +} + +/* + * Function: const char *arxescsi_info(struct Scsi_Host * host) + * Purpose : returns a descriptive string about this interface, + * Params : host - driver host structure to return info for. + * Returns : pointer to a static buffer containing null terminated string. + */ +const char *arxescsi_info(struct Scsi_Host *host) +{ + ARXEScsi_Info *info = (ARXEScsi_Info *)host->hostdata; + static char string[100], *p; + + p = string; + p += sprintf(string, "%s at port %lX irq %d v%d.%d.%d scsi %s", + host->hostt->name, host->io_port, host->irq, + VER_MAJOR, VER_MINOR, VER_PATCH, + info->info.scsi.type); + + return string; +} + +/* + * Function: int arxescsi_proc_info(char *buffer, char **start, off_t offset, + * int length, int host_no, int inout) + * Purpose : Return information about the driver to a user process accessing + * the /proc filesystem. + * Params : buffer - a buffer to write information to + * start - a pointer into this buffer set by this routine to the start + * of the required information. + * offset - offset into information that we have read upto. + * length - length of buffer + * host_no - host number to return information for + * inout - 0 for reading, 1 for writing. + * Returns : length of data written to buffer. + */ +int arxescsi_proc_info(char *buffer, char **start, off_t offset, + int length, int host_no, int inout) +{ + int pos, begin; + struct Scsi_Host *host = scsi_hostlist; + ARXEScsi_Info *info; + Scsi_Device *scd; + + while (host) { + if (host->host_no == host_no) + break; + host = host->next; + } + if (!host) + return 0; + + info = (ARXEScsi_Info *)host->hostdata; + if (inout == 1) + return -EINVAL; + + begin = 0; + pos = sprintf(buffer, + "ARXE 16-bit SCSI driver version %d.%d.%d\n", + VER_MAJOR, VER_MINOR, VER_PATCH); + pos += sprintf(buffer + pos, + "Address: %08lX IRQ : %d\n" + "FAS : %s\n\n" + "Statistics:\n", + host->io_port, host->irq, info->info.scsi.type); + + pos += fas216_print_stats(&info->info, buffer + pos); + + pos += sprintf (buffer+pos, "\nAttached devices:\n"); + + for (scd = host->host_queue; scd; scd = scd->next) { + pos += fas216_print_device(&info->info, scd, buffer + pos); + + if (pos + begin < offset) { + begin += pos; + pos = 0; + } + if (pos + begin > offset + length) + break; + } + + *start = buffer + (offset - begin); + pos -= offset - begin; + if (pos > length) + pos = length; + + return pos; +} + +#ifdef MODULE +Scsi_Host_Template driver_template = ARXEScsi; + +#include "../../scsi/scsi_module.c" +#endif diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/arxescsi.h linux/drivers/acorn/scsi/arxescsi.h --- v2.3.6/linux/drivers/acorn/scsi/arxescsi.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/acorn/scsi/arxescsi.h Thu Jun 17 01:11:35 1999 @@ -0,0 +1,80 @@ +/* + * ARXE SCSI card driver + * + * Copyright (C) 1997 Russell King + * Changes to support ARXE 16-bit SCSI card by Stefan Hanske + */ +#ifndef ARXE_SCSI_H +#define ARXE_SCSI_H + +#define MANU_ARXE 0x0041 +#define PROD_ARXE_SCSI 0x00be + +extern int arxescsi_detect (Scsi_Host_Template *); +extern int arxescsi_release (struct Scsi_Host *); +extern const char *arxescsi_info (struct Scsi_Host *); +extern int arxescsi_proc_info (char *buffer, char **start, off_t offset, + int length, int hostno, int inout); + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#ifndef CAN_QUEUE +/* + * Default queue size + */ +#define CAN_QUEUE 1 +#endif + +#ifndef CMD_PER_LUN +#define CMD_PER_LUN 1 +#endif + +#ifndef SCSI_ID +/* + * Default SCSI host ID + */ +#define SCSI_ID 7 +#endif + +#include + +#ifndef HOSTS_C +#include "fas216.h" +#endif + +#define ARXEScsi { \ +proc_info: arxescsi_proc_info, \ +name: "ARXE SCSI card", \ +detect: arxescsi_detect, /* detect */ \ +release: arxescsi_release, /* release */ \ +info: arxescsi_info, /* info */ \ +command: fas216_command, /* command */ \ +queuecommand: fas216_queue_command, /* queuecommand */ \ +abort: fas216_abort, /* abort */ \ +reset: fas216_reset, /* reset */ \ +bios_param: scsicam_bios_param, /* biosparam */ \ +can_queue: CAN_QUEUE, /* can queue */ \ +this_id: SCSI_ID, /* scsi host id */ \ +sg_tablesize: SG_ALL, /* sg_tablesize */ \ +cmd_per_lun: CMD_PER_LUN, /* cmd per lun */ \ +use_clustering: DISABLE_CLUSTERING \ + } + +#ifndef HOSTS_C + +typedef struct { + FAS216_Info info; + + /* other info... */ + unsigned int cstatus; /* card status register */ + unsigned int dmaarea; /* Pseudo DMA area */ +} ARXEScsi_Info; + +#define CSTATUS_IRQ (1 << 0) +#define CSTATUS_DRQ (1 << 0) + +#endif /* HOSTS_C */ + +#endif /* ARXE_SCSI_H */ diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/cumana_2.c linux/drivers/acorn/scsi/cumana_2.c --- v2.3.6/linux/drivers/acorn/scsi/cumana_2.c Thu Dec 17 09:07:45 1998 +++ linux/drivers/acorn/scsi/cumana_2.c Thu Jun 17 01:11:35 1999 @@ -4,12 +4,12 @@ * Copyright (C) 1997-1998 Russell King * * Changelog: - * 30-08-1997 RMK 0.0.0 Created, READONLY version - * 22-01-1998 RMK 0.0.1 Updated to 2.1.80 + * 30-08-1997 RMK 0.0.0 Created, READONLY version. + * 22-01-1998 RMK 0.0.1 Updated to 2.1.80. * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. - * 02-05-1998 RMK 0.0.2 Updated & added DMA support + * 02-05-1998 RMK 0.0.2 Updated & added DMA support. * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h - * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth + * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth. */ #include @@ -117,6 +117,8 @@ cumanascsi_2_irqenable, cumanascsi_2_irqdisable, NULL, + NULL, + NULL, NULL }; @@ -364,6 +366,7 @@ info->info.ifcfg.sync_max_depth = CUMANASCSI2_SYNC_DEPTH; info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; info->info.ifcfg.disconnect_ok = 1; + info->info.ifcfg.wide_max_size = 0; info->info.dma.setup = cumanascsi_2_dma_setup; info->info.dma.pseudo = cumanascsi_2_dma_pseudo; info->info.dma.stop = cumanascsi_2_dma_stop; diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/eesox.c linux/drivers/acorn/scsi/eesox.c --- v2.3.6/linux/drivers/acorn/scsi/eesox.c Thu Dec 17 09:07:45 1998 +++ linux/drivers/acorn/scsi/eesox.c Thu Jun 17 01:11:35 1999 @@ -38,9 +38,6 @@ #include "../../scsi/hosts.h" #include "eesox.h" -#define NO_IRQ 255 -#define NO_DMA 255 - /* Configuration */ #define EESOX_XTALFREQ 40 #define EESOX_ASYNC_PERIOD 200 @@ -123,6 +120,8 @@ eesoxscsi_irqenable, eesoxscsi_irqdisable, NULL, + NULL, + NULL, NULL }; @@ -379,6 +378,7 @@ info->info.ifcfg.sync_max_depth = EESOX_SYNC_DEPTH; info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; info->info.ifcfg.disconnect_ok = 1; + info->info.ifcfg.wide_max_size = 0; info->info.dma.setup = eesoxscsi_dma_setup; info->info.dma.pseudo = eesoxscsi_dma_pseudo; info->info.dma.stop = eesoxscsi_dma_stop; diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/fas216.c linux/drivers/acorn/scsi/fas216.c --- v2.3.6/linux/drivers/acorn/scsi/fas216.c Wed Dec 23 09:44:41 1998 +++ linux/drivers/acorn/scsi/fas216.c Thu Jun 17 01:11:35 1999 @@ -24,9 +24,9 @@ * 02-05-1998 RMK Added extra checks in fas216_reset * 24-05-1998 RMK Fixed synchronous transfers with period >= 200ns * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h + * 26-08-1998 RMK Improved message support wrt MESSAGE_REJECT * * Todo: - * - tighten up the MESSAGE_REJECT support. * - allow individual devices to enable sync xfers. */ @@ -57,7 +57,7 @@ #define VER_MAJOR 0 #define VER_MINOR 0 -#define VER_PATCH 4 +#define VER_PATCH 5 #define SCSI2_TAG @@ -86,6 +86,8 @@ */ #define SCSI2_SYNC +#define SCSI2_WIDE + #undef DEBUG_CONNECT #undef DEBUG_BUSSERVICE #undef DEBUG_FUNCTIONDONE @@ -132,8 +134,8 @@ printk(" SCp={ ptr=%p this_residual=%X buffer=%p buffers_residual=%X }\n", info->scsi.SCp.ptr, info->scsi.SCp.this_residual, info->scsi.SCp.buffer, info->scsi.SCp.buffers_residual); - printk(" msgs async_stp=%X last_message=%X disconnectable=%d aborting=%d }\n", - info->scsi.async_stp, info->scsi.last_message, + printk(" msgs async_stp=%X disconnectable=%d aborting=%d }\n", + info->scsi.async_stp, info->scsi.disconnectable, info->scsi.aborting); printk(" stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X\n" " disconnects=%X aborts=%X resets=%X }\n", @@ -144,10 +146,10 @@ info->ifcfg.clockrate, info->ifcfg.select_timeout, info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth); for (i = 0; i < 8; i++) { - printk(" busyluns[%d]=%X dev[%d]={ disconnect_ok=%d stp=%X sof=%X negstate=%X }\n", + printk(" busyluns[%d]=%X dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n", i, info->busyluns[i], i, info->device[i].disconnect_ok, info->device[i].stp, - info->device[i].sof, info->device[i].negstate); + info->device[i].sof, info->device[i].sync_state); } printk(" dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n", info->dma.transfer_type, info->dma.setup, @@ -192,19 +194,19 @@ static const char *fas216_drv_phase(FAS216_Info *info) { switch (info->scsi.phase) { - case PHASE_IDLE: return "idle"; - case PHASE_SELECTION: return "selection"; - case PHASE_MESSAGESENT: return "message sent"; - case PHASE_RECONNECTED: return "reconnected"; - case PHASE_DATAOUT: return "data out"; - case PHASE_DATAIN: return "data in"; - case PHASE_MSGOUT: return "message out"; - case PHASE_MSGIN: return "message in"; - case PHASE_AFTERMSGOUT: return "after message out"; - case PHASE_STATUS: return "status"; - case PHASE_DISCONNECT: return "disconnect"; - case PHASE_DONE: return "done"; - default: return "???"; + case PHASE_IDLE: return "idle"; + case PHASE_SELECTION: return "selection"; + case PHASE_COMMAND: return "command"; + case PHASE_RECONNECTED: return "reconnected"; + case PHASE_DATAOUT: return "data out"; + case PHASE_DATAIN: return "data in"; + case PHASE_MSGIN: return "message in"; + case PHASE_MSGIN_DISCONNECT: return "disconnect"; + case PHASE_MSGOUT_EXPECT: return "expect message out"; + case PHASE_MSGOUT: return "message out"; + case PHASE_STATUS: return "status"; + case PHASE_DONE: return "done"; + default: return "???"; } } @@ -262,6 +264,37 @@ return clock; } +/* Function: unsigned short fas216_get_last_msg(FAS216_Info *info, int pos) + * Purpose : retrieve a last message from the list, using position in fifo + * Params : info - interface to search + * : pos - current fifo position + */ +static inline unsigned short +fas216_get_last_msg(FAS216_Info *info, int pos) +{ + unsigned short packed_msg = NOP; + struct message *msg; + int msgnr = 0; + + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + if (pos >= msg->fifo) + break; + } + + if (msg) { + if (msg->msg[0] == EXTENDED_MESSAGE) + packed_msg = EXTENDED_MESSAGE | msg->msg[2] << 8; + else + packed_msg = msg->msg[0]; + } + +#ifdef DEBUG_MESSAGES + printk("Message: %04X found at position %02X\n", + packed_msg, pos); +#endif + return packed_msg; +} + /* Function: int fas216_syncperiod(FAS216_Info *info, int ns) * Purpose : Calculate value to be loaded into the STP register * for a given period in ns @@ -303,6 +336,240 @@ outb(info->scsi.cfg[2], REG_CNTL3(info)); } +/* Synchronous transfer support + * + * Note: The SCSI II r10 spec says (5.6.12): + * + * (2) Due to historical problems with early host adapters that could + * not accept an SDTR message, some targets may not initiate synchronous + * negotiation after a power cycle as required by this standard. Host + * adapters that support synchronous mode may avoid the ensuing failure + * modes when the target is independently power cycled by initiating a + * synchronous negotiation on each REQUEST SENSE and INQUIRY command. + * This approach increases the SCSI bus overhead and is not recommended + * for new implementations. The correct method is to respond to an + * SDTR message with a MESSAGE REJECT message if the either the + * initiator or target devices does not support synchronous transfers + * or does not want to negotiate for synchronous transfers at the time. + * Using the correct method assures compatibility with wide data + * transfers and future enhancements. + * + * We will always initiate a synchronous transfer negociation request on + * every INQUIRY or REQUEST SENSE message, unless the target itself has + * at some point performed a synchronous transfer negociation request, or + * we have synchronous transfers disabled for this device. + */ + +/* Function: void fas216_handlesync(FAS216_Info *info, char *msg) + * Purpose : Handle a synchronous transfer message from the target + * Params : info - state structure for interface + * : msg - message from target + */ +static void +fas216_handlesync(FAS216_Info *info, char *msg) +{ + struct fas216_device *dev = &info->device[info->SCpnt->target]; + enum { sync, async, none, reject } res = none; + +#ifdef SCSI2_SYNC + switch (msg[0]) { + case MESSAGE_REJECT: + /* Synchronous transfer request failed. + * Note: SCSI II r10: + * + * SCSI devices that are capable of synchronous + * data transfers shall not respond to an SDTR + * message with a MESSAGE REJECT message. + * + * Hence, if we get this condition, we disable + * negociation for this device. + */ + if (dev->sync_state == neg_inprogress) { + dev->sync_state = neg_invalid; + res = async; + } + break; + + case EXTENDED_MESSAGE: + switch (dev->sync_state) { + /* We don't accept synchronous transfer requests. + * Respond with a MESSAGE_REJECT to prevent a + * synchronous transfer agreement from being reached. + */ + case neg_invalid: + res = reject; + break; + + /* We were not negociating a synchronous transfer, + * but the device sent us a negociation request. + * Honour the request by sending back a SDTR + * message containing our capability, limited by + * the targets capability. + */ + default: + outb(CMD_SETATN, REG_CMD(info)); + if (msg[4] > info->ifcfg.sync_max_depth) + msg[4] = info->ifcfg.sync_max_depth; + if (msg[3] < 1000 / info->ifcfg.clockrate) + msg[3] = 1000 / info->ifcfg.clockrate; + + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 5, + EXTENDED_MESSAGE, 3, EXTENDED_SDTR, + msg[3], msg[4]); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + + /* This is wrong. The agreement is not in effect + * until this message is accepted by the device + */ + dev->sync_state = neg_targcomplete; + res = sync; + break; + + /* We initiated the synchronous transfer negociation, + * and have successfully received a response from the + * target. The synchronous transfer agreement has been + * reached. Note: if the values returned are out of our + * bounds, we must reject the message. + */ + case neg_inprogress: + res = reject; + if (msg[4] <= info->ifcfg.sync_max_depth && + msg[3] >= 1000 / info->ifcfg.clockrate) { + dev->sync_state = neg_complete; + res = sync; + } + break; + } + } +#else + res = reject; +#endif + + switch (res) { + case sync: + dev->period = msg[3]; + dev->sof = msg[4]; + dev->stp = fas216_syncperiod(info, msg[3] * 4); + fas216_set_sync(info, info->SCpnt->target); + break; + + case reject: + outb(CMD_SETATN, REG_CMD(info)); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + + case async: + dev->period = info->ifcfg.asyncperiod / 4; + dev->sof = 0; + dev->stp = info->scsi.async_stp; + fas216_set_sync(info, info->SCpnt->target); + break; + + case none: + break; + } +} + +/* Function: void fas216_handlewide(FAS216_Info *info, char *msg) + * Purpose : Handle a wide transfer message from the target + * Params : info - state structure for interface + * : msg - message from target + */ +static void +fas216_handlewide(FAS216_Info *info, char *msg) +{ + struct fas216_device *dev = &info->device[info->SCpnt->target]; + enum { wide, bit8, none, reject } res = none; + +#ifdef SCSI2_WIDE + switch (msg[0]) { + case MESSAGE_REJECT: + /* Wide transfer request failed. + * Note: SCSI II r10: + * + * SCSI devices that are capable of wide + * data transfers shall not respond to a + * WDTR message with a MESSAGE REJECT message. + * + * Hence, if we get this condition, we never + * reattempt negociation for this device. + */ + if (dev->wide_state == neg_inprogress) { + dev->wide_state = neg_invalid; + res = bit8; + } + break; + + case EXTENDED_MESSAGE: + switch (dev->wide_state) { + /* We don't accept wide data transfer requests. + * Respond with a MESSAGE REJECT to prevent a + * wide data transfer agreement from being reached. + */ + case neg_invalid: + res = reject; + break; + + /* We were not negociating a wide data transfer, + * but the device sent is a negociation request. + * Honour the request by sending back a WDTR + * message containing our capability, limited by + * the targets capability. + */ + default: + outb(CMD_SETATN, REG_CMD(info)); + if (msg[3] > info->ifcfg.wide_max_size) + msg[3] = info->ifcfg.wide_max_size; + + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 4, + EXTENDED_MESSAGE, 2, EXTENDED_WDTR, + msg[3]); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + res = wide; + break; + + /* We initiated the wide data transfer negociation, + * and have successfully received a response from the + * target. The synchronous transfer agreement has been + * reached. Note: if the values returned are out of our + * bounds, we must reject the message. + */ + case neg_inprogress: + res = reject; + if (msg[3] <= info->ifcfg.wide_max_size) { + dev->wide_state = neg_complete; + res = wide; + } + break; + } + } +#else + res = reject; +#endif + + switch (res) { + case wide: + dev->wide_xfer = msg[3]; + break; + + case reject: + outb(CMD_SETATN, REG_CMD(info)); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + + case bit8: + dev->wide_xfer = 0; + break; + + case none: + break; + } +} + /* Function: void fas216_updateptrs(FAS216_Info *info, int bytes_transferred) * Purpose : update data pointers after transfer suspended/paused * Params : info - interface's local pointer to update @@ -338,6 +605,9 @@ residual -= bytes_transferred; ptr += bytes_transferred; + if (residual == 0) + ptr = NULL; + info->scsi.SCp.ptr = ptr; info->scsi.SCp.this_residual = residual; } @@ -353,7 +623,7 @@ { unsigned int residual; char *ptr; - int correction; + int correction = 0; fas216_checkmagic(info, "fas216_pio"); @@ -361,23 +631,24 @@ ptr = info->scsi.SCp.ptr; if (direction == DMA_OUT) { - while (residual > 0) { - if ((inb(REG_CFIS(info)) & CFIS_CF) < 8) { +// while (residual > 0) { +// if ((inb(REG_CFIS(info)) & CFIS_CF) < 8) { outb(*ptr++, REG_FF(info)); residual -= 1; - } else if (inb(REG_STAT(info)) & STAT_INT) - break; - } - correction = inb(REG_CFIS(info)) & CFIS_CF; +// } +// if (inb(REG_STAT(info)) & STAT_INT) +// break; +// } +// correction = inb(REG_CFIS(info)) & CFIS_CF; } else { - while (residual > 0) { - if ((inb(REG_CFIS(info)) & CFIS_CF) != 0) { +// while (residual > 0) { +// if ((inb(REG_CFIS(info)) & CFIS_CF) != 0) { *ptr++ = inb(REG_FF(info)); residual -= 1; - } else if (inb(REG_STAT(info)) & STAT_INT) - break; - } - correction = 0; +// } +// if (inb(REG_STAT(info)) & STAT_INT) +// break; +// } } ptr -= correction; @@ -549,10 +820,11 @@ switch (info->scsi.phase) { case PHASE_SELECTION: /* while selecting - no target */ + case PHASE_SELSTEPS: fas216_done(info, DID_NO_CONNECT); break; - case PHASE_DISCONNECT: /* message in - disconnecting */ + case PHASE_MSGIN_DISCONNECT: /* message in - disconnecting */ outb(CMD_ENABLESEL, REG_CMD(info)); info->scsi.disconnectable = 1; info->scsi.reconnected.tag = 0; @@ -564,8 +836,8 @@ fas216_done(info, DID_OK); break; - case PHASE_AFTERMSGOUT: /* message out - possible ABORT message */ - if (info->scsi.last_message == ABORT) { + case PHASE_MSGOUT: /* message out - possible ABORT message */ + if (fas216_get_last_msg(info, info->scsi.msgin_fifo) == ABORT) { info->scsi.aborting = 0; fas216_done(info, DID_ABORT); break; @@ -592,14 +864,17 @@ fas216_checkmagic(info, "fas216_reselected_intr"); - if (info->scsi.phase == PHASE_SELECTION && info->SCpnt) { + if ((info->scsi.phase == PHASE_SELECTION || + info->scsi.phase == PHASE_SELSTEPS) && info->SCpnt) { Scsi_Cmnd *SCpnt = info->SCpnt; info->origSCpnt = SCpnt; info->SCpnt = NULL; - if (info->device[SCpnt->target].negstate == syncneg_sent) - info->device[SCpnt->target].negstate = syncneg_start; + if (info->device[SCpnt->target].wide_state == neg_inprogress) + info->device[SCpnt->target].wide_state = neg_wait; + if (info->device[SCpnt->target].sync_state == neg_inprogress) + info->device[SCpnt->target].sync_state = neg_wait; } #ifdef DEBUG_CONNECT @@ -607,15 +882,14 @@ fas216_target(info), info->scsi.phase); #endif - msgqueue_flush(&info->scsi.msgs); - if ((inb(REG_CFIS(info)) & CFIS_CF) != 2) { printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n", info->host->host_no); outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT; outb(CMD_MSGACCEPTED, REG_CMD(info)); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; return; } @@ -636,13 +910,14 @@ if (!ok) { /* - * Something went wrong - abort the command on - * the target. Should this be INITIATOR_ERROR ? + * Something went wrong - send an initiator error to + * the target. */ outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); - info->scsi.phase = PHASE_MSGOUT; outb(CMD_MSGACCEPTED, REG_CMD(info)); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; return; } @@ -672,17 +947,20 @@ if (!ok && queue_probetgtlun(&info->queues.disconnected, target, identify_msg)) ok = 1; + msgqueue_flush(&info->scsi.msgs); if (ok) { info->scsi.phase = PHASE_RECONNECTED; outb(target, REG_SDID(info)); } else { /* - * Our command structure not found - abort the command on the target - * Should this be INITIATOR_ERROR ? + * Our command structure not found - abort the + * command on the target. Since we have no + * record of this command, we can't send + * an INITIATOR DETECTED ERROR message. */ outb(CMD_SETATN, REG_CMD(info)); msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); - info->scsi.phase = PHASE_MSGOUT; + info->scsi.phase = PHASE_MSGOUT_EXPECT; } outb(CMD_MSGACCEPTED, REG_CMD(info)); } @@ -733,8 +1011,14 @@ } if (!info->SCpnt) { outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); - info->scsi.phase = PHASE_MSGOUT; + msgqueue_flush(&info->scsi.msgs); +#if 0 + if (info->scsi.reconnected.tag) + msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, info->scsi.reconnected.tag); + else +#endif + msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; info->scsi.aborting = 1; } else { /* @@ -751,6 +1035,28 @@ #endif } +static unsigned char fas216_get_msg_byte(FAS216_Info *info) +{ + int tout; + + outb(CMD_MSGACCEPTED, REG_CMD(info)); + for (tout = 1000000; tout; tout --) + if (inb(REG_STAT(info)) & STAT_INT) + break; + + inb(REG_INST(info)); + + outb(CMD_TRANSFERINFO, REG_CMD(info)); + + for (tout = 1000000; tout; tout --) + if (inb(REG_STAT(info)) & STAT_INT) + break; + + inb(REG_INST(info)); + + return inb(REG_FF(info)); +} + /* Function: void fas216_message(FAS216_Info *info) * Purpose : handle a function done interrupt from FAS216 chip * Params : info - interface which caused function done interrupt @@ -765,34 +1071,10 @@ message[0] = inb(REG_FF(info)); if (message[0] == EXTENDED_MESSAGE) { - int tout; - outb(CMD_MSGACCEPTED, REG_CMD(info)); - for (tout = 1000000; tout; tout--) - if (inb(REG_STAT(info)) & STAT_INT) - break; - inb(REG_INST(info)); - outb(CMD_TRANSFERINFO, REG_CMD(info)); - for (tout = 1000000; tout; tout--) - if (inb(REG_STAT(info)) & STAT_INT) - break; - inb(REG_INST(info)); - - message[1] = inb(REG_FF(info)); - - for (msglen = 2; msglen < message[1] + 2; msglen++) { - outb(CMD_MSGACCEPTED, REG_CMD(info)); - for (tout = 1000000; tout; tout--) - if (inb(REG_STAT(info)) & STAT_INT) - break; - inb(REG_INST(info)); - outb(CMD_TRANSFERINFO, REG_CMD(info)); - for (tout = 1000000; tout; tout--) - if (inb(REG_STAT(info)) & STAT_INT) - break; - inb(REG_INST(info)); + message[1] = fas216_get_msg_byte(info); - message[msglen] = inb(REG_FF(info)); - } + for (msglen = 2; msglen < message[1] + 2; msglen++) + message[msglen] = fas216_get_msg_byte(info); } #ifdef DEBUG_MESSAGES @@ -806,6 +1088,7 @@ printk("\n"); } #endif + if (info->scsi.phase == PHASE_RECONNECTED) { if (message[0] == SIMPLE_QUEUE_TAG) info->scsi.reconnected.tag = message[1]; @@ -815,14 +1098,22 @@ switch (message[0]) { case COMMAND_COMPLETE: - printk("fas216: command complete with no status in MESSAGE_IN?\n"); + printk(KERN_ERR "scsi%d.%c: command complete with no " + "status in MESSAGE_IN?\n", + info->host->host_no, fas216_target(info)); break; case SAVE_POINTERS: /* * Save current data pointer to SAVED data pointer + * SCSI II standard says that we must not acknowledge + * this until we have really saved pointers. + * NOTE: we DO NOT save the command nor status pointers + * as required by the SCSI II standard. These always + * point to the start of their respective areas. */ info->SCpnt->SCp = info->scsi.SCp; + info->SCpnt->SCp.sent_command = 0; #if defined (DEBUG_MESSAGES) || defined (DEBUG_CONNECT) printk("scsi%d.%c: save data pointers: [%p, %X]\n", info->host->host_no, fas216_target(info), @@ -843,13 +1134,27 @@ break; case DISCONNECT: - info->scsi.phase = PHASE_DISCONNECT; + info->scsi.phase = PHASE_MSGIN_DISCONNECT; break; case MESSAGE_REJECT: - printk("scsi%d.%c: reject, last message %04X\n", - info->host->host_no, fas216_target(info), - info->scsi.last_message); + switch (fas216_get_last_msg(info, info->scsi.msgin_fifo)) { + case EXTENDED_MESSAGE | EXTENDED_SDTR << 8: + fas216_handlesync(info, message); + break; + + case EXTENDED_MESSAGE | EXTENDED_WDTR << 8: + fas216_handlewide(info, message); + break; + + default: + printk("scsi%d.%c: reject, last message %04X\n", + info->host->host_no, fas216_target(info), + fas216_get_last_msg(info, info->scsi.msgin_fifo)); + } + break; + + case NOP: break; case SIMPLE_QUEUE_TAG: @@ -862,49 +1167,18 @@ case EXTENDED_MESSAGE: switch (message[2]) { case EXTENDED_SDTR: /* Sync transfer negociation request/reply */ - switch (info->device[info->SCpnt->target].negstate) { - case syncneg_invalid: - msgqueue_flush(&info->scsi.msgs); - outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT; - break; - - default: - if (message[4] > info->ifcfg.sync_max_depth) - message[4] = info->ifcfg.sync_max_depth; - if (message[3] < 1000 / info->ifcfg.clockrate) - message[3] = 1000 / info->ifcfg.clockrate; - - outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 5, - EXTENDED_MESSAGE, 3, EXTENDED_SDTR, - message[3], message[4]); - info->scsi.phase = PHASE_MSGOUT; - case syncneg_sent: - info->device[info->SCpnt->target].negstate = syncneg_complete; - info->device[info->SCpnt->target].period = message[3]; - info->device[info->SCpnt->target].sof = message[4]; - info->device[info->SCpnt->target].stp = - fas216_syncperiod(info, message[3] * 4); - printk(KERN_NOTICE "scsi%d.%c: using synchronous transfer, offset %d, %d ns\n", - info->host->host_no, fas216_target(info), message[4], message[3] * 4); - fas216_set_sync(info, info->SCpnt->target); - break; - } + fas216_handlesync(info, message); break; case EXTENDED_WDTR: /* Wide transfer negociation request/reply */ - /* We don't do wide transfers - reject message */ + fas216_handlewide(info, message); + break; + default: printk("scsi%d.%c: unrecognised extended message %02X, rejecting\n", info->host->host_no, fas216_target(info), message[2]); - msgqueue_flush(&info->scsi.msgs); - outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT; - break; + goto reject_message; } break; @@ -912,13 +1186,17 @@ printk("scsi%d.%c: unrecognised message %02X, rejecting\n", info->host->host_no, fas216_target(info), message[0]); - msgqueue_flush(&info->scsi.msgs); - outb(CMD_SETATN, REG_CMD(info)); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT; - break; + goto reject_message; } outb(CMD_MSGACCEPTED, REG_CMD(info)); + return; + +reject_message: + outb(CMD_SETATN, REG_CMD(info)); + outb(CMD_MSGACCEPTED, REG_CMD(info)); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; } /* Function: void fas216_send_command(FAS216_Info *info) @@ -935,201 +1213,46 @@ outb(CMD_FLUSHFIFO, REG_CMD(info)); /* load command */ - for (i = 0; i < info->SCpnt->cmd_len; i++) + for (i = info->scsi.SCp.sent_command; i < info->SCpnt->cmd_len; i++) outb(info->SCpnt->cmnd[i], REG_FF(info)); outb(CMD_TRANSFERINFO, REG_CMD(info)); -} - -/* Function: int fas216_busservice_selection(FAS216_Info *info, unsigned int stat) - * Purpose : handle bus service in selection phase - * Params : info - interface which caused bus service - * Returns : 0 if unable to service this interrupt - */ -static int fas216_busservice_selection(FAS216_Info *info, unsigned int stat) -{ - fas216_checkmagic(info, "fas216_busservice_selection"); - - switch (stat & STAT_BUSMASK) { - case STAT_DATAOUT: /* data out phase */ - fas216_starttransfer(info, DMA_OUT, 1); - return 1; - - case STAT_DATAIN: /* data in phase */ - fas216_starttransfer(info, DMA_IN, 0); - return 1; - - case STAT_STATUS: /* status phase */ - info->scsi.phase = PHASE_STATUS; - outb(CMD_INITCMDCOMPLETE, REG_CMD(info)); - return 1; - - case STAT_MESGIN: /* message in phase */ - info->scsi.phase = PHASE_MSGIN; - outb(CMD_TRANSFERINFO, REG_CMD(info)); - return 1; - - case STAT_MESGOUT:{ /* message out phase */ - char *msg; - int start = 1, msglen; - - /* load message bytes, but don't forget to miss the first - * byte! - */ - while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) { - int i; - - for (i = start; i < msglen; i++) - outb(msg[i], REG_FF(info)); - start = 0; - } - outb(CMD_TRANSFERINFO, REG_CMD(info)); - info->scsi.phase = PHASE_MESSAGESENT; - return 1; - } - default: - return 0; - } -} - -/* Function: int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat) - * Purpose : handle bus service after the IDENTIFY message has been sent - * Params : info - interface which caused bus service - * Returns : 0 if unable to service this interrupt - */ -static int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat) -{ - fas216_checkmagic(info, "fas216_busservice_messagesent"); - - switch (stat & STAT_BUSMASK) { - case STAT_MESGIN: /* message in phase */ - info->scsi.phase = PHASE_MSGIN; - outb(CMD_FLUSHFIFO, REG_CMD(info)); - outb(CMD_TRANSFERINFO, REG_CMD(info)); - return 1; - - case STAT_COMMAND: /* command phase */ - fas216_send_command(info); - return 1; - - default: - return 0; - } -} - -/* Function: int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat) - * Purpose : handle bus service in a data in/out phase. - * Params : info - interface which caused bus service - * Returns : 0 if unable to service this interrupt - * Note : We do not allow the device to change the data direction! - */ -static int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat) -{ - fas216_checkmagic(info, "fas216_busservice_dataphase"); - - switch (stat & STAT_BUSMASK) { - case STAT_DATAIN: /* continue data in phase */ - if (info->scsi.phase == PHASE_DATAIN) { - fas216_starttransfer(info, DMA_IN, 0); - return 1; - } else - return 0; - - case STAT_DATAOUT: /* continue data out phase */ - if (info->scsi.phase == PHASE_DATAOUT) { - fas216_starttransfer(info, DMA_OUT, 0); - return 1; - } else - return 0; - - case STAT_STATUS: /* status in phase */ - fas216_stoptransfer(info); - info->scsi.phase = PHASE_STATUS; - outb(CMD_INITCMDCOMPLETE, REG_CMD(info)); - return 1; - case STAT_MESGIN: /* message in phase */ - fas216_stoptransfer(info); - info->scsi.phase = PHASE_MSGIN; - outb(CMD_TRANSFERINFO, REG_CMD(info)); - return 1; - - default: - return 0; - } + info->scsi.phase = PHASE_COMMAND; } -/* Function: int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat) - * Purpose : handle bus service in after a reconnection +/* Function: void fas216_send_messageout(FAS216_Info *info, int start) + * Purpose : handle bus service to send a message * Params : info - interface which caused bus service - * Returns : 0 if unable to service this interrupt * Note : We do not allow the device to change the data direction! */ -static int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat) +static void fas216_send_messageout(FAS216_Info *info, int start) { - fas216_checkmagic(info, "fas216_busservice_reconnected"); - - switch (stat & STAT_BUSMASK) { - case STAT_MESGIN: - outb(CMD_TRANSFERINFO, REG_CMD(info)); - return 1; - - case STAT_STATUS: - fas216_finish_reconnect(info); - info->scsi.phase = PHASE_STATUS; - outb(CMD_INITCMDCOMPLETE, REG_CMD(info)); - return 1; + unsigned int tot_msglen = msgqueue_msglength(&info->scsi.msgs); - case STAT_DATAOUT: /* data out phase */ - fas216_finish_reconnect(info); - fas216_starttransfer(info, DMA_OUT, 1); - return 1; + fas216_checkmagic(info, "fas216_send_messageout"); - case STAT_DATAIN: /* data in phase */ - fas216_finish_reconnect(info); - fas216_starttransfer(info, DMA_IN, 0); - return 1; + outb(CMD_FLUSHFIFO, REG_CMD(info)); - default: - return 0; - } -} + if (tot_msglen) { + struct message *msg; + int msgnr = 0; -/* Function: int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat) - * Purpose : handle bus service to send a message - * Params : info - interface which caused bus service - * Returns : 0 if unable to service this interrupt - * Note : We do not allow the device to change the data direction! - */ -static int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat) -{ - fas216_checkmagic(info, "fas216_busservice_messageout"); - - if ((stat & STAT_BUSMASK) != STAT_MESGOUT) { - printk("scsi%d.%c: didn't manage MESSAGE OUT phase\n", - info->host->host_no, fas216_target(info)); - return 0; - } else { - unsigned int msglen = msgqueue_msglength(&info->scsi.msgs); + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + int i; - outb(CMD_FLUSHFIFO, REG_CMD(info)); + for (i = start; i < msg->length; i++) + outb(msg->msg[i], REG_FF(info)); - if (msglen == 0) - outb(NOP, REG_FF(info)); - else { - char *msg; + msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF); + start = 0; + } + } else + outb(NOP, REG_FF(info)); - while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) { - int i; + outb(CMD_TRANSFERINFO, REG_CMD(info)); - for (i = 0; i < msglen; i++) - outb(msg[i], REG_FF(info)); - } - } - outb(CMD_TRANSFERINFO, REG_CMD(info)); - info->scsi.phase = PHASE_AFTERMSGOUT; - return 1; - } + info->scsi.phase = PHASE_MSGOUT; } /* Function: void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr) @@ -1151,91 +1274,150 @@ case IS_COMPLETE: /* last action completed */ outb(CMD_NOP, REG_CMD(info)); - switch (info->scsi.phase) { - case PHASE_SELECTION: /* while selecting - selected target */ - if (!fas216_busservice_selection(info, stat)) - printk("scsi%d.%c: bus phase %s after connect?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat)); - break; - - case PHASE_MESSAGESENT: - if (!fas216_busservice_messagesent(info, stat)) - printk("scsi%d.%c: bus phase %s after message sent?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat)); - break; +#define STATE(st,ph) ((ph) << 3 | (st)) + /* This table describes the legal SCSI state transitions, + * as described by the SCSI II spec. + */ + switch (STATE(stat & STAT_BUSMASK, info->scsi.phase)) { + /* Reselmsgin -> Data In */ + case STATE(STAT_DATAIN, PHASE_RECONNECTED): + fas216_finish_reconnect(info); + case STATE(STAT_DATAIN, PHASE_SELSTEPS):/* Sel w/ steps -> Data In */ + case STATE(STAT_DATAIN, PHASE_DATAIN): /* Data In -> Data In */ + case STATE(STAT_DATAIN, PHASE_MSGOUT): /* Message Out -> Data In */ + case STATE(STAT_DATAIN, PHASE_COMMAND): /* Command -> Data In */ + case STATE(STAT_DATAIN, PHASE_MSGIN): /* Message In -> Data In */ + fas216_starttransfer(info, DMA_IN, 0); + return; - case PHASE_DATAIN: /* while transfering data in */ - case PHASE_DATAOUT: /* while transfering data out */ - if (!fas216_busservice_dataphase(info, stat)) - printk("scsi%d.%c: bus phase %s after %s?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat), fas216_drv_phase(info)); - break; + case STATE(STAT_DATAOUT, PHASE_DATAOUT):/* Data Out -> Data Out */ + fas216_starttransfer(info, DMA_OUT, 0); + return; - case PHASE_RECONNECTED: /* newly reconnected device */ - /* - * Command reconnected - if MESGIN, get message - it may be - * the tag. If not, get command out of the disconnected queue - */ - if (!fas216_busservice_reconnected(info, stat)) - printk("scsi%d.%c: bus phase %s after reconnect?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat)); - break; + /* Reselmsgin -> Data Out */ + case STATE(STAT_DATAOUT, PHASE_RECONNECTED): + fas216_finish_reconnect(info); + case STATE(STAT_DATAOUT, PHASE_SELSTEPS):/* Sel w/ steps-> Data Out */ + case STATE(STAT_DATAOUT, PHASE_MSGOUT): /* Message Out -> Data Out */ + case STATE(STAT_DATAOUT, PHASE_COMMAND):/* Command -> Data Out */ + case STATE(STAT_DATAOUT, PHASE_MSGIN): /* Message In -> Data Out */ + fas216_starttransfer(info, DMA_OUT, 1); + return; + + /* Reselmsgin -> Status */ + case STATE(STAT_STATUS, PHASE_RECONNECTED): + fas216_finish_reconnect(info); + goto status; + case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out -> Status */ + case STATE(STAT_STATUS, PHASE_DATAIN): /* Data In -> Status */ + fas216_stoptransfer(info); + case STATE(STAT_STATUS, PHASE_SELSTEPS):/* Sel w/ steps -> Status */ + case STATE(STAT_STATUS, PHASE_MSGOUT): /* Message Out -> Status */ + case STATE(STAT_STATUS, PHASE_COMMAND): /* Command -> Status */ + case STATE(STAT_STATUS, PHASE_MSGIN): /* Message In -> Status */ + status: + outb(CMD_INITCMDCOMPLETE, REG_CMD(info)); + info->scsi.phase = PHASE_STATUS; + return; + + case STATE(STAT_MESGIN, PHASE_DATAOUT): /* Data Out -> Message In */ + case STATE(STAT_MESGIN, PHASE_DATAIN): /* Data In -> Message In */ + fas216_stoptransfer(info); + case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In */ + case STATE(STAT_MESGIN, PHASE_MSGOUT): /* Message Out -> Message In */ + info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF; + outb(CMD_TRANSFERINFO, REG_CMD(info)); + info->scsi.phase = PHASE_MSGIN; + return; - case PHASE_MSGIN: - case PHASE_AFTERMSGOUT: - switch (stat & STAT_BUSMASK) { - case STAT_MESGIN: - info->scsi.phase = PHASE_MSGIN; - outb(CMD_TRANSFERINFO, REG_CMD(info)); - break; + /* Reselmsgin -> Message In */ + case STATE(STAT_MESGIN, PHASE_RECONNECTED): + case STATE(STAT_MESGIN, PHASE_MSGIN): + info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF; + outb(CMD_TRANSFERINFO, REG_CMD(info)); + return; - case STAT_COMMAND: /* command phase */ - fas216_send_command(info); - info->scsi.phase = PHASE_SELECTION; - break; + /* Reselmsgin -> Command */ + case STATE(STAT_COMMAND, PHASE_RECONNECTED): + fas216_finish_reconnect(info); + case STATE(STAT_COMMAND, PHASE_MSGOUT): /* Message Out -> Command */ + case STATE(STAT_COMMAND, PHASE_MSGIN): /* Message In -> Command */ + fas216_send_command(info); + info->scsi.phase = PHASE_COMMAND; + return; + /* Selection -> Message Out */ + case STATE(STAT_MESGOUT, PHASE_SELECTION): + fas216_send_messageout(info, 1); + return; + /* Any -> Message Out */ + case STATE(STAT_MESGOUT, PHASE_MSGOUT_EXPECT): + fas216_send_messageout(info, 0); + return; + + /* Error recovery rules. + * These either attempt to abort or retry the operation. + * TODO: we need more of these + */ + case STATE(STAT_COMMAND, PHASE_COMMAND):/* Command -> Command */ + /* error - we've sent out all the command bytes + * we have. + * NOTE: we need SAVE DATA POINTERS/RESTORE DATA POINTERS + * to include the command bytes sent for this to work + * correctly. + */ + printk(KERN_ERR "scsi%d.%c: " + "target trying to receive more command bytes\n", + info->host->host_no, fas216_target(info)); + outb(CMD_SETATN, REG_CMD(info)); + outb(15, REG_STCL(info)); + outb(0, REG_STCM(info)); + outb(0, REG_STCH(info)); + outb(CMD_PADBYTES | CMD_WITHDMA, REG_CMD(info)); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + return; - default: - printk("scsi%d.%c: bus phase %s after %s?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat), - fas216_drv_phase(info)); - } - break; + /* Selection -> Message Out */ + case STATE(STAT_MESGOUT, PHASE_SELSTEPS): + case STATE(STAT_MESGOUT, PHASE_MSGOUT): /* Message Out -> Message Out */ + /* If we get another message out phase, this + * usually means some parity error occurred. + * Resend complete set of messages. If we have + * more than 1 byte to send, we need to assert + * ATN again. + */ + if (msgqueue_msglength(&info->scsi.msgs) > 1) + outb(CMD_SETATN, REG_CMD(info)); - case PHASE_MSGOUT: - if (!fas216_busservice_messageout(info, stat)) - printk("scsi%d.%c: bus phase %s instead of message out?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat)); - break; + fas216_send_messageout(info, 0); + return; + } - case PHASE_DISCONNECT: - printk("scsi%d.%c: disconnect message received, but bus service %s?\n", + if (info->scsi.phase == PHASE_MSGIN_DISCONNECT) { + printk(KERN_ERR "scsi%d.%c: disconnect message received, but bus service %s?\n", info->host->host_no, fas216_target(info), fas216_bus_phase(stat)); + msgqueue_flush(&info->scsi.msgs); outb(CMD_SETATN, REG_CMD(info)); msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); - info->scsi.phase = PHASE_MSGOUT; + info->scsi.phase = PHASE_MSGOUT_EXPECT; info->scsi.aborting = 1; outb(CMD_TRANSFERINFO, REG_CMD(info)); - break; - - default: - printk("scsi%d.%c: internal phase %s for bus service?" - " What do I do with this?\n", - info->host->host_no, fas216_target(info), - fas216_drv_phase(info)); + return; } - break; + printk(KERN_ERR "scsi%d.%c: bus phase %s after %s?\n", + info->host->host_no, fas216_target(info), + fas216_bus_phase(stat), + fas216_drv_phase(info)); + print_debug_list(); + return; default: printk("scsi%d.%c: bus service at step %d?\n", info->host->host_no, fas216_target(info), ssr & IS_BITS); + print_debug_list(); } } @@ -1269,6 +1451,7 @@ case PHASE_MSGIN: /* message in phase */ case PHASE_RECONNECTED: /* reconnected command */ if ((stat & STAT_BUSMASK) == STAT_MESGIN) { + info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF; fas216_message(info); break; } @@ -1300,10 +1483,11 @@ if (stat & STAT_INT) { if (isr & INST_BUSRESET) - printk("scsi%d.H: fas216: bus reset detected\n", instance->host_no); - else if (isr & INST_ILLEGALCMD) + printk(KERN_DEBUG "scsi%d.H: bus reset detected\n", instance->host_no); + else if (isr & INST_ILLEGALCMD) { printk(KERN_CRIT "scsi%d.H: illegal command given\n", instance->host_no); - else if (isr & INST_DISCONNECT) + fas216_dumpstate(info); + } else if (isr & INST_DISCONNECT) fas216_disconnect_intr(info); else if (isr & INST_RESELECTED) /* reselected */ fas216_reselected_intr(info); @@ -1327,7 +1511,7 @@ static void fas216_kick(FAS216_Info *info) { Scsi_Cmnd *SCpnt; - int i, msglen, from_queue = 0; + int tot_msglen, from_queue = 0; fas216_checkmagic(info, "fas216_kick"); @@ -1380,7 +1564,8 @@ if (from_queue) { #ifdef SCSI2_TAG - if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE) { + if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE && + SCpnt->cmnd[0] != INQUIRY) { SCpnt->device->current_tag += 1; if (SCpnt->device->current_tag == 0) SCpnt->device->current_tag = 1; @@ -1409,6 +1594,7 @@ /* build outgoing message bytes */ msgqueue_flush(&info->scsi.msgs); + if (info->device[SCpnt->target].disconnect_ok) msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(1, SCpnt->lun)); else @@ -1418,15 +1604,29 @@ if (SCpnt->tag) msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag); - /* add synchronous negociation */ - if (SCpnt->cmnd[0] == REQUEST_SENSE && - info->device[SCpnt->target].negstate == syncneg_start) { - info->device[SCpnt->target].negstate = syncneg_sent; +#ifdef SCSI2_WIDE + if (info->device[SCpnt->target].wide_state == neg_wait) { + info->device[SCpnt->target].wide_state = neg_inprogress; + msgqueue_addmsg(&info->scsi.msgs, 4, + EXTENDED_MESSAGE, 2, EXTENDED_WDTR, + info->ifcfg.wide_max_size); + } +#ifdef SCSI2_SYNC + else +#endif +#endif +#ifdef SCSI2_SYNC + if ((info->device[SCpnt->target].sync_state == neg_wait || + info->device[SCpnt->target].sync_state == neg_complete) && + (SCpnt->cmnd[0] == REQUEST_SENSE || + SCpnt->cmnd[0] == INQUIRY)) { + info->device[SCpnt->target].sync_state = neg_inprogress; msgqueue_addmsg(&info->scsi.msgs, 5, EXTENDED_MESSAGE, 3, EXTENDED_SDTR, 1000 / info->ifcfg.clockrate, info->ifcfg.sync_max_depth); } +#endif /* following what the ESP driver says */ outb(0, REG_STCL(info)); @@ -1444,25 +1644,29 @@ /* synchronous transfers */ fas216_set_sync(info, SCpnt->target); - msglen = msgqueue_msglength(&info->scsi.msgs); + tot_msglen = msgqueue_msglength(&info->scsi.msgs); - if (msglen == 1 || msglen == 3) { + if (tot_msglen == 1 || tot_msglen == 3) { /* * We have an easy message length to send... */ - char *msg; + struct message *msg; + int msgnr = 0, i; + + info->scsi.phase = PHASE_SELSTEPS; /* load message bytes */ - while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) { - for (i = 0; i < msglen; i++) - outb(msg[i], REG_FF(info)); + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + for (i = 0; i < msg->length; i++) + outb(msg->msg[i], REG_FF(info)); + msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF); } /* load command */ for (i = 0; i < SCpnt->cmd_len; i++) outb(SCpnt->cmnd[i], REG_FF(info)); - if (msglen == 1) + if (tot_msglen == 1) outb(CMD_SELECTATN, REG_CMD(info)); else outb(CMD_SELECTATN3, REG_CMD(info)); @@ -1471,17 +1675,11 @@ * We have an unusual number of message bytes to send. * Load first byte into fifo, and issue SELECT with ATN and * stop steps. - * Note: we only peek at t his message - we need the rest - * later on! */ - int thismsg; - char *msg = msgqueue_peeknextmsg(&info->scsi.msgs, &thismsg); + struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0); - if (!msg || thismsg < 1) - printk(KERN_CRIT "scsi%d.%c: no message to send, but %d bytes\n", - info->host->host_no, fas216_target(info), msglen); - else - outb(msg[0], REG_FF(info)); + outb(msg->msg[0], REG_FF(info)); + msg->fifo = 1; outb(CMD_SELECTATNSTOP, REG_CMD(info)); } @@ -1525,11 +1723,15 @@ /* * In theory, this should not happen, but just in case it does. */ - if (info->scsi.SCp.ptr && result == DID_OK) { + if (info->scsi.SCp.ptr && + info->scsi.SCp.this_residual && + result == DID_OK) { switch (SCpnt->cmnd[0]) { case INQUIRY: case START_STOP: case READ_CAPACITY: + case TEST_UNIT_READY: + case MODE_SENSE: break; default: @@ -1579,6 +1781,7 @@ int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; + unsigned long flags; fas216_checkmagic(info, "fas216_queue_command"); @@ -1618,27 +1821,18 @@ info->stats.queues += 1; SCpnt->tag = 0; - if (info->scsi.irq != NO_IRQ) { - unsigned long flags; - - /* add command into execute queue and let it complete under - * the drivers interrupts. - */ - if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) { - SCpnt->result = DID_ERROR << 16; - done(SCpnt); - } - save_flags_cli(flags); - if (!info->SCpnt || info->scsi.disconnectable) - fas216_kick(info); - restore_flags(flags); - } else { - /* no interrupts to rely on - we'll have to handle the - * command ourselves. For now, we give up. - */ + /* add command into execute queue and let it complete under + * whatever scheme we're using. + */ + if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) { SCpnt->result = DID_ERROR << 16; done(SCpnt); } + save_flags_cli(flags); + if (!info->SCpnt || info->scsi.disconnectable) + fas216_kick(info); + restore_flags(flags); + return 0; } @@ -1672,15 +1866,28 @@ /* * This wastes time, since we can't return until the command is - * complete. We can't seep either since we may get re-entered! + * complete. We can't sleep either since we may get re-entered! * However, we must re-enable interrupts, or else we'll be * waiting forever. */ save_flags(flags); sti(); - while (!info->internal_done) - barrier(); + while (!info->internal_done) { + /* + * If we don't have an IRQ, then we must + * poll the card for it's interrupt, and + * use that to call this driver's interrupt + * routine. That way, we keep the command + * progressing. + */ + if (info->scsi.irq == NO_IRQ) { + sti(); + while (!(inb(REG_STAT(info)) & STAT_INT)); + cli(); + fas216_intr(info->host); + } + } restore_flags(flags); @@ -1752,6 +1959,95 @@ return FAILED; } +enum res_abort { res_not_running, res_success, res_success_clear, res_snooze }; + +/* + * Prototype: enum res_abort fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt) + * Purpose : abort a command on this host + * Params : SCpnt - command to abort + * Returns : abort status + */ +static enum res_abort +fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt) +{ + enum res_abort res = res_not_running; + + if (queue_removecmd(&info->queues.issue, SCpnt)) { + /* + * The command was on the issue queue, and has not been + * issued yet. We can remove the command from the queue, + * and acknowledge the abort. Neither the devices nor the + * interface know about the command. + */ + printk("on issue queue "); + + res = res_success; + } else if (queue_removecmd(&info->queues.disconnected, SCpnt)) { + /* + * The command was on the disconnected queue. Simply + * acknowledge the abort condition, and when the target + * reconnects, we will give it an ABORT message. The + * target should then disconnect, and we will clear + * the busylun bit. + */ + printk("on disconnected queue "); + + res = res_success; + } else if (info->SCpnt == SCpnt) { + unsigned long flags; + + printk("executing "); + + save_flags(flags); + cli(); + switch (info->scsi.phase) { + /* + * If the interface is idle, and the command is 'disconnectable', + * then it is the same as on the disconnected queue. We simply + * remove all traces of the command. When the target reconnects, + * we will give it an ABORT message since the command could not + * be found. When the target finally disconnects, we will clear + * the busylun bit. + */ + case PHASE_IDLE: + if (info->scsi.disconnectable) { + info->scsi.disconnectable = 0; + info->SCpnt = NULL; + res = res_success; + } + break; + + /* + * If the command has connected and done nothing futher, + * simply force a disconnect. We also need to clear the + * busylun bit. + */ + case PHASE_SELECTION: +// info->SCpnt = NULL; +// res = res_success_clear; +// break; + + default: + res = res_snooze; + break; + } + restore_flags(flags); + } else if (info->origSCpnt == SCpnt) { + /* + * The command will be executed next, but a command + * is currently using the interface. This is similar to + * being on the issue queue, except the busylun bit has + * been set. + */ + info->origSCpnt = NULL; + printk("waiting for execution "); + res = res_success_clear; + } else + printk("unknown "); + + return res; +} + /* Function: int fas216_abort(Scsi_Cmnd *SCpnt) * Purpose : abort a command if something horrible happens. * Params : SCpnt - Command that is believed to be causing a problem. @@ -1769,46 +2065,51 @@ print_debug_list(); fas216_dumpstate(info); fas216_dumpinfo(info); - printk(KERN_WARNING "scsi%d: fas216_abort: ", info->host->host_no); - do { - /* If command is waiting in the issue queue, then we can - * simply remove the command and return abort status - */ - if (queue_removecmd(&info->queues.issue, SCpnt)) { - SCpnt->result = DID_ABORT << 16; - SCpnt->scsi_done(SCpnt); - printk("command on issue queue"); - result = SCSI_ABORT_SUCCESS; - break; - } + printk(KERN_WARNING "scsi%d: abort ", info->host->host_no); - /* If the command is on the disconencted queue, we need to - * reconnect to the device - */ - if (queue_cmdonqueue(&info->queues.disconnected, SCpnt)) - printk("command on disconnected queue"); + switch (fas216_do_abort(info, SCpnt)) { + /* + * We managed to find the command and cleared it out. + * We do not expect the command to be executing on the + * target, but we have set the busylun bit. + */ + case res_success_clear: + printk("clear "); + clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns); - /* If the command is connected, we need to flag that the - * command needs to be aborted - */ - if (info->SCpnt == SCpnt) - printk("command executing"); + /* + * We found the command, and cleared it out. Either + * the command is still known to be executing on the + * target, or the busylun bit is not set. + */ + case res_success: + printk("success\n"); + SCpnt->result = DID_ABORT << 16; + SCpnt->scsi_done(SCpnt); + result = SCSI_ABORT_SUCCESS; + break; - /* If the command is pending for execution, then again - * this is simple - we remove it and report abort status - */ - if (info->origSCpnt == SCpnt) { - info->origSCpnt = NULL; - SCpnt->result = DID_ABORT << 16; - SCpnt->scsi_done(SCpnt); - printk("command waiting for execution"); - result = SCSI_ABORT_SUCCESS; - break; - } - } while (0); + /* + * We did find the command, but unfortunately we couldn't + * unhook it from ourselves. Wait some more, and if it + * still doesn't complete, reset the interface. + */ + case res_snooze: + printk("snooze\n"); + result = SCSI_ABORT_SNOOZE; + break; - printk("\n"); + /* + * The command could not be found (either because it completed, + * or it got dropped. + */ + default: + case res_not_running: + result = SCSI_ABORT_SNOOZE; + printk("not running\n"); + break; + } return result; } @@ -1819,7 +2120,7 @@ */ static void fas216_reset_state(FAS216_Info *info) { - syncneg_t negstate; + neg_t sync_state, wide_state; int i; fas216_checkmagic(info, "fas216_reset_state"); @@ -1833,26 +2134,37 @@ info->scsi.reconnected.lun = 0; info->scsi.reconnected.tag = 0; info->scsi.disconnectable = 0; - info->scsi.last_message = 0; info->scsi.aborting = 0; info->scsi.phase = PHASE_IDLE; - info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod); + info->scsi.async_stp = + fas216_syncperiod(info, info->ifcfg.asyncperiod); + + if (info->ifcfg.wide_max_size == 0) + wide_state = neg_invalid; + else +#ifdef SCSI2_WIDE + wide_state = neg_wait; +#else + wide_state = neg_invalid; +#endif if (info->host->dma_channel == NO_DMA || !info->dma.setup) - negstate = syncneg_invalid; + sync_state = neg_invalid; else #ifdef SCSI2_SYNC - negstate = syncneg_start; + sync_state = neg_wait; #else - negstate = syncneg_invalid; + sync_state = neg_invalid; #endif for (i = 0; i < 8; i++) { - info->device[i].disconnect_ok = info->ifcfg.disconnect_ok; - info->device[i].negstate = negstate; - info->device[i].period = info->ifcfg.asyncperiod / 4; - info->device[i].stp = info->scsi.async_stp; - info->device[i].sof = 0; + info->device[i].disconnect_ok = info->ifcfg.disconnect_ok; + info->device[i].sync_state = sync_state; + info->device[i].wide_state = wide_state; + info->device[i].period = info->ifcfg.asyncperiod / 4; + info->device[i].stp = info->scsi.async_stp; + info->device[i].sof = 0; + info->device[i].wide_xfer = 0; } } @@ -1908,9 +2220,11 @@ info->stats.resets += 1; print_debug_list(); - printk(KERN_WARNING "scsi%d: fas216_reset: ", info->host->host_no); + printk(KERN_WARNING "scsi%d: reset ", info->host->host_no); if (SCpnt) - printk(" for target %d ", SCpnt->target); + printk("for target %d ", SCpnt->target); + + printk("\n"); outb(info->scsi.cfg[3], REG_CNTL3(info)); @@ -1963,8 +2277,6 @@ SCpnt->scsi_done(SCpnt); } - printk("\n"); - return result | SCSI_RESET_SUCCESS; } @@ -2083,6 +2395,49 @@ return 0; } +int fas216_print_stats(FAS216_Info *info, char *buffer) +{ + return sprintf(buffer, + "Queued commands: %-10u Issued commands: %-10u\n" + "Done commands : %-10u Reads : %-10u\n" + "Writes : %-10u Others : %-10u\n" + "Disconnects : %-10u Aborts : %-10u\n" + "Resets : %-10u\n", + info->stats.queues, info->stats.removes, + info->stats.fins, info->stats.reads, + info->stats.writes, info->stats.miscs, + info->stats.disconnects, info->stats.aborts, + info->stats.resets); +} + +int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer) +{ + struct fas216_device *dev = &info->device[scd->id]; + int len = 0; + char *p; + + proc_print_scsidevice(scd, buffer, &len, 0); + p = buffer + len; + + p += sprintf(p, " Extensions: "); + + if (scd->tagged_supported) + p += sprintf(p, "TAG %sabled [%d] ", + scd->tagged_queue ? "en" : "dis", + scd->current_tag); + + p += sprintf(p, "\n Transfers : %d-bit ", + 8 << dev->wide_xfer); + + if (dev->sof) + p += sprintf(p, "sync offset %d, %d ns\n", + dev->sof, dev->period * 4); + else + p += sprintf(p, "async\n"); + + return p - buffer; +} + EXPORT_SYMBOL(fas216_init); EXPORT_SYMBOL(fas216_abort); EXPORT_SYMBOL(fas216_reset); @@ -2094,7 +2449,8 @@ EXPORT_SYMBOL(fas216_eh_device_reset); EXPORT_SYMBOL(fas216_eh_bus_reset); EXPORT_SYMBOL(fas216_eh_host_reset); - +EXPORT_SYMBOL(fas216_print_stats); +EXPORT_SYMBOL(fas216_print_device); #ifdef MODULE int init_module(void) diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/fas216.h linux/drivers/acorn/scsi/fas216.h --- v2.3.6/linux/drivers/acorn/scsi/fas216.h Sat Jul 18 11:55:24 1998 +++ linux/drivers/acorn/scsi/fas216.h Thu Jun 17 01:11:35 1999 @@ -40,6 +40,7 @@ #define CMD_TRANSFERINFO 0x10 #define CMD_INITCMDCOMPLETE 0x11 #define CMD_MSGACCEPTED 0x12 +#define CMD_PADBYTES 0x18 #define CMD_SETATN 0x1a #define CMD_RSETATN 0x1b @@ -171,15 +172,17 @@ typedef enum { PHASE_IDLE, /* we're not planning on doing anything */ PHASE_SELECTION, /* selecting a device */ + PHASE_SELSTEPS, /* selection with command steps */ + PHASE_COMMAND, /* command sent */ PHASE_MESSAGESENT, /* selected, and we're sending cmd */ PHASE_RECONNECTED, /* reconnected */ PHASE_DATAOUT, /* data out to device */ PHASE_DATAIN, /* data in from device */ PHASE_MSGIN, /* message in from device */ - PHASE_MSGOUT, /* message out to device */ - PHASE_AFTERMSGOUT, /* after message out phase */ + PHASE_MSGIN_DISCONNECT, /* disconnecting from bus */ + PHASE_MSGOUT, /* after message out phase */ + PHASE_MSGOUT_EXPECT, /* expecting message out */ PHASE_STATUS, /* status from device */ - PHASE_DISCONNECT, /* disconnecting from bus */ PHASE_DONE /* Command complete */ } phase_t; @@ -197,13 +200,15 @@ } fasdmatype_t; typedef enum { - syncneg_start, /* Negociate with device for Sync xfers */ - syncneg_sent, /* Sync Xfer negociation sent */ - syncneg_complete, /* Sync Xfer complete */ - syncneg_invalid /* Sync Xfer not supported */ -} syncneg_t; + neg_wait, /* Negociate with device */ + neg_inprogress, /* Negociation sent */ + neg_complete, /* Negociation complete */ + neg_targcomplete, /* Target completed negociation */ + neg_invalid /* Negociation not supported */ +} neg_t; #define MAGIC 0x441296bdUL +#define NR_MSGS 8 typedef struct { unsigned long magic_start; @@ -231,7 +236,7 @@ MsgQueue_t msgs; /* message queue for connected device */ unsigned int async_stp; /* Async transfer STP value */ - unsigned short last_message; /* last message to be sent */ + unsigned char msgin_fifo; /* bytes in fifo at time of message in */ unsigned char disconnectable:1; /* this command can be disconnected */ unsigned char aborting:1; /* aborting command */ @@ -255,6 +260,7 @@ unsigned char clockrate; /* clock rate of FAS device (MHz) */ unsigned char select_timeout; /* timeout (R5) */ unsigned char sync_max_depth; /* Synchronous xfer max fifo depth */ + unsigned char wide_max_size; /* Maximum wide transfer size */ unsigned char cntl3; /* Control Reg 3 */ unsigned int asyncperiod; /* Async transfer period (ns) */ unsigned int disconnect_ok:1; /* Disconnects allowed? */ @@ -267,12 +273,14 @@ } queues; /* per-device info */ - struct { + struct fas216_device { unsigned char disconnect_ok:1; /* device can disconnect */ - unsigned int period; /* sync xfer period (*4ns) */ + unsigned char period; /* sync xfer period in (*4ns) */ unsigned char stp; /* synchronous transfer period */ unsigned char sof; /* synchronous offset register */ - syncneg_t negstate; /* synchronous transfer mode */ + unsigned char wide_xfer; /* currently negociated wide transfer */ + neg_t sync_state; /* synchronous transfer mode */ + neg_t wide_state; /* wide transfer mode */ } device[8]; unsigned char busyluns[8]; /* array of bits indicating LUNs busy */ @@ -339,6 +347,9 @@ * Returns : 0 on success */ extern int fas216_release (struct Scsi_Host *instance); + +extern int fas216_print_stats(FAS216_Info *info, char *buffer); +extern int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer); /* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt) * Purpose : abort this command diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/msgqueue.c linux/drivers/acorn/scsi/msgqueue.c --- v2.3.6/linux/drivers/acorn/scsi/msgqueue.c Sat Jul 18 11:55:24 1998 +++ linux/drivers/acorn/scsi/msgqueue.c Thu Jun 17 01:11:35 1999 @@ -83,45 +83,25 @@ int length = 0; for (mq = msgq->qe; mq; mq = mq->next) - length += mq->length; + length += mq->msg.length; return length; } /* - * Function: char *msgqueue_getnextmsg(MsgQueue_t *msgq, int *length) - * Purpose : return a message & its length + * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) + * Purpose : return a message * Params : msgq - queue to obtain message from - * length - pointer to int for message length + * : msgno - message number * Returns : pointer to message string, or NULL */ -char *msgqueue_getnextmsg(MsgQueue_t *msgq, int *length) +struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) { struct msgqueue_entry *mq; - if ((mq = msgq->qe) != NULL) { - msgq->qe = mq->next; - mqe_free(msgq, mq); - *length = mq->length; - } - - return mq ? mq->msg : NULL; -} - -/* - * Function: char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length) - * Purpose : return next message & length without removing it from the list - * Params : msgq - queue to obtain message from - * : length - pointer to int for message length - * Returns : pointer to message string, or NULL - */ -char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length) -{ - struct msgqueue_entry *mq = msgq->qe; - - *length = mq ? mq->length : 0; + for (mq = msgq->qe; mq && msgno; mq = mq->next, msgno--); - return mq ? mq->msg : NULL; + return mq ? &mq->msg : NULL; } /* @@ -143,10 +123,11 @@ va_start(ap, length); for (i = 0; i < length; i++) - mq->msg[i] = va_arg(ap, unsigned char); + mq->msg.msg[i] = va_arg(ap, unsigned char); va_end(ap); - mq->length = length; + mq->msg.length = length; + mq->msg.fifo = 0; mq->next = NULL; mqp = &msgq->qe; @@ -178,8 +159,7 @@ EXPORT_SYMBOL(msgqueue_initialise); EXPORT_SYMBOL(msgqueue_free); EXPORT_SYMBOL(msgqueue_msglength); -EXPORT_SYMBOL(msgqueue_getnextmsg); -EXPORT_SYMBOL(msgqueue_peeknextmsg); +EXPORT_SYMBOL(msgqueue_getmsg); EXPORT_SYMBOL(msgqueue_addmsg); EXPORT_SYMBOL(msgqueue_flush); diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/msgqueue.h linux/drivers/acorn/scsi/msgqueue.h --- v2.3.6/linux/drivers/acorn/scsi/msgqueue.h Fri May 8 00:42:39 1998 +++ linux/drivers/acorn/scsi/msgqueue.h Thu Jun 17 01:11:35 1999 @@ -6,9 +6,14 @@ #ifndef MSGQUEUE_H #define MSGQUEUE_H -struct msgqueue_entry { +struct message { char msg[8]; int length; + int fifo; +}; + +struct msgqueue_entry { + struct message msg; struct msgqueue_entry *next; }; @@ -21,60 +26,51 @@ } MsgQueue_t; /* - * Function: void msgqueue_initialise (MsgQueue_t *msgq) + * Function: void msgqueue_initialise(MsgQueue_t *msgq) * Purpose : initialise a message queue * Params : msgq - queue to initialise */ -extern void msgqueue_initialise (MsgQueue_t *msgq); +extern void msgqueue_initialise(MsgQueue_t *msgq); /* - * Function: void msgqueue_free (MsgQueue_t *msgq) + * Function: void msgqueue_free(MsgQueue_t *msgq) * Purpose : free a queue * Params : msgq - queue to free */ -extern void msgqueue_free (MsgQueue_t *msgq); +extern void msgqueue_free(MsgQueue_t *msgq); /* - * Function: int msgqueue_msglength (MsgQueue_t *msgq) + * Function: int msgqueue_msglength(MsgQueue_t *msgq) * Purpose : calculate the total length of all messages on the message queue * Params : msgq - queue to examine * Returns : number of bytes of messages in queue */ -extern int msgqueue_msglength (MsgQueue_t *msgq); +extern int msgqueue_msglength(MsgQueue_t *msgq); /* - * Function: char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length) + * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) * Purpose : return a message & its length * Params : msgq - queue to obtain message from - * length - pointer to int for message length - * Returns : pointer to message string, or NULL - */ -extern char *msgqueue_getnextmsg (MsgQueue_t *msgq, int *length); - -/* - * Function: char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length) - * Purpose : return next message & length without removing it from the list - * Params : msgq - queue to obtain message from - * : length - pointer to int for message length + * : msgno - message number * Returns : pointer to message string, or NULL */ -extern char *msgqueue_peeknextmsg(MsgQueue_t *msgq, int *length); +extern struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno); /* - * Function: int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...) + * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) * Purpose : add a message onto a message queue * Params : msgq - queue to add message on * length - length of message * ... - message bytes * Returns : != 0 if successful */ -extern int msgqueue_addmsg (MsgQueue_t *msgq, int length, ...); +extern int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...); /* - * Function: void msgqueue_flush (MsgQueue_t *msgq) + * Function: void msgqueue_flush(MsgQueue_t *msgq) * Purpose : flush all messages from message queue * Params : msgq - queue to flush */ -extern void msgqueue_flush (MsgQueue_t *msgq); +extern void msgqueue_flush(MsgQueue_t *msgq); #endif diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/powertec.c linux/drivers/acorn/scsi/powertec.c --- v2.3.6/linux/drivers/acorn/scsi/powertec.c Thu Dec 17 09:07:45 1998 +++ linux/drivers/acorn/scsi/powertec.c Thu Jun 17 01:11:35 1999 @@ -114,6 +114,8 @@ powertecscsi_irqenable, powertecscsi_irqdisable, NULL, + NULL, + NULL, NULL }; @@ -271,8 +273,9 @@ info->info.ifcfg.select_timeout = 255; info->info.ifcfg.asyncperiod = POWERTEC_ASYNC_PERIOD; info->info.ifcfg.sync_max_depth = POWERTEC_SYNC_DEPTH; - info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; + info->info.ifcfg.cntl3 = /*CNTL3_BS8 |*/ CNTL3_FASTSCSI | CNTL3_FASTCLK; info->info.ifcfg.disconnect_ok = 1; + info->info.ifcfg.wide_max_size = 0; info->info.dma.setup = powertecscsi_dma_setup; info->info.dma.pseudo = NULL; info->info.dma.stop = powertecscsi_dma_stop; @@ -443,31 +446,12 @@ host->io_port, host->irq, host->dma_channel, info->info.scsi.type, info->control.terms ? "on" : "off"); - pos += sprintf(buffer+pos, - "Queued commands: %-10u Issued commands: %-10u\n" - "Done commands : %-10u Reads : %-10u\n" - "Writes : %-10u Others : %-10u\n" - "Disconnects : %-10u Aborts : %-10u\n" - "Resets : %-10u\n", - info->info.stats.queues, info->info.stats.removes, - info->info.stats.fins, info->info.stats.reads, - info->info.stats.writes, info->info.stats.miscs, - info->info.stats.disconnects, info->info.stats.aborts, - info->info.stats.resets); + pos += fas216_print_stats(&info->info, buffer + pos); - pos += sprintf (buffer+pos, "\nAttached devices:%s\n", host->host_queue ? "" : " none"); + pos += sprintf (buffer+pos, "\nAttached devices:\n"); for (scd = host->host_queue; scd; scd = scd->next) { - int len; - - proc_print_scsidevice (scd, buffer, &len, pos); - pos += len; - pos += sprintf (buffer+pos, "Extensions: "); - if (scd->tagged_supported) - pos += sprintf (buffer+pos, "TAG %sabled [%d] ", - scd->tagged_queue ? "en" : "dis", - scd->current_tag); - pos += sprintf (buffer+pos, "\n"); + pos += fas216_print_device(&info->info, scd, buffer + pos); if (pos + begin < offset) { begin += pos; diff -u --recursive --new-file v2.3.6/linux/drivers/acorn/scsi/queue.c linux/drivers/acorn/scsi/queue.c --- v2.3.6/linux/drivers/acorn/scsi/queue.c Sat Jul 18 11:55:25 1998 +++ linux/drivers/acorn/scsi/queue.c Thu Jun 17 01:11:35 1999 @@ -55,6 +55,7 @@ q->magic = QUEUE_MAGIC_FREE; q->SCpnt = NULL; } + q -= 1; q->next = NULL; } diff -u --recursive --new-file v2.3.6/linux/drivers/block/Config.in linux/drivers/block/Config.in --- v2.3.6/linux/drivers/block/Config.in Thu May 13 11:00:08 1999 +++ linux/drivers/block/Config.in Thu Jun 17 01:11:35 1999 @@ -38,16 +38,16 @@ bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 bool ' Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then - bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA + bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI + if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then + bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO + fi bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD bool ' AEC6210 chipset support' CONFIG_BLK_DEV_AEC6210 - if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then - bool ' Use DMA by default when available' CONFIG_IDEDMA_AUTO - fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 bool ' Intel PIIXn chipsets support (EXPERIMENTAL)' CONFIG_BLK_DEV_PIIX - if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then + if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" ]; then bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 bool ' VIA82C586 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_VIA82C586 @@ -66,7 +66,7 @@ fi fi fi - if [ "$CONFIG_PPC" = "y" ]; then + if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then bool ' Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105 fi fi @@ -75,11 +75,25 @@ if [ "$CONFIG_BLK_DEV_IDE_PMAC" != "n" ]; then bool ' PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC if [ "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" ]; then - define_bool CONFIG_BLK_DEV_IDEDMA y bool ' Use DMA by default' CONFIG_PMAC_IDEDMA_AUTO fi fi fi + if [ "$CONFIG_ARCH_ACORN" = "y" ]; then + bool ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE + if [ "$CONFIG_BLK_DEV_IDE_ICSIDE" = "y" ]; then + bool ' ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS + if [ "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then + bool ' Use ICS DMA by default' CONFIG_IDEDMA_ICS_AUTO + fi + fi + bool ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE + fi + if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" -o \ + "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" -o \ + "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then + define_bool CONFIG_BLK_DEV_IDEDMA y + fi bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then comment 'Note: most of these also require special kernel boot parameters' @@ -170,7 +184,8 @@ "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \ "$CONFIG_BLK_DEV_CY82C693" = "y" -o \ "$CONFIG_BLK_DEV_HPT343" = "y" -o \ - "$CONFIG_BLK_DEV_PIIX" = "y" ]; then + "$CONFIG_BLK_DEV_PIIX" = "y" -o \ + "$CONFIG_BLK_DEV_SL82C105" = "y" ]; then define_bool CONFIG_BLK_DEV_IDE_MODES y else define_bool CONFIG_BLK_DEV_IDE_MODES n diff -u --recursive --new-file v2.3.6/linux/drivers/block/icside.c linux/drivers/block/icside.c --- v2.3.6/linux/drivers/block/icside.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/icside.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,634 @@ +/* + * linux/drivers/block/icside.c + * + * Copyright (c) 1996,1997 Russell King. + * + * Changelog: + * 08-Jun-1996 RMK Created + * 12-Sep-1997 RMK Added interrupt enable/disable + * 17-Apr-1999 RMK Added support for V6 EASI + * 22-May-1999 RMK Added support for V6 DMA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Maximum number of interfaces per card + */ +#define MAX_IFS 2 + +#define ICS_IDENT_OFFSET 0x8a0 + +#define ICS_ARCIN_V5_INTRSTAT 0x000 +#define ICS_ARCIN_V5_INTROFFSET 0x001 +#define ICS_ARCIN_V5_IDEOFFSET 0xa00 +#define ICS_ARCIN_V5_IDEALTOFFSET 0xae0 +#define ICS_ARCIN_V5_IDESTEPPING 4 + +#define ICS_ARCIN_V6_IDEOFFSET_1 0x800 +#define ICS_ARCIN_V6_INTROFFSET_1 0x880 +#define ICS_ARCIN_V6_INTRSTAT_1 0x8a4 +#define ICS_ARCIN_V6_IDEALTOFFSET_1 0x8e0 +#define ICS_ARCIN_V6_IDEOFFSET_2 0xc00 +#define ICS_ARCIN_V6_INTROFFSET_2 0xc80 +#define ICS_ARCIN_V6_INTRSTAT_2 0xca4 +#define ICS_ARCIN_V6_IDEALTOFFSET_2 0xce0 +#define ICS_ARCIN_V6_IDESTEPPING 4 + +struct cardinfo { + unsigned int dataoffset; + unsigned int ctrloffset; + unsigned int stepping; +}; + +static struct cardinfo icside_cardinfo_v5 = { + ICS_ARCIN_V5_IDEOFFSET, + ICS_ARCIN_V5_IDEALTOFFSET, + ICS_ARCIN_V5_IDESTEPPING +}; + +static struct cardinfo icside_cardinfo_v6_1 = { + ICS_ARCIN_V6_IDEOFFSET_1, + ICS_ARCIN_V6_IDEALTOFFSET_1, + ICS_ARCIN_V6_IDESTEPPING +}; + +static struct cardinfo icside_cardinfo_v6_2 = { + ICS_ARCIN_V6_IDEOFFSET_2, + ICS_ARCIN_V6_IDEALTOFFSET_2, + ICS_ARCIN_V6_IDESTEPPING +}; + +static const card_ids icside_cids[] = { + { MANU_ICS, PROD_ICS_IDE }, + { MANU_ICS2, PROD_ICS2_IDE }, + { 0xffff, 0xffff } +}; + +typedef enum { + ics_if_unknown, + ics_if_arcin_v5, + ics_if_arcin_v6 +} iftype_t; + +/* ---------------- Version 5 PCB Support Functions --------------------- */ +/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) + * Purpose : enable interrupts from card + */ +static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) +{ + unsigned int memc_port = (unsigned int)ec->irq_data; + outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET); +} + +/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) + * Purpose : disable interrupts from card + */ +static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) +{ + unsigned int memc_port = (unsigned int)ec->irq_data; + inb (memc_port + ICS_ARCIN_V5_INTROFFSET); +} + +static const expansioncard_ops_t icside_ops_arcin_v5 = { + icside_irqenable_arcin_v5, + icside_irqdisable_arcin_v5, + NULL, + NULL, + NULL, + NULL +}; + + +/* ---------------- Version 6 PCB Support Functions --------------------- */ +/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) + * Purpose : enable interrupts from card + */ +static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) +{ + unsigned int ide_base_port = (unsigned int)ec->irq_data; + + outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); + outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); +} + +/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) + * Purpose : disable interrupts from card + */ +static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) +{ + unsigned int ide_base_port = (unsigned int)ec->irq_data; + + inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); + inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); +} + +/* Prototype: icside_irqprobe(struct expansion_card *ec) + * Purpose : detect an active interrupt from card + */ +static int icside_irqpending_arcin_v6(struct expansion_card *ec) +{ + unsigned int ide_base_port = (unsigned int)ec->irq_data; + + return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 || + inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1; +} + +static const expansioncard_ops_t icside_ops_arcin_v6 = { + icside_irqenable_arcin_v6, + icside_irqdisable_arcin_v6, + icside_irqpending_arcin_v6, + NULL, + NULL, + NULL +}; + +/* Prototype: icside_identifyif (struct expansion_card *ec) + * Purpose : identify IDE interface type + * Notes : checks the description string + */ +static iftype_t icside_identifyif (struct expansion_card *ec) +{ + unsigned int addr; + iftype_t iftype; + int id = 0; + + iftype = ics_if_unknown; + + addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET; + + id = inb (addr) & 1; + id |= (inb (addr + 1) & 1) << 1; + id |= (inb (addr + 2) & 1) << 2; + id |= (inb (addr + 3) & 1) << 3; + + switch (id) { + case 0: /* A3IN */ + printk("icside: A3IN unsupported\n"); + break; + + case 1: /* A3USER */ + printk("icside: A3USER unsupported\n"); + break; + + case 3: /* ARCIN V6 */ + printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no); + iftype = ics_if_arcin_v6; + break; + + case 15:/* ARCIN V5 (no id) */ + printk(KERN_DEBUG "icside: detected ARCIN V5 in slot %d\n", ec->slot_no); + iftype = ics_if_arcin_v5; + break; + + default:/* we don't know - complain very loudly */ + printk("icside: ***********************************\n"); + printk("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id); + printk("icside: ***********************************\n"); + printk("icside: please report this to linux@arm.linux.org.uk\n"); + printk("icside: defaulting to ARCIN V5\n"); + iftype = ics_if_arcin_v5; + break; + } + + return iftype; +} + +#ifdef CONFIG_BLK_DEV_IDEDMA_ICS +/* + * SG-DMA support. + * + * Similar to the BM-DMA, but we use the RiscPCs IOMD + * DMA controllers. There is only one DMA controller + * per card, which means that only one drive can be + * accessed at one time. NOTE! We do not inforce that + * here, but we rely on the main IDE driver spotting + * that both interfaces use the same IRQ, which should + * guarantee this. + * + * We are limited by the drives IOR/IOW pulse time. + * The closest that we can get to the requirements is + * a type C cycle for both mode 1 and mode 2. However, + * this does give a burst of 8MB/s. + * + * This has been tested with a couple of Conner + * Peripherals 1080MB CFS1081A drives, one on each + * interface, which deliver about 2MB/s each. I + * believe that this is limited by the lack of + * on-board drive cache. + */ +#define TABLE_SIZE 2048 + +static int +icside_build_dmatable(ide_drive_t *drive, int reading) +{ + struct request *rq = HWGROUP(drive)->rq; + struct buffer_head *bh = rq->bh; + unsigned long addr, size; + unsigned char *virt_addr; + unsigned int count = 0; + dmasg_t *sg = (dmasg_t *)HWIF(drive)->dmatable; + + do { + if (bh == NULL) { + /* paging requests have (rq->bh == NULL) */ + virt_addr = rq->buffer; + addr = virt_to_bus (virt_addr); + size = rq->nr_sectors << 9; + } else { + /* group sequential buffers into one large buffer */ + virt_addr = bh->b_data; + addr = virt_to_bus (virt_addr); + size = bh->b_size; + while ((bh = bh->b_reqnext) != NULL) { + if ((addr + size) != virt_to_bus (bh->b_data)) + break; + size += bh->b_size; + } + } + + if (addr & 3) { + printk("%s: misaligned DMA buffer\n", drive->name); + return 0; + } + + if (size) { + if (reading) + dma_cache_inv((unsigned int)virt_addr, size); + else + dma_cache_wback((unsigned int)virt_addr, size); + } + + sg[count].address = addr; + sg[count].length = size; + if (++count >= (TABLE_SIZE / sizeof(dmasg_t))) { + printk("%s: DMA table too small\n", drive->name); + return 0; + } + } while (bh != NULL); + + if (!count) + printk("%s: empty DMA table?\n", drive->name); + + return count; +} + +static int +icside_config_drive(ide_drive_t *drive, int mode) +{ + ide_hwif_t *hwif = HWIF(drive); + int speed, err; + + if (mode == 2) { + speed = XFER_MW_DMA_2; + drive->drive_data = 250; + } else { + speed = XFER_MW_DMA_1; + drive->drive_data = 250; + } + + /* + * Don't use ide_wait_cmd here - it will + * attempt to set_geometry and recalibrate, + * but for some reason these don't work at + * this point (lost interrupt). + */ + SELECT_DRIVE(hwif, drive); + OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG); + OUT_BYTE(speed, IDE_NSECTOR_REG); + OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG); + OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG); + + err = ide_wait_stat(drive, DRIVE_READY, + BUSY_STAT|DRQ_STAT|ERR_STAT, WAIT_CMD); + + if (err == 0) { + drive->id->dma_mword &= 0x00ff; + drive->id->dma_mword |= 256 << mode; + } else + drive->drive_data = 0; + + return err; +} + +static int +icside_dma_check(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + int autodma = hwif->autodma; + + if (id && (id->capability & 1) && autodma) { + int dma_mode = 0; + + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) + return hwif->dmaproc(ide_dma_off, drive); + + /* Enable DMA on any drive that has + * UltraDMA (mode 0/1/2) enabled + */ + if (id->field_valid & 4 && id->dma_ultra & 7) + dma_mode = 2; + + /* Enable DMA on any drive that has mode1 + * or mode2 multiword DMA enabled + */ + if (id->field_valid & 2 && id->dma_mword & 6) + dma_mode = id->dma_mword & 4 ? 2 : 1; + + /* Consult the list of known "good" drives */ + if (ide_dmaproc(ide_dma_good_drive, drive)) + dma_mode = 1; + + if (dma_mode && icside_config_drive(drive, dma_mode) == 0) + return hwif->dmaproc(ide_dma_on, drive); + } + return hwif->dmaproc(ide_dma_off_quietly, drive); +} + +static int +icside_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + int count, reading = 0; + + switch (func) { + case ide_dma_check: + return icside_dma_check(drive); + + case ide_dma_read: + reading = 1; + case ide_dma_write: + count = icside_build_dmatable(drive, reading); + if (!count) + return 1; + disable_dma(hwif->hw.dma); + + /* Route the DMA signals to + * to the correct interface. + */ + outb(hwif->select_data, hwif->config_data); + + /* Select the correct timing + * for this drive + */ + set_dma_speed(hwif->hw.dma, drive->drive_data); + + set_dma_sg(hwif->hw.dma, (dmasg_t *)hwif->dmatable, count); + set_dma_mode(hwif->hw.dma, reading ? DMA_MODE_READ + : DMA_MODE_WRITE); + + drive->waiting_for_dma = 1; + if (drive->media != ide_disk) + return 0; + + ide_set_handler(drive, &ide_dma_intr, WAIT_CMD); + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, + IDE_COMMAND_REG); + + case ide_dma_begin: + enable_dma(hwif->hw.dma); + return 0; + + case ide_dma_end: + drive->waiting_for_dma = 0; + disable_dma(hwif->hw.dma); + return get_dma_residue(hwif->hw.dma) != 0; + + case ide_dma_test_irq: + return inb((unsigned long)hwif->hw.priv) & 1; + + default: + return ide_dmaproc(func, drive); + } +} + +static unsigned long +icside_alloc_dmatable(void) +{ + static unsigned long dmatable; + static unsigned int leftover; + unsigned long table; + + if (leftover < TABLE_SIZE) { +#if PAGE_SIZE == TABLE_SIZE * 2 + dmatable = __get_free_pages(GFP_KERNEL, 1); + leftover = PAGE_SIZE; +#else + dmatable = kmalloc(TABLE_SIZE, GFP_KERNEL); + leftover = TABLE_SIZE; +#endif + } + + table = dmatable; + if (table) { + dmatable += TABLE_SIZE; + leftover -= TABLE_SIZE; + } + + return table; +} + +static int +icside_setup_dma(ide_hwif_t *hwif, int autodma) +{ + unsigned long table = icside_alloc_dmatable(); + + printk(" %s: SG-DMA", hwif->name); + + if (!table) + printk(" -- ERROR, unable to allocate DMA table\n"); + else { + hwif->dmatable = (void *)table; + hwif->dmaproc = icside_dmaproc; + hwif->autodma = autodma; + + printk(" capable%s\n", autodma ? + ", auto-enable" : ""); + } + + return hwif->dmatable != NULL; +} +#endif + +static ide_hwif_t * +icside_find_hwif(unsigned long dataport) +{ + ide_hwif_t *hwif; + int index; + + for (index = 0; index < MAX_HWIFS; ++index) { + hwif = &ide_hwifs[index]; + if (hwif->hw.io_ports[IDE_DATA_OFFSET] == (ide_ioreg_t)dataport) + goto found; + } + + for (index = 0; index < MAX_HWIFS; ++index) { + hwif = &ide_hwifs[index]; + if (!hwif->hw.io_ports[IDE_DATA_OFFSET]) + goto found; + } + + return NULL; +found: + return hwif; +} + +static ide_hwif_t * +icside_setup(unsigned long base, struct cardinfo *info, int irq) +{ + unsigned long port = base + info->dataoffset; + ide_hwif_t *hwif; + + hwif = icside_find_hwif(base); + if (hwif) { + int i; + + memset(&hwif->hw, 0, sizeof(hw_regs_t)); + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hwif->hw.io_ports[i] = (ide_ioreg_t)port; + port += 1 << info->stepping; + } + hwif->hw.io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset; + hwif->hw.irq = irq; + hwif->hw.dma = NO_DMA; + hwif->noprobe = 0; + hwif->chipset = ide_acorn; + } + + return hwif; +} + +static int icside_register_v5(struct expansion_card *ec, int autodma) +{ + unsigned long slot_port; + ide_hwif_t *hwif; + + slot_port = ecard_address(ec, ECARD_MEMC, 0); + + ec->irqaddr = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT); + ec->irqmask = 1; + ec->irq_data = (void *)slot_port; + ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5; + + /* + * Be on the safe side - disable interrupts + */ + inb(slot_port + ICS_ARCIN_V5_INTROFFSET); + + hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec->irq); + + return hwif ? 0 : -1; +} + +static int icside_register_v6(struct expansion_card *ec, int autodma) +{ + unsigned long slot_port, port; + ide_hwif_t *hwif, *mate; + int sel = 0; + + slot_port = ecard_address(ec, ECARD_IOC, ECARD_FAST); + port = ecard_address(ec, ECARD_EASI, ECARD_FAST); + + if (port == 0) + port = slot_port; + else + sel = 1 << 5; + + outb(sel, slot_port); + + ec->irq_data = (void *)port; + ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6; + + /* + * Be on the safe side - disable interrupts + */ + inb(port + ICS_ARCIN_V6_INTROFFSET_1); + inb(port + ICS_ARCIN_V6_INTROFFSET_2); + + hwif = icside_setup(port, &icside_cardinfo_v6_1, ec->irq); + mate = icside_setup(port, &icside_cardinfo_v6_2, ec->irq); + +#ifdef CONFIG_BLK_DEV_IDEDMA_ICS + if (ec->dma != NO_DMA) { + if (request_dma(ec->dma, hwif->name)) + goto no_dma; + + if (hwif) { + hwif->config_data = slot_port; + hwif->select_data = sel; + hwif->hw.dma = ec->dma; + hwif->hw.priv = (void *) + (port + ICS_ARCIN_V6_INTRSTAT_1); + hwif->channel = 0; + icside_setup_dma(hwif, autodma); + } + if (mate) { + mate->config_data = slot_port; + mate->select_data = sel | 1; + mate->hw.dma = ec->dma; + mate->hw.priv = (void *) + (port + ICS_ARCIN_V6_INTRSTAT_2); + mate->channel = 1; + icside_setup_dma(mate, autodma); + } + } +#endif + +no_dma: + return hwif || mate ? 0 : -1; +} + +int icside_init(void) +{ + int autodma = 0; + +#ifdef CONFIG_IDEDMA_ICS_AUTO + autodma = 1; +#endif + + ecard_startfind (); + + do { + struct expansion_card *ec; + int result; + + ec = ecard_find(0, icside_cids); + if (ec == NULL) + break; + + ecard_claim(ec); + + switch (icside_identifyif(ec)) { + case ics_if_arcin_v5: + result = icside_register_v5(ec, autodma); + break; + + case ics_if_arcin_v6: + result = icside_register_v6(ec, autodma); + break; + + default: + result = -1; + break; + } + + if (result) + ecard_release(ec); + } while (1); + + return 0; +} diff -u --recursive --new-file v2.3.6/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- v2.3.6/linux/drivers/block/ll_rw_blk.c Wed Jun 2 22:21:51 1999 +++ linux/drivers/block/ll_rw_blk.c Wed Jun 16 19:26:27 1999 @@ -603,7 +603,7 @@ /* Verify requested block sizes. */ for (i = 0; i < nr; i++) { - if (bh[i] && bh[i]->b_size != correct_size) { + if (bh[i]->b_size != correct_size) { printk(KERN_NOTICE "ll_rw_block: device %s: " "only %d-char blocks implemented (%lu)\n", kdevname(bh[0]->b_dev), diff -u --recursive --new-file v2.3.6/linux/drivers/block/rapide.c linux/drivers/block/rapide.c --- v2.3.6/linux/drivers/block/rapide.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/rapide.c Thu Jun 17 01:11:35 1999 @@ -0,0 +1,92 @@ +/* + * linux/arch/arm/drivers/block/ide-rapide.c + * + * Copyright (c) 1996-1998 Russell King. + * + * Changelog: + * 08-06-1996 RMK Created + * 13-04-1998 RMK Added manufacturer and product IDs + */ + +#include +#include +#include +#include +#include + +#include + +static const card_ids rapide_cids[] = { + { MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 }, + { 0xffff, 0xffff } +}; + +static struct expansion_card *ec[MAX_ECARDS]; +static int result[MAX_ECARDS]; + +static inline int rapide_register(struct expansion_card *ec) +{ + unsigned long port = ecard_address (ec, ECARD_MEMC, 0); + hw_regs_t hw; + + int i; + + memset(&hw, 0, sizeof(hw)); + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw.io_ports[i] = (ide_ioreg_t)port; + port += 1 << 4; + } + hw.io_ports[IDE_CONTROL_OFFSET] = port + 0x206; + hw.irq = ec->irq; + + return ide_register_hw(&hw, NULL); +} + +int rapide_init(void) +{ + int i; + + for (i = 0; i < MAX_ECARDS; i++) + ec[i] = NULL; + + ecard_startfind(); + + for (i = 0; ; i++) { + if ((ec[i] = ecard_find(0, rapide_cids)) == NULL) + break; + + ecard_claim(ec[i]); + result[i] = rapide_register(ec[i]); + } + for (i = 0; i < MAX_ECARDS; i++) + if (ec[i] && result[i] < 0) { + ecard_release(ec[i]); + ec[i] = NULL; + } + return 0; +} + +#ifdef MODULE + +int init_module (void) +{ + return rapide_init(); +} + +void cleanup_module (void) +{ + int i; + + for (i = 0; i < MAX_ECARDS; i++) + if (ec[i]) { + unsigned long port; + port = ecard_address(ec[i], ECARD_MEMC, 0); + + ide_unregister_port(port, ec[i]->irq, 16); + ecard_release(ec[i]); + ec[i] = NULL; + } +} +#endif + diff -u --recursive --new-file v2.3.6/linux/drivers/char/n_hdlc.c linux/drivers/char/n_hdlc.c --- v2.3.6/linux/drivers/char/n_hdlc.c Wed May 12 13:27:37 1999 +++ linux/drivers/char/n_hdlc.c Wed Jun 16 19:26:27 1999 @@ -9,6 +9,7 @@ * Al Longyear , Paul Mackerras * * Original release 01/11/99 + * ==FILEDATE 19990524== * * This code is released under the GNU General Public License (GPL) * @@ -72,7 +73,7 @@ */ #define HDLC_MAGIC 0x239e -#define HDLC_VERSION "1.0" +#define HDLC_VERSION "1.2" #include #include @@ -813,6 +814,8 @@ { struct n_hdlc *n_hdlc = tty2n_hdlc (tty); int error = 0; + int count; + unsigned long flags; if (debuglevel >= DEBUG_LEVEL_INFO) printk("%s(%d)n_hdlc_tty_ioctl() called %d\n", @@ -824,21 +827,29 @@ switch (cmd) { case FIONREAD: - { - /* report count of read data available */ - /* in next available frame (if any) */ - int count; - unsigned long flags; - spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags); - if (n_hdlc->rx_buf_list.head) - count = n_hdlc->rx_buf_list.head->count; - else - count = 0; - spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags); - PUT_USER (error, count, (int *) arg); - } + /* report count of read data available */ + /* in next available frame (if any) */ + spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags); + if (n_hdlc->rx_buf_list.head) + count = n_hdlc->rx_buf_list.head->count; + else + count = 0; + spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags); + PUT_USER (error, count, (int *) arg); break; - + + case TIOCOUTQ: + /* get the pending tx byte count in the driver */ + count = tty->driver.chars_in_buffer ? + tty->driver.chars_in_buffer(tty) : 0; + /* add size of next output frame in queue */ + spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags); + if (n_hdlc->tx_buf_list.head) + count += n_hdlc->tx_buf_list.head->count; + spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags); + PUT_USER (error, count, (int*)arg); + break; + default: error = n_tty_ioctl (tty, file, cmd, arg); break; diff -u --recursive --new-file v2.3.6/linux/drivers/char/synclink.c linux/drivers/char/synclink.c --- v2.3.6/linux/drivers/char/synclink.c Wed May 12 13:27:37 1999 +++ linux/drivers/char/synclink.c Wed Jun 16 19:26:27 1999 @@ -1,6 +1,8 @@ /* * linux/drivers/char/synclink.c * + * ==FILEDATE 19990610== + * * Device driver for Microgate SyncLink ISA and PCI * high speed multiprotocol serial adapters. * @@ -43,14 +45,15 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ - + #define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq)) #define BREAKPOINT() asm(" int $3"); #define MAX_ISA_DEVICES 10 -#include +#include #include +#include #include #include #include @@ -68,7 +71,7 @@ #include #include -#if LINUX_VERSION_CODE >= VERSION(2,1,0) +#if LINUX_VERSION_CODE >= VERSION(2,1,0) #include #include #include @@ -209,8 +212,21 @@ } BH_EVENT, *BH_QUEUE; /* Queue of BH actions to be done. */ #define MAX_BH_QUEUE_ENTRIES 200 +#define IO_PIN_SHUTDOWN_LIMIT (MAX_BH_QUEUE_ENTRIES/4) #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + +struct _input_signal_events { + int ri_up; + int ri_down; + int dsr_up; + int dsr_down; + int dcd_up; + int dcd_down; + int cts_up; + int cts_down; +}; + /* * Device instance data structure */ @@ -266,6 +282,11 @@ int bh_running; /* Protection from multiple */ int isr_overflow; int bh_requested; + + int dcd_chkcount; /* check counts to prevent */ + int cts_chkcount; /* too many IRQs if a signal */ + int dsr_chkcount; /* is floating */ + int ri_chkcount; char *buffer_list; /* virtual address of Rx & Tx buffer lists */ unsigned long buffer_list_phys; @@ -327,6 +348,11 @@ char flag_buf[HDLC_MAX_FRAME_SIZE]; char char_buf[HDLC_MAX_FRAME_SIZE]; BOOLEAN drop_rts_on_tx_done; + + BOOLEAN loopmode_insert_requested; + BOOLEAN loopmode_send_done_requested; + + struct _input_signal_events input_signal_events; }; #define MGSL_MAGIC 0x5401 @@ -712,6 +738,13 @@ void mgsl_tx_timeout(unsigned long context); + +void usc_loopmode_cancel_transmit( struct mgsl_struct * info ); +void usc_loopmode_insert_request( struct mgsl_struct * info ); +int usc_loopmode_active( struct mgsl_struct * info); +void usc_loopmode_send_done( struct mgsl_struct * info ); +int usc_loopmode_send_active( struct mgsl_struct * info ); + /* * Defines a BUS descriptor value for the PCI adapter * local bus address ranges. @@ -820,7 +853,8 @@ static int mgsl_txenable(struct mgsl_struct * info, int enable); static int mgsl_txabort(struct mgsl_struct * info); static int mgsl_rxenable(struct mgsl_struct * info, int enable); -static int mgsl_wait_event(struct mgsl_struct * info, int mask); +static int mgsl_wait_event(struct mgsl_struct * info, int * mask); +static int mgsl_loopmode_send_done( struct mgsl_struct * info ); #define jiffies_from_ms(a) ((((a) * HZ)/1000)+1) @@ -865,7 +899,7 @@ #endif static char *driver_name = "SyncLink serial driver"; -static char *driver_version = "1.00"; +static char *driver_version = "1.7"; static struct tty_driver serial_driver, callout_driver; static int serial_refcount; @@ -1001,6 +1035,7 @@ /* As a safety measure, mark the end of the chain with a NULL */ info->free_bh_queue_tail->link = NULL; + info->isr_overflow=0; } /* end of mgsl_format_bh_queue() */ @@ -1092,6 +1127,14 @@ spin_unlock_irqrestore(&info->irq_spinlock,flags); return 1; } + + if ( info->isr_overflow ) { + if (debug_level >= DEBUG_LEVEL_BH) + printk("ISR overflow cleared.\n"); + info->isr_overflow=0; + usc_EnableMasterIrqBit(info); + usc_EnableDmaInterrupts(info,DICR_MASTER); + } /* Mark BH routine as complete */ info->bh_running = 0; @@ -1155,10 +1198,6 @@ } } - if ( info->isr_overflow ) { - printk("ISR overflow detected.\n"); - } - if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):mgsl_bh_handler(%s) exit\n", __FILE__,__LINE__,info->device_name); @@ -1199,6 +1238,7 @@ void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount ) { struct tty_struct *tty = info->tty; + unsigned long flags; if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):mgsl_bh_transmit_data() entry on %s\n", @@ -1215,7 +1255,15 @@ } wake_up_interruptible(&tty->write_wait); } - + + /* if transmitter idle and loopmode_send_done_requested + * then start echoing RxD to TxD + */ + spin_lock_irqsave(&info->irq_spinlock,flags); + if ( !info->tx_active && info->loopmode_send_done_requested ) + usc_loopmode_send_done( info ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } /* End Of mgsl_bh_transmit_data() */ /* mgsl_bh_status_handler() @@ -1240,6 +1288,23 @@ printk( "%s(%d):mgsl_bh_status_handler() entry on %s\n", __FILE__,__LINE__,info->device_name); + if (status & MISCSTATUS_RI_LATCHED) { + if (info->ri_chkcount) + (info->ri_chkcount)--; + } + if (status & MISCSTATUS_DSR_LATCHED) { + if (info->dsr_chkcount) + (info->dsr_chkcount)--; + } + if (status & MISCSTATUS_DCD_LATCHED) { + if (info->dcd_chkcount) + (info->dcd_chkcount)--; + } + if (status & MISCSTATUS_CTS_LATCHED) { + if (info->cts_chkcount) + (info->cts_chkcount)--; + } + } /* End Of mgsl_bh_status_handler() */ /* mgsl_isr_receive_status() @@ -1259,8 +1324,21 @@ printk("%s(%d):mgsl_isr_receive_status status=%04X\n", __FILE__,__LINE__,status); - usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); - usc_UnlatchRxstatusBits( info, status ); + if ( (status & RXSTATUS_ABORT_RECEIVED) && + info->loopmode_insert_requested && + usc_loopmode_active(info) ) + { + ++info->icount.rxabort; + info->loopmode_insert_requested = FALSE; + + /* clear CMR:13 to start echoing RxD to TxD */ + info->cmr_value &= ~BIT13; + usc_OutReg(info, CMR, info->cmr_value); + + /* disable received abort irq (no longer required) */ + usc_OutReg(info, RICR, + (usc_InReg(info, RICR) & ~RXSTATUS_ABORT_RECEIVED)); + } if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) { if (status & RXSTATUS_EXITED_HUNT) @@ -1278,6 +1356,9 @@ usc_RTCmd( info, RTCmd_PurgeRxFifo ); } + usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); + usc_UnlatchRxstatusBits( info, status ); + } /* end of mgsl_isr_receive_status() */ /* mgsl_isr_transmit_status() @@ -1300,7 +1381,7 @@ usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); usc_UnlatchTxstatusBits( info, status ); - + if ( status & TXSTATUS_EOF_SENT ) info->icount.txok++; else if ( status & TXSTATUS_UNDERRUN ) @@ -1356,12 +1437,32 @@ MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) { icount = &info->icount; /* update input line counters */ - if (status & MISCSTATUS_RI_LATCHED) + if (status & MISCSTATUS_RI_LATCHED) { + if ((info->ri_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) + usc_DisablestatusIrqs(info,SICR_RI); icount->rng++; - if (status & MISCSTATUS_DSR_LATCHED) + if ( status & MISCSTATUS_RI ) + info->input_signal_events.ri_up++; + else + info->input_signal_events.ri_down++; + } + if (status & MISCSTATUS_DSR_LATCHED) { + if ((info->dsr_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) + usc_DisablestatusIrqs(info,SICR_DSR); icount->dsr++; + if ( status & MISCSTATUS_DSR ) + info->input_signal_events.dsr_up++; + else + info->input_signal_events.dsr_down++; + } if (status & MISCSTATUS_DCD_LATCHED) { + if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) + usc_DisablestatusIrqs(info,SICR_DCD); icount->dcd++; + if ( status & MISCSTATUS_DCD ) + info->input_signal_events.dcd_up++; + else + info->input_signal_events.dcd_down++; #ifdef CONFIG_HARD_PPS if ((info->flags & ASYNC_HARDPPS_CD) && (status & MISCSTATUS_DCD_LATCHED)) @@ -1369,7 +1470,15 @@ #endif } if (status & MISCSTATUS_CTS_LATCHED) + { + if ((info->cts_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT) + usc_DisablestatusIrqs(info,SICR_CTS); icount->cts++; + if ( status & MISCSTATUS_CTS ) + info->input_signal_events.cts_up++; + else + info->input_signal_events.cts_down++; + } wake_up_interruptible(&info->status_event_wait_q); wake_up_interruptible(&info->event_wait_q); @@ -1411,6 +1520,8 @@ } } + mgsl_bh_queue_put(info, BH_TYPE_STATUS, status); + /* for diagnostics set IRQ flag */ if ( status & MISCSTATUS_TXC_LATCHED ){ usc_OutReg( info, SICR, @@ -1642,8 +1753,10 @@ /* Post a receive event for BH processing. */ mgsl_bh_queue_put( info, BH_TYPE_RECEIVE_DMA, status ); - if ( status & BIT3 ) + if ( status & BIT3 ) { info->rx_overflow = 1; + info->icount.buf_overrun++; + } } /* end of mgsl_isr_receive_dma() */ @@ -1696,9 +1809,9 @@ if ( info->isr_overflow ) { printk(KERN_ERR"%s(%d):%s isr overflow irq=%d\n", __FILE__,__LINE__,info->device_name, irq); - /* Interrupt overflow. Reset adapter and exit. */ -// UscReset(info); -// break; + usc_DisableMasterIrqBit(info); + usc_DisableDmaInterrupts(info,DICR_MASTER); + break; } } @@ -1980,6 +2093,11 @@ usc_set_async_mode(info); usc_set_serial_signals(info); + + info->dcd_chkcount = 0; + info->cts_chkcount = 0; + info->ri_chkcount = 0; + info->dsr_chkcount = 0; /* enable modem signal IRQs and read initial signal states */ usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI); @@ -2112,16 +2230,27 @@ if ( info->params.mode == MGSL_MODE_HDLC ) { /* operating in synchronous (frame oriented) mode */ - + if (info->tx_active) { ret = 0; goto cleanup; } - + + /* if operating in HDLC LoopMode and the adapter */ + /* has yet to be inserted into the loop, we can't */ + /* transmit */ + + if ( (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) && + !usc_loopmode_active(info) ) + { + ret = 0; + goto cleanup; + } + if ( info->xmit_cnt ) { /* Send accumulated from send_char() calls */ /* as frame and wait before accepting more data. */ ret = 0; - + /* copy data from circular xmit_buf to */ /* transmit DMA buffer. */ mgsl_load_tx_dma_buffer(info, @@ -2578,8 +2707,19 @@ spin_lock_irqsave(&info->irq_spinlock,flags); if ( enable ) { - if ( !info->tx_enabled ) + if ( !info->tx_enabled ) { + usc_start_transmitter(info); + /*-------------------------------------------------- + * if HDLC/SDLC Loop mode, attempt to insert the + * station in the 'loop' by setting CMR:13. Upon + * receipt of the next GoAhead (RxAbort) sequence, + * the OnLoop indicator (CCSR:7) should go active + * to indicate that we are on the loop + *--------------------------------------------------*/ + if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) + usc_loopmode_insert_request( info ); + } } else { if ( info->tx_enabled ) usc_stop_transmitter(info); @@ -2604,7 +2744,12 @@ spin_lock_irqsave(&info->irq_spinlock,flags); if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC ) - usc_TCmd(info,TCmd_SendAbort); + { + if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) + usc_loopmode_cancel_transmit( info ); + else + usc_TCmd(info,TCmd_SendAbort); + } spin_unlock_irqrestore(&info->irq_spinlock,flags); return 0; @@ -2640,25 +2785,39 @@ /* mgsl_wait_event() wait for specified event to occur * * Arguments: info pointer to device instance data - * mask bitmask of events to wait for - * Return Value: bit mask of triggering event, otherwise error code + * mask pointer to bitmask of events to wait for + * Return Value: 0 if successful and bit mask updated with + * of events triggerred, + * otherwise error code */ -static int mgsl_wait_event(struct mgsl_struct * info, int mask) +static int mgsl_wait_event(struct mgsl_struct * info, int * mask_ptr) { unsigned long flags; int s; int rc=0; u16 regval; struct mgsl_icount cprev, cnow; + int events = 0; + int mask; + struct _input_signal_events signal_events_prev, signal_events_now; + + COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int)); + if (rc) { + return -EFAULT; + } if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgsl_wait_event(%s,%d)\n", __FILE__,__LINE__, info->device_name, mask); - + spin_lock_irqsave(&info->irq_spinlock,flags); - + + usc_get_serial_signals(info); + s = info->serial_signals; + /* note the counters on entry */ cprev = info->icount; + signal_events_prev = info->input_signal_events; if (mask & MgslEvent_ExitHuntMode) { /* enable exit hunt mode IRQ */ @@ -2676,7 +2835,22 @@ spin_unlock_irqrestore(&info->irq_spinlock,flags); - while(!rc) { + /* Determine if any user requested events for input signals is currently TRUE */ + + events |= (mask & ((s & SerialSignal_DSR) ? + MgslEvent_DsrActive:MgslEvent_DsrInactive)); + + events |= (mask & ((s & SerialSignal_DCD) ? + MgslEvent_DcdActive:MgslEvent_DcdInactive)); + + events |= (mask & ((s & SerialSignal_CTS) ? + MgslEvent_CtsActive:MgslEvent_CtsInactive)); + + events |= (mask & ((s & SerialSignal_RI) ? + MgslEvent_RiActive:MgslEvent_RiInactive)); + + + while(!events) { /* sleep until event occurs */ interruptible_sleep_on(&info->event_wait_q); @@ -2687,39 +2861,52 @@ } spin_lock_irqsave(&info->irq_spinlock,flags); + /* get icount and serial signal states */ cnow = info->icount; - s = info->serial_signals; + signal_events_now = info->input_signal_events; spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if (signal_events_now.dsr_up != signal_events_prev.dsr_up && + mask & MgslEvent_DsrActive ) + events |= MgslEvent_DsrActive; + + if (signal_events_now.dsr_down != signal_events_prev.dsr_down && + mask & MgslEvent_DsrInactive ) + events |= MgslEvent_DsrInactive; + + if (signal_events_now.dcd_up != signal_events_prev.dcd_up && + mask & MgslEvent_DcdActive ) + events |= MgslEvent_DcdActive; + + if (signal_events_now.dcd_down != signal_events_prev.dcd_down && + mask & MgslEvent_DcdInactive ) + events |= MgslEvent_DcdInactive; + + if (signal_events_now.cts_up != signal_events_prev.cts_up && + mask & MgslEvent_CtsActive ) + events |= MgslEvent_CtsActive; + + if (signal_events_now.cts_down != signal_events_prev.cts_down && + mask & MgslEvent_CtsInactive ) + events |= MgslEvent_CtsInactive; + + if (signal_events_now.ri_up != signal_events_prev.ri_up && + mask & MgslEvent_RiActive ) + events |= MgslEvent_RiActive; + + if (signal_events_now.ri_down != signal_events_prev.ri_down && + mask & MgslEvent_RiInactive ) + events |= MgslEvent_RiInactive; - rc = 0; - - if (cnow.dsr != cprev.dsr) - rc |= (mask & ((s & SerialSignal_DSR) ? - MgslEvent_DsrActive:MgslEvent_DsrInactive)); - - if (cnow.dcd != cprev.dcd) - rc |= (mask & ((s & SerialSignal_DCD) ? - MgslEvent_DcdActive:MgslEvent_DcdInactive)); - - if (cnow.cts != cprev.cts) - rc |= (mask & ((s & SerialSignal_CTS) ? - MgslEvent_CtsActive:MgslEvent_CtsInactive)); - - if (cnow.rng != cprev.rng) - rc |= (mask & ((s & SerialSignal_RI) ? - MgslEvent_RiActive:MgslEvent_RiInactive)); - if (cnow.exithunt != cprev.exithunt) - rc |= (mask & MgslEvent_ExitHuntMode); - + events |= (mask & MgslEvent_ExitHuntMode); + if (cnow.rxidle != cprev.rxidle) - rc |= (mask & MgslEvent_ExitHuntMode); - - if (!rc) - rc = -EIO; /* no change => error */ - + events |= (mask & MgslEvent_IdleReceived); + cprev = cnow; + signal_events_prev = signal_events_now; } if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { @@ -2732,7 +2919,10 @@ } spin_unlock_irqrestore(&info->irq_spinlock,flags); } - + + if ( rc == 0 ) + PUT_USER(rc, events, mask_ptr); + return rc; } /* end of mgsl_wait_event() */ @@ -2772,7 +2962,7 @@ if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgsl_get_modem_info %s value=%08X\n", - __FILE__,__LINE__, info->device_name, *value ); + __FILE__,__LINE__, info->device_name, result ); PUT_USER(err,result,value); return err; @@ -2928,7 +3118,9 @@ case MGSL_IOCGSTATS: return mgsl_get_stats(info,(struct mgsl_icount*)arg); case MGSL_IOCWAITEVENT: - return mgsl_wait_event(info,(int)arg); + return mgsl_wait_event(info,(int*)arg); + case MGSL_IOCLOOPTXDONE: + return mgsl_loopmode_send_done(info); case MGSL_IOCCLRMODCOUNT: while(MOD_IN_USE) MOD_DEC_USE_COUNT; @@ -3626,11 +3818,6 @@ } spin_unlock_irqrestore(&info->irq_spinlock,flags); -#if 0 && LINUX_VERSION_CODE >= VERSION(2,1,0) - ret += sprintf(buf+ret, "irq_spinlock=%08X\n", - info->irq_spinlock.lock ); -#endif - return ret; } /* end of line_info() */ @@ -4227,6 +4414,18 @@ if ( PCIBIOS_SUCCESSFUL == pcibios_find_device( MICROGATE_VENDOR_ID, SYNCLINK_DEVICE_ID, i, &bus, &func) ) { +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + struct pci_dev *pdev = pci_find_slot(bus,func); + irq_line = pdev->irq; +#else + if (pcibios_read_config_byte(bus,func, + PCI_INTERRUPT_LINE,&irq_line) ) { + printk( "%s(%d):USC I/O addr not set.\n", + __FILE__,__LINE__); + continue; + } +#endif + if (pcibios_read_config_dword(bus,func, PCI_BASE_ADDRESS_3,&shared_mem_base) ) { printk( "%s(%d):Shared mem addr not set.\n", @@ -4248,13 +4447,6 @@ continue; } - if (pcibios_read_config_byte(bus,func, - PCI_INTERRUPT_LINE,&irq_line) ) { - printk( "%s(%d):USC I/O addr not set.\n", - __FILE__,__LINE__); - continue; - } - info = mgsl_allocate_device(); if ( !info ) { /* error allocating device instance data */ @@ -4671,29 +4863,53 @@ { u16 RegValue; - /* Channel mode Register (CMR) - * - * <15..14> 00 Tx Sub modes, Underrun Action - * <13> 0 1 = Send Preamble before opening flag - * <12> 0 1 = Consecutive Idles share common 0 - * <11..8> 0110 Transmitter mode = HDLC/SDLC - * <7..4> 0000 Rx Sub modes, addr/ctrl field handling - * <3..0> 0110 Receiver mode = HDLC/SDLC - * - * 0000 0110 0000 0110 = 0x0606 - */ + if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) + { + /* + ** Channel Mode Register (CMR) + ** + ** <15..14> 10 Tx Sub Modes, Send Flag on Underrun + ** <13> 0 0 = Transmit Disabled (initially) + ** <12> 0 1 = Consecutive Idles share common 0 + ** <11..8> 1110 Transmitter Mode = HDLC/SDLC Loop + ** <7..4> 0000 Rx Sub Modes, addr/ctrl field handling + ** <3..0> 0110 Receiver Mode = HDLC/SDLC + ** + ** 1000 1110 0000 0110 = 0x8e06 + */ + RegValue = 0x8e06; + + /*-------------------------------------------------- + * ignore user options for UnderRun Actions and + * preambles + *--------------------------------------------------*/ + } + else + { + /* Channel mode Register (CMR) + * + * <15..14> 00 Tx Sub modes, Underrun Action + * <13> 0 1 = Send Preamble before opening flag + * <12> 0 1 = Consecutive Idles share common 0 + * <11..8> 0110 Transmitter mode = HDLC/SDLC + * <7..4> 0000 Rx Sub modes, addr/ctrl field handling + * <3..0> 0110 Receiver mode = HDLC/SDLC + * + * 0000 0110 0000 0110 = 0x0606 + */ - RegValue = 0x0606; + RegValue = 0x0606; - if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 ) - RegValue |= BIT14; - else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG ) - RegValue |= BIT15; - else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC ) - RegValue |= BIT15 + BIT14; + if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 ) + RegValue |= BIT14; + else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG ) + RegValue |= BIT15; + else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC ) + RegValue |= BIT15 + BIT14; - if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE ) - RegValue |= BIT13; + if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE ) + RegValue |= BIT13; + } if ( info->params.flags & HDLC_FLAG_SHARE_ZERO ) RegValue |= BIT12; @@ -4862,6 +5078,8 @@ RegValue |= 0x0003; /* RxCLK from DPLL */ else if ( info->params.flags & HDLC_FLAG_RXC_BRG ) RegValue |= 0x0004; /* RxCLK from BRG0 */ + else if ( info->params.flags & HDLC_FLAG_RXC_TXCPIN) + RegValue |= 0x0006; /* RxCLK from TXC Input */ else RegValue |= 0x0007; /* RxCLK from Port1 */ @@ -4869,6 +5087,8 @@ RegValue |= 0x0018; /* TxCLK from DPLL */ else if ( info->params.flags & HDLC_FLAG_TXC_BRG ) RegValue |= 0x0020; /* TxCLK from BRG0 */ + else if ( info->params.flags & HDLC_FLAG_TXC_RXCPIN) + RegValue |= 0x0038; /* RxCLK from TXC Input */ else RegValue |= 0x0030; /* TxCLK from Port0 */ @@ -4922,10 +5142,24 @@ /* of rounding up and then subtracting 1 we just don't subtract */ /* the one in this case. */ - Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed); - if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2) - / info->params.clock_speed) ) - Tc--; + /*-------------------------------------------------- + * ejz: for DPLL mode, application should use the + * same clock speed as the partner system, even + * though clocking is derived from the input RxData. + * In case the user uses a 0 for the clock speed, + * default to 0xffffffff and don't try to divide by + * zero + *--------------------------------------------------*/ + if ( info->params.clock_speed ) + { + Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed); + if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2) + / info->params.clock_speed) ) + Tc--; + } + else + Tc = -1; + /* Write 16-bit Time Constant for BRG1 */ usc_OutReg( info, TC1R, Tc ); @@ -6328,6 +6562,13 @@ if ( debug_level >= DEBUG_LEVEL_DATA ) mgsl_trace_block(info,Buffer,BufferSize,1); + if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) { + /* set CMR:13 to start transmit when + * next GoAhead (abort) is received + */ + info->cmr_value |= BIT13; + } + /* Setup the status and RCC (Frame Size) fields of the 1st */ /* buffer entry in the transmit DMA buffer list. */ @@ -6381,7 +6622,7 @@ unsigned int i; BOOLEAN rc = TRUE; unsigned long flags; - + spin_lock_irqsave(&info->irq_spinlock,flags); usc_reset(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); @@ -6471,7 +6712,7 @@ usc_reset(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); - if ( !info->irq_occurred ) + if ( !info->irq_occurred ) return FALSE; else return TRUE; @@ -6499,7 +6740,7 @@ volatile unsigned long EndTime; unsigned long flags; MGSL_PARAMS tmp_params; - + /* save current port options */ memcpy(&tmp_params,&info->params,sizeof(MGSL_PARAMS)); /* load default port options */ @@ -6657,7 +6898,7 @@ /**********************************/ /* WAIT FOR TRANSMIT FIFO TO FILL */ /**********************************/ - + /* Wait 100ms */ EndTime = jiffies + jiffies_from_ms(100); @@ -6724,7 +6965,7 @@ if ( rc == TRUE ){ /* CHECK FOR TRANSMIT ERRORS */ - if ( status & (BIT5 + BIT1) ) + if ( status & (BIT5 + BIT1) ) rc = FALSE; } @@ -6981,13 +7222,90 @@ if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) { info->icount.txtimeout++; } - spin_lock_irqsave(&info->irq_spinlock,flags); info->tx_active = 0; info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + if ( info->params.flags & HDLC_FLAG_HDLC_LOOPMODE ) + usc_loopmode_cancel_transmit( info ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); mgsl_bh_transmit_data(info,0); } /* end of mgsl_tx_timeout() */ + +/* signal that there are no more frames to send, so that + * line is 'released' by echoing RxD to TxD when current + * transmission is complete (or immediately if no tx in progress). + */ +static int mgsl_loopmode_send_done( struct mgsl_struct * info ) +{ + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + if (info->params.flags & HDLC_FLAG_HDLC_LOOPMODE) { + if (info->tx_active) + info->loopmode_send_done_requested = TRUE; + else + usc_loopmode_send_done(info); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return 0; +} + +/* release the line by echoing RxD to TxD + * upon completion of a transmit frame + */ +void usc_loopmode_send_done( struct mgsl_struct * info ) +{ + info->loopmode_send_done_requested = FALSE; + /* clear CMR:13 to 0 to start echoing RxData to TxData */ + info->cmr_value &= ~BIT13; + usc_OutReg(info, CMR, info->cmr_value); +} + +/* abort a transmit in progress while in HDLC LoopMode + */ +void usc_loopmode_cancel_transmit( struct mgsl_struct * info ) +{ + /* reset tx dma channel and purge TxFifo */ + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + usc_DmaCmd( info, DmaCmd_ResetTxChannel ); + usc_loopmode_send_done( info ); +} + +/* for HDLC/SDLC LoopMode, setting CMR:13 after the transmitter is enabled + * is an Insert Into Loop action. Upon receipt of a GoAhead sequence (RxAbort) + * we must clear CMR:13 to begin repeating TxData to RxData + */ +void usc_loopmode_insert_request( struct mgsl_struct * info ) +{ + info->loopmode_insert_requested = TRUE; + + /* enable RxAbort irq. On next RxAbort, clear CMR:13 to + * begin repeating TxData on RxData (complete insertion) + */ + usc_OutReg( info, RICR, + (usc_InReg( info, RICR ) | RXSTATUS_ABORT_RECEIVED ) ); + + /* set CMR:13 to insert into loop on next GoAhead (RxAbort) */ + info->cmr_value |= BIT13; + usc_OutReg(info, CMR, info->cmr_value); +} + +/* return 1 if station is inserted into the loop, otherwise 0 + */ +int usc_loopmode_active( struct mgsl_struct * info) +{ + return usc_InReg( info, CCSR ) & BIT7 ? 1 : 0 ; +} + +/* return 1 if USC is in loop send mode, otherwise 0 + */ +int usc_loopmode_send_active( struct mgsl_struct * info ) +{ + return usc_InReg( info, CCSR ) & BIT6 ? 1 : 0 ; +} diff -u --recursive --new-file v2.3.6/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v2.3.6/linux/drivers/char/tty_io.c Tue May 11 14:37:40 1999 +++ linux/drivers/char/tty_io.c Wed Jun 16 19:26:27 1999 @@ -651,9 +651,7 @@ ssize_t ret = 0, written = 0; struct inode *inode = file->f_dentry->d_inode; - up(&inode->i_sem); - if (down_interruptible(&inode->i_atomic_write)) { - down(&inode->i_sem); + if (down_interruptible(&inode->i_sem)) { return -ERESTARTSYS; } for (;;) { @@ -678,8 +676,7 @@ file->f_dentry->d_inode->i_mtime = CURRENT_TIME; ret = written; } - up(&inode->i_atomic_write); - down(&inode->i_sem); + up(&inode->i_sem); return ret; } diff -u --recursive --new-file v2.3.6/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v2.3.6/linux/drivers/net/Makefile Mon Jun 7 14:35:09 1999 +++ linux/drivers/net/Makefile Thu Jun 17 01:11:35 1999 @@ -143,14 +143,6 @@ endif endif -ifeq ($(CONFIG_ETHERH),y) -CONFIG_8390_BUILTIN = y -else - ifeq ($(CONFIG_ETHERH),m) - CONFIG_8390_MODULE = y - endif -endif - ifeq ($(CONFIG_WD80x3),y) L_OBJS += wd.o CONFIG_8390_BUILTIN = y @@ -168,14 +160,6 @@ ifeq ($(CONFIG_EL2),m) CONFIG_8390_MODULE = y M_OBJS += 3c503.o - endif -endif - -ifeq ($(CONFIG_ETHERH),y) -CONFIG_8390_BUILTIN = y -else - ifeq ($(CONFIG_ETHERH),m) - CONFIG_8390_MODULE = y endif endif diff -u --recursive --new-file v2.3.6/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c --- v2.3.6/linux/drivers/net/ibmtr.c Wed May 12 13:27:37 1999 +++ linux/drivers/net/ibmtr.c Wed Jun 16 19:26:27 1999 @@ -515,7 +515,7 @@ /* How much shared RAM is on adapter ? */ #ifdef PCMCIA ti->avail_shared_ram = pcmcia_reality_check(get_sram_size(ti)); - ibmtr_mem_base = ti->sram_base ; + ibmtr_mem_base = ti->sram_base << 12 ; #else ti->avail_shared_ram = get_sram_size(ti); #endif @@ -835,6 +835,9 @@ (int)readb(ti->srb + offsetof(struct srb_close_adapter, ret_code))); dev->start = 0; +#ifdef PCMCIA + ti->sram = 0 ; +#endif DPRINTK("Adapter closed.\n"); MOD_DEC_USE_COUNT; diff -u --recursive --new-file v2.3.6/linux/drivers/net/irda/irport.c linux/drivers/net/irda/irport.c --- v2.3.6/linux/drivers/net/irda/irport.c Mon Jun 7 16:18:58 1999 +++ linux/drivers/net/irda/irport.c Wed Jun 16 19:26:27 1999 @@ -321,7 +321,7 @@ /* Turn on interrups */ outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, iobase+UART_IER); - spin_unlock_irqrestore(&self->lock, flags); + spin_unlock_irqrestore(&idev->lock, flags); } /* diff -u --recursive --new-file v2.3.6/linux/drivers/pci/pci.c linux/drivers/pci/pci.c --- v2.3.6/linux/drivers/pci/pci.c Sun Mar 7 15:19:55 1999 +++ linux/drivers/pci/pci.c Sat Jun 19 18:20:13 1999 @@ -47,13 +47,23 @@ struct pci_dev * pci_find_device(unsigned int vendor, unsigned int device, struct pci_dev *from) { - if (!from) - from = pci_devices; - else - from = from->next; - while (from && (from->vendor != vendor || from->device != device)) - from = from->next; - return from; + struct pci_dev *next; + + next = pci_devices; + if (from) + next = from->next; + + while (next) { + struct pci_dev *dev = next; + next = next->next; + if (vendor != PCI_ANY_ID && dev->vendor != vendor) + continue; + if (device != PCI_ANY_ID && dev->device != device) + continue; + + return dev; + } + return NULL; } @@ -178,10 +188,8 @@ if (pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l) || /* some broken boards return 0 if a slot is empty: */ - l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) { - is_multi = 0; + l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) continue; - } dev = kmalloc(sizeof(*dev), GFP_ATOMIC); memset(dev, 0, sizeof(*dev)); diff -u --recursive --new-file v2.3.6/linux/drivers/sbus/audio/cs4215.h linux/drivers/sbus/audio/cs4215.h --- v2.3.6/linux/drivers/sbus/audio/cs4215.h Mon Mar 15 16:11:30 1999 +++ linux/drivers/sbus/audio/cs4215.h Thu Jun 17 01:08:50 1999 @@ -11,11 +11,10 @@ struct cs4215 { __u8 data[4]; /* Data mode: Time slots 5-8 */ __u8 ctrl[4]; /* Ctrl mode: Time slots 1-4 */ - __volatile__ struct dbri_mem td; - __volatile__ struct dbri_mem rd; __u8 onboard; - __u32 status; - __u32 version; + __u8 offset; /* Bit offset from frame sync to time slot 1 */ + volatile __u32 status; + volatile __u32 version; }; diff -u --recursive --new-file v2.3.6/linux/drivers/sbus/audio/dbri.c linux/drivers/sbus/audio/dbri.c --- v2.3.6/linux/drivers/sbus/audio/dbri.c Mon Mar 15 16:11:30 1999 +++ linux/drivers/sbus/audio/dbri.c Thu Jun 17 01:08:50 1999 @@ -79,25 +79,25 @@ #define D_CMD (1<<2) #define D_MM (1<<3) #define D_USR (1<<4) +#define D_DESC (1<<5) -/* static int dbri_debug = D_GEN|D_INT|D_CMD|D_MM|D_USR; */ static int dbri_debug = 0; MODULE_PARM(dbri_debug, "i"); +static int dbri_trace = 0; +MODULE_PARM(dbri_trace, "i"); +#define tprintk(x) if(dbri_trace) printk x + static char *cmds[] = { "WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS", "SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV" }; -/* Bit hunting */ -#define dumpcmd {int i; for(i=0; idma->cmd[i]); } - #define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (1 << 27) | value) #else #define dprintk(a, x) -#define dumpcmd #define DBRI_CMD(cmd, intr, value) ((cmd << 28) | (intr << 27) | value) #endif /* DBRI_DEBUG */ @@ -114,41 +114,42 @@ **************************************************************************** ************** DBRI initialization and command synchronization ************* **************************************************************************** -*/ +Commands are sent to the DBRI by building a list of them in memory, +then writing the address of the first list item to DBRI register 8. +The list is terminated with a WAIT command, which can generate a +CPU interrupt if required. + +Since the DBRI can run in parallel with the CPU, several means of +synchronization present themselves. The original scheme (Rudolf's) +was to set a flag when we "cmdlock"ed the DBRI, clear the flag when +an interrupt signaled completion, and wait on a wait_queue if a routine +attempted to cmdlock while the flag was set. The problems arose when +we tried to cmdlock from inside an interrupt handler, which might +cause scheduling in an interrupt (if we waited), etc, etc + +A more sophisticated scheme might involve a circular command buffer +or an array of command buffers. A routine could fill one with +commands and link it onto a list. When a interrupt signaled +completion of the current command buffer, look on the list for +the next one. + +I've decided to implement something much simpler - after each command, +the CPU waits for the DBRI to finish the command by polling the P bit +in DBRI register 0. I've tried to implement this in such a way +that might make implementing a more sophisticated scheme easier. + +Every time a routine wants to write commands to the DBRI, it must +first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd +in return. After the commands have been writen, dbri_cmdsend() is +called with the final pointer value. -/* - * Commands are sent to the DBRI by building a list of them in memory, - * then writing the address of the first list item to DBRI register 8. - * The list is terminated with a WAIT command, which can generate a - * CPU interrupt if required. - * - * Since the DBRI can run asynchronously to the CPU, several means of - * synchronization present themselves. The original scheme (Rudolf's) - * was to set a flag when we "cmdlock"ed the DBRI, clear the flag when - * an interrupt signaled completion, and wait on a wait_queue if a routine - * attempted to cmdlock while the flag was set. The problems arose when - * we tried to cmdlock from inside an interrupt handler, which might - * cause scheduling in an interrupt (if we waited), etc, etc - * - * A more sophisticated scheme might involve a circular command buffer - * or an array of command buffers. A routine could fill one with - * commands and link it onto a list. When a interrupt signaled - * completion of the current command buffer, look on the list for - * the next one. - * - * I've decided to implement something much simpler - after each command, - * the CPU waits for the DBRI to finish the command by polling the P bit - * in DBRI register 0. I've tried to implement this in such a way - * that might make implementing a more sophisticated scheme easier. - * - * Every time a routine wants to write commands to the DBRI, it must - * first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd - * in return. After the commands have been writen, dbri_cmdsend() is - * called with the final pointer value. - */ +Something a little more clever is required if this code is ever run +on an SMP machine. + +*/ -static int dbri_locked = 0; /* XXX not SMP safe! XXX */ +static int dbri_locked = 0; static volatile int * dbri_cmdlock(struct dbri *dbri) { @@ -159,9 +160,21 @@ return dbri->dma->cmd; } +static void dbri_process_interrupt_buffer(struct dbri *); + static void dbri_cmdsend(struct dbri *dbri, volatile int * cmd) { - int maxloops = 1000000; + int MAXLOOPS = 1000000; + int maxloops = MAXLOOPS; + unsigned int flags; + volatile int * ptr; + + for (ptr = dbri->dma->cmd; ptr < cmd; ptr ++) { + dprintk(D_CMD, ("DBRI cmd: %08x:%08x\n", + (unsigned int) ptr, *ptr)); + } + + save_and_cli(flags); dbri_locked --; if (dbri_locked != 0) { @@ -170,14 +183,25 @@ printk("DBRI: Command buffer overflow! (bug in driver)\n"); } else { *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); - *(cmd++) = DBRI_CMD(D_WAIT, 0, 0); + *(cmd++) = DBRI_CMD(D_WAIT, 1, 0); + dbri->wait_seen = 0; dbri->regs->reg8 = (int)dbri->dma_dvma->cmd; - while ((maxloops--) > 0 && (dbri->regs->reg0 & D_P)); + while ((--maxloops) > 0 && (dbri->regs->reg0 & D_P)); + if (maxloops == 0) { + printk("DBRI: Chip never completed command buffer\n"); + } else { + while ((--maxloops) > 0 && (! dbri->wait_seen)) + dbri_process_interrupt_buffer(dbri); + if (maxloops == 0) { + printk("DBRI: Chip never acked WAIT\n"); + } else { + dprintk(D_INT, ("DBRI: Chip completed command buffer (%d)\n", + MAXLOOPS - maxloops)); + } + } } - if (maxloops == 0) { - printk("DBRI: Chip never completed command buffer\n"); - } + restore_flags(flags); } static void dbri_reset(struct dbri *dbri) @@ -198,7 +222,7 @@ dbri_reset(dbri); free_irq(dbri->irq, dbri); sparc_free_io(dbri->regs, dbri->regs_size); - /* Should we release the DMA structure dbri->dma here? */ + release_region((unsigned long) dbri->dma, sizeof(struct dbri_dma)); kfree(dbri); } @@ -242,6 +266,17 @@ **************************************************************************** *************************** DBRI interrupt handler ************************* **************************************************************************** + +The DBRI communicates with the CPU mainly via a circular interrupt +buffer. When an interrupt is signaled, the CPU walks through the +buffer and calls dbri_process_one_interrupt() for each interrupt word. +Complicated interrupts are handled by dedicated functions (which +appear first in this file). Any pending interrupts can be serviced by +calling dbri_process_interrupt_buffer(), which works even if the CPU's +interrupts are disabled. This function is used by dbri_cmdsend() +to make sure we're synced up with the chip after each command sequence, +even if we're running cli'ed. + */ @@ -263,6 +298,7 @@ case 2: b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1); case 1: + case 0: break; default: printk("DBRI reverse_bytes: unsupported length\n"); @@ -273,37 +309,39 @@ /* transmission_complete_intr() * * Called by main interrupt handler when DBRI signals transmission complete - * on a pipe. + * on a pipe (interrupt triggered by the B bit in a transmit descriptor). * * Walks through the pipe's list of transmit buffer descriptors, releasing - * each one's DMA buffer (if present) and signaling its callback routine - * (if present), before flaging the descriptor available and proceeding - * to the next one. - * - * Assumes that only the last in a chain of descriptors will have FINT - * sent to signal an interrupt, so that the chain will be completely - * transmitted by the time we get here, and there's no need to save - * any of the descriptors. In particular, use of the DBRI's CDP command - * is precluded, but I've not been able to get CDP working reliably anyway. + * each one's DMA buffer (if present), flagging the descriptor available, + * and signaling its callback routine (if present), before proceeding + * to the next one. Stops when the first descriptor is found without + * TBC (Transmit Buffer Complete) set, or we've run through them all. */ static void transmission_complete_intr(struct dbri *dbri, int pipe) { - int td = dbri->pipes[pipe].desc; + int td; int status; void *buffer; void (*callback)(void *, int); + void *callback_arg; - dbri->pipes[pipe].desc = -1; + td = dbri->pipes[pipe].desc; - for (; td >= 0; td = dbri->descs[td].next) { + while (td >= 0) { if (td >= DBRI_NO_DESCS) { printk("DBRI: invalid td on pipe %d\n", pipe); return; } - status = dbri->dma->desc[td].word4; + status = DBRI_TD_STATUS(dbri->dma->desc[td].word4); + + if (! (status & DBRI_TD_TBC)) { + break; + } + + dprintk(D_INT, ("DBRI: TD %d, status 0x%02x\n", td, status)); buffer = dbri->descs[td].buffer; if (buffer) { @@ -313,12 +351,16 @@ } callback = dbri->descs[td].output_callback; - if (callback != NULL) { - callback(dbri->descs[td].output_callback_arg, - DBRI_TD_STATUS(status) & 0xe); - } + callback_arg = dbri->descs[td].output_callback_arg; dbri->descs[td].inuse = 0; + + td = dbri->descs[td].next; + dbri->pipes[pipe].desc = td; + + if (callback != NULL) { + callback(callback_arg, status & 0xe); + } } } @@ -335,7 +377,7 @@ } dbri->descs[rd].inuse = 0; - dbri->pipes[pipe].desc = -1; + dbri->pipes[pipe].desc = dbri->descs[rd].next; status = dbri->dma->desc[rd].word1; buffer = dbri->descs[rd].buffer; @@ -351,95 +393,170 @@ DBRI_RD_STATUS(status), DBRI_RD_CNT(status)-2); } + + dprintk(D_INT, ("DBRI: Recv RD %d, status 0x%02x, len %d\n", + rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status))); } -static void dbri_intr(int irq, void *opaque, struct pt_regs *regs) +static void dbri_process_one_interrupt(struct dbri *dbri, int x) { - struct dbri *dbri = (struct dbri *)opaque; - int x; - - /* - * Read it, so the interrupt goes away. - */ - x = dbri->regs->reg1; + int val = D_INTR_GETVAL(x); + int channel = D_INTR_GETCHAN(x); + int command = D_INTR_GETCMD(x); + int code = D_INTR_GETCODE(x); + int rval = D_INTR_GETRVAL(x); + + if (channel == D_INTR_CMD) { + dprintk(D_INT,("DBRI: INTR: Command: %-5s Value:%d\n", + cmds[command], val)); + } else { + dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n", + channel, code, rval)); + } - if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) { - /* - * What should I do here ? - */ - if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n"); - if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n"); - if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n"); - if(x & D_MBE) printk("DBRI: Burst Error on SBus\n"); + if (channel == D_INTR_CMD && command == D_WAIT) { + dbri->wait_seen ++; } - if (!(x & D_IR)) /* Not for us */ - return; + if (code == D_INTR_SBRI) { - x = dbri->dma->intr[dbri->dbri_irqp]; - while (x != 0) { - int val = D_INTR_GETVAL(x); - int channel = D_INTR_GETCHAN(x); + /* SBRI - BRI status change */ - dbri->dma->intr[dbri->dbri_irqp] = 0; + int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7}; + dbri->liu_state = liu_states[val & 0x7]; + if (dbri->liu_callback) + dbri->liu_callback(dbri->liu_callback_arg); + } - if(D_INTR_GETCHAN(x) == D_INTR_CMD) { - dprintk(D_INT,("DBRI: INTR: Command: %-5s Value:%d\n", - cmds[D_INTR_GETCMD(x)], D_INTR_GETVAL(x))); - } else { - dprintk(D_INT,("DBRI: INTR: Chan:%d Code:%d Val:%#x\n", - D_INTR_GETCHAN(x), D_INTR_GETCODE(x), - D_INTR_GETRVAL(x))); - } + if (code == D_INTR_BRDY) { + reception_complete_intr(dbri, channel); + } - if (D_INTR_GETCODE(x) == D_INTR_SBRI) { + if (code == D_INTR_XCMP) { + transmission_complete_intr(dbri, channel); + } - /* SBRI - BRI status change */ + if (code == D_INTR_UNDR) { - int liu_states[] = {1, 0, 8, 3, 4, 5, 6, 7}; - dbri->liu_state = liu_states[val & 0x7]; - if (dbri->liu_callback) - dbri->liu_callback(dbri->liu_callback_arg); - } + /* UNDR - Transmission underrun + * resend SDP command with clear pipe bit (C) set + */ - if (D_INTR_GETCODE(x) == D_INTR_BRDY) { - reception_complete_intr(dbri, channel); - } + volatile int *cmd; + int pipe = channel; + int td = dbri->pipes[pipe].desc; + + dbri->dma->desc[td].word4 = 0; + + cmd = dbri_cmdlock(dbri); + *(cmd++) = DBRI_CMD(D_SDP, 0, + dbri->pipes[pipe].sdp + | D_SDP_P | D_SDP_C | D_SDP_2SAME); + *(cmd++) = (int) & dbri->dma_dvma->desc[td]; + dbri_cmdsend(dbri, cmd); + } + + if (code == D_INTR_FXDT) { + + /* FXDT - Fixed data change */ - if (D_INTR_GETCODE(x) == D_INTR_XCMP) { - transmission_complete_intr(dbri, channel); + if (dbri->pipes[channel].sdp & D_SDP_MSB) { + val = reverse_bytes(val, dbri->pipes[channel].length); } - if (D_INTR_GETCODE(x) == D_INTR_FXDT) { + if (dbri->pipes[channel].recv_fixed_ptr) { + * dbri->pipes[channel].recv_fixed_ptr = val; + } + } +} - /* FXDT - Fixed data change */ +/* dbri_process_interrupt_buffer advances through the DBRI's interrupt + * buffer until it finds a zero word (indicating nothing more to do + * right now). Non-zero words require processing and are handed off + * to dbri_process_one_interrupt AFTER advancing the pointer. This + * order is important since we might recurse back into this function + * and need to make sure the pointer has been advanced first. + */ - if (dbri->pipes[D_INTR_GETCHAN(x)].sdp & D_SDP_MSB) { - val = reverse_bytes(val, dbri->pipes[channel].length); - } +static void dbri_process_interrupt_buffer(struct dbri *dbri) +{ + int x; - if (dbri->pipes[D_INTR_GETCHAN(x)].recv_fixed_ptr) { - * dbri->pipes[channel].recv_fixed_ptr = val; - } - } + while ((x = dbri->dma->intr[dbri->dbri_irqp]) != 0) { + dbri->dma->intr[dbri->dbri_irqp] = 0; dbri->dbri_irqp++; if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK)) dbri->dbri_irqp = 1; else if ((dbri->dbri_irqp & (DBRI_INT_BLK-1)) == 0) dbri->dbri_irqp++; - x = dbri->dma->intr[dbri->dbri_irqp]; + + dbri_process_one_interrupt(dbri, x); } } +static void dbri_intr(int irq, void *opaque, struct pt_regs *regs) +{ + struct dbri *dbri = (struct dbri *)opaque; + int x; + + /* + * Read it, so the interrupt goes away. + */ + x = dbri->regs->reg1; + + dprintk(D_INT, ("DBRI: Interrupt! (reg1=0x%08x)\n", x)); + + if ( x & (D_MRR|D_MLE|D_LBG|D_MBE) ) { + + if(x & D_MRR) printk("DBRI: Multiple Error Ack on SBus\n"); + if(x & D_MLE) printk("DBRI: Multiple Late Error on SBus\n"); + if(x & D_LBG) printk("DBRI: Lost Bus Grant on SBus\n"); + if(x & D_MBE) printk("DBRI: Burst Error on SBus\n"); + + /* Some of these SBus errors cause the chip's SBus circuitry + * to be disabled, so just re-enable and try to keep going. + * + * The only one I've seen is MRR, which will be triggered + * if you let a transmit pipe underrun, then try to CDP it. + * + * If these things persist, we should probably reset + * and re-init the chip. + */ + + dbri->regs->reg0 &= ~D_D; + } + +#if 0 + if (!(x & D_IR)) /* Not for us */ + return; +#endif + + dbri_process_interrupt_buffer(dbri); +} + /* **************************************************************************** ************************** DBRI data pipe management *********************** **************************************************************************** + +While DBRI control functions use the command and interrupt buffers, the +main data path takes the form of data pipes, which can be short (command +and interrupt driven), or long (attached to DMA buffers). These functions +provide a rudimentary means of setting up and managing the DBRI's pipes, +but the calling functions have to make sure they respect the pipes' linked +list ordering, among other things. The transmit and receive functions +here interface closely with the transmit and receive interrupt code. + */ +static int pipe_active(struct dbri *dbri, int pipe) +{ + return (dbri->pipes[pipe].desc != -1); +} + /* reset_pipe(dbri, pipe) * @@ -449,6 +566,7 @@ static void reset_pipe(struct dbri *dbri, int pipe) { int sdp; + int desc; volatile int *cmd; if (pipe < 0 || pipe > 31) { @@ -467,6 +585,35 @@ *(cmd++) = 0; dbri_cmdsend(dbri, cmd); + desc = dbri->pipes[pipe].desc; + while (desc != -1) { + void *buffer = dbri->descs[desc].buffer; + void (*output_callback) (void *, int) + = dbri->descs[desc].output_callback; + void *output_callback_arg + = dbri->descs[desc].output_callback_arg; + void (*input_callback) (void *, int, unsigned int) + = dbri->descs[desc].input_callback; + void *input_callback_arg + = dbri->descs[desc].input_callback_arg; + + if (buffer) { + mmu_release_scsi_one(sbus_dvma_addr(buffer), + dbri->descs[desc].len, + dbri->sdev->my_bus); + } + + dbri->descs[desc].inuse = 0; + desc = dbri->descs[desc].next; + + if (output_callback) { + output_callback(output_callback_arg, -1); + } + if (input_callback) { + input_callback(input_callback_arg, -1, 0); + } + } + dbri->pipes[pipe].desc = -1; } @@ -482,140 +629,179 @@ /* sdp &= 0xf800; */ } + /* If this is a fixed receive pipe, arrange for an interrupt + * every time its data changes + */ + + if (D_SDP_MODE(sdp) == D_SDP_FIXED && ! (sdp & D_SDP_TO_SER)) { + sdp |= D_SDP_CHANGE; + } + sdp |= D_PIPE(pipe); dbri->pipes[pipe].sdp = sdp; + dbri->pipes[pipe].desc = -1; reset_pipe(dbri, pipe); } -enum master_or_slave { CHImaster, CHIslave }; - -static void reset_chi(struct dbri *dbri, enum master_or_slave master_or_slave, - int bits_per_frame) +static void link_time_slot(struct dbri *dbri, int pipe, + enum in_or_out direction, int basepipe, + int length, int cycle) { volatile int *cmd; int val; + int prevpipe; + int nextpipe; - cmd = dbri_cmdlock(dbri); + if (pipe < 0 || pipe > 31 || basepipe < 0 || basepipe > 31) { + printk("DBRI: link_time_slot called with illegal pipe number\n"); + return; + } - /* Set CHI Anchor: Pipe 16 */ + if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[basepipe].sdp == 0) { + printk("DBRI: link_time_slot called on uninitialized pipe\n"); + return; + } - val = D_DTS_VI | D_DTS_VO | D_DTS_INS | - D_DTS_PRVIN(D_P_16) | D_DTS_PRVOUT(D_P_16) | D_PIPE(D_P_16); - *(cmd++) = DBRI_CMD(D_DTS, 0, val); - *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16); - *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(D_P_16); - - dbri->pipes[16].sdp = 1; - dbri->pipes[16].nextpipe = 16; - - if (master_or_slave == CHIslave) { - /* Setup DBRI for CHI Slave - receive clock, frame sync (FS) - * - * CHICM = 0 (slave mode, 8 kHz frame rate) - * IR = give immediate CHI status interrupt - * EN = give CHI status interrupt upon change - */ - *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0) - | D_CHI_IR | D_CHI_EN); + /* Deal with CHI special case: + * "If transmission on edges 0 or 1 is desired, then cycle n + * (where n = # of bit times per frame...) must be used." + * - DBRI data sheet, page 11 + */ + + if (basepipe == 16 && direction == PIPEoutput && cycle == 0) { + cycle = dbri->chi_bpf; + } + + if (basepipe == pipe) { + prevpipe = pipe; + nextpipe = pipe; } else { - /* Setup DBRI for CHI Master - generate clock, FS - * - * BPF = bits per 8 kHz frame - * 12.288 MHz / CHICM_divisor = clock rate - * FD = 1 - drive CHIFS on rising edge of CHICK + + /* We're not initializing a new linked list (basepipe != pipe), + * so run through the linked list and find where this pipe + * should be sloted in, based on its cycle. CHI confuses + * things a bit, since it has a single anchor for both its + * transmit and receive lists. */ - int clockrate = bits_per_frame * 8; - int divisor = 12288 / clockrate; + if (basepipe == 16) { + if (direction == PIPEinput) { + prevpipe = dbri->chi_in_pipe; + } else { + prevpipe = dbri->chi_out_pipe; + } + } else { + prevpipe = basepipe; + } + + nextpipe = dbri->pipes[prevpipe].nextpipe; - if (divisor > 255 || divisor * clockrate != 12288) { - printk("DBRI: illegal bits_per_frame in setup_chi\n"); + while (dbri->pipes[nextpipe].cycle < cycle + && dbri->pipes[nextpipe].nextpipe != basepipe) { + prevpipe = nextpipe; + nextpipe = dbri->pipes[nextpipe].nextpipe; } + } - *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD - | D_CHI_IR | D_CHI_EN - | D_CHI_BPF(bits_per_frame)); + if (prevpipe == 16) { + if (direction == PIPEinput) { + dbri->chi_in_pipe = pipe; + } else { + dbri->chi_out_pipe = pipe; + } + } else { + dbri->pipes[prevpipe].nextpipe = pipe; } - /* CHI Data Mode - * - * RCE = 0 - receive on falling edge of CHICK - * XCE = 1 - transmit on rising edge of CHICK - * XEN = 1 - enable transmitter - * REN = 1 - enable receiver - */ + dbri->pipes[pipe].nextpipe = nextpipe; + dbri->pipes[pipe].cycle = cycle; + dbri->pipes[pipe].length = length; - *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); + cmd = dbri_cmdlock(dbri); - *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN); + if (direction == PIPEinput) { + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe; + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); + *(cmd++) = 0; + } else { + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe; + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = 0; + *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); + } dbri_cmdsend(dbri, cmd); } -enum in_or_out { PIPEinput, PIPEoutput }; +/* I don't use this function, so it's basically untested. */ -static void link_time_slot(struct dbri *dbri, int pipe, - enum in_or_out direction, int prevpipe, - int length, int cycle) +static void unlink_time_slot(struct dbri *dbri, int pipe, + enum in_or_out direction, int prevpipe, + int nextpipe) { volatile int *cmd; int val; - int nextpipe; if (pipe < 0 || pipe > 31 || prevpipe < 0 || prevpipe > 31) { - printk("DBRI: link_time_slot called with illegal pipe number\n"); + printk("DBRI: unlink_time_slot called with illegal pipe number\n"); return; } - if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[prevpipe].sdp == 0) { - printk("DBRI: link_time_slot called on uninitialized pipe\n"); - return; - } - - if (pipe == prevpipe) { - nextpipe = pipe; - } else { - nextpipe = dbri->pipes[prevpipe].nextpipe; - } - - dbri->pipes[pipe].nextpipe = nextpipe; - dbri->pipes[pipe].cycle = cycle; - dbri->pipes[pipe].length = length; - cmd = dbri_cmdlock(dbri); if (direction == PIPEinput) { - val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe; + val = D_DTS_VI | D_DTS_DEL | D_DTS_PRVIN(prevpipe) | pipe; *(cmd++) = DBRI_CMD(D_DTS, 0, val); - *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); + *(cmd++) = D_TS_NEXT(nextpipe); *(cmd++) = 0; } else { - val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe; + val = D_DTS_VO | D_DTS_DEL | D_DTS_PRVOUT(prevpipe) | pipe; *(cmd++) = DBRI_CMD(D_DTS, 0, val); *(cmd++) = 0; - *(cmd++) = D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); + *(cmd++) = D_TS_NEXT(nextpipe); } dbri_cmdsend(dbri, cmd); } +/* xmit_fixed() / recv_fixed() + * + * Transmit/receive data on a "fixed" pipe - i.e, one whose contents are not + * expected to change much, and which we don't need to buffer. + * The DBRI only interrupts us when the data changes (receive pipes), + * or only changes the data when this function is called (transmit pipes). + * Only short pipes (numbers 16-31) can be used in fixed data mode. + * + * These function operate on a 32-bit field, no matter how large + * the actual time slot is. The interrupt handler takes care of bit + * ordering and alignment. An 8-bit time slot will always end up + * in the low-order 8 bits, filled either MSB-first or LSB-first, + * depending on the settings passed to setup_pipe() + */ + static void xmit_fixed(struct dbri *dbri, int pipe, unsigned int data) { volatile int *cmd; if (pipe < 16 || pipe > 31) { - printk("DBRI: xmit_fixed called with illegal pipe number\n"); + printk("DBRI: xmit_fixed: Illegal pipe number\n"); + return; + } + + if (D_SDP_MODE(dbri->pipes[pipe].sdp) == 0) { + printk("DBRI: xmit_fixed: Uninitialized pipe %d\n", pipe); return; } if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) { - printk("DBRI: xmit_fixed called on non-fixed pipe\n"); + printk("DBRI: xmit_fixed: Non-fixed pipe %d\n", pipe); return; } if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) { - printk("DBRI: xmit_fixed called on receive pipe\n"); + printk("DBRI: xmit_fixed: Called on receive pipe %d\n", pipe); return; } @@ -633,21 +819,7 @@ dbri_cmdsend(dbri, cmd); } -/* recv_fixed() - * - * Receive data on a "fixed" pipe - i.e, one whose contents are not - * expected to change much, and which we don't need to read constantly - * into a buffer. The DBRI only interrupts us when the data changes. - * Only short pipes (numbers 16-31) can be used in fixed data mode. - * - * Pass this function a pointer to a 32-bit field, no matter how large - * the actual time slot is. The interrupt handler takes care of bit - * ordering and alignment. An 8-bit time slot will always end up - * in the low-order 8 bits, filled either MSB-first or LSB-first, - * depending on the settings passed to setup_pipe() - */ - -static void recv_fixed(struct dbri *dbri, int pipe, __u32 *ptr) +static void recv_fixed(struct dbri *dbri, int pipe, volatile __u32 *ptr) { if (pipe < 16 || pipe > 31) { printk("DBRI: recv_fixed called with illegal pipe number\n"); @@ -655,12 +827,12 @@ } if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) { - printk("DBRI: recv_fixed called on non-fixed pipe\n"); + printk("DBRI: recv_fixed called on non-fixed pipe %d\n", pipe); return; } if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) { - printk("DBRI: recv_fixed called on transmit pipe\n"); + printk("DBRI: recv_fixed called on transmit pipe %d\n", pipe); return; } @@ -668,37 +840,46 @@ } +/* xmit_on_pipe() / recv_on_pipe() + * + * Transmit/receive data on a "long" pipe - i.e, one associated + * with a DMA buffer. + * + * Only pipe numbers 0-15 can be used in this mode. + * + * Both functions take pointer/len arguments pointing to a data buffer, + * and both provide callback functions (may be NULL) to notify higher + * level code when transmission/reception is complete. + * + * Both work by building chains of descriptors which identify the + * data buffers. Buffers too large for a single descriptor will + * be spread across multiple descriptors. + */ + static void xmit_on_pipe(struct dbri *dbri, int pipe, void * buffer, unsigned int len, void (*callback)(void *, int), void * callback_arg) { volatile int *cmd; + register unsigned int flags; int td = 0; int first_td = -1; - int last_td; + int last_td = -1; __u32 dvma_buffer; if (pipe < 0 || pipe > 15) { - printk("DBRI: xmit_on_pipe called with illegal pipe number\n"); + printk("DBRI: xmit_on_pipe: Illegal pipe number\n"); return; } if (dbri->pipes[pipe].sdp == 0) { - printk("DBRI: xmit_on_pipe called on uninitialized pipe\n"); + printk("DBRI: xmit_on_pipe: Uninitialized pipe %d\n", pipe); return; } if (! dbri->pipes[pipe].sdp & D_SDP_TO_SER) { - printk("DBRI: xmit_on_pipe called on receive pipe\n"); - return; - } - - /* XXX Fix this XXX - * Should be able to queue multiple buffers to send on a pipe - */ - - if (dbri->pipes[pipe].desc != -1) { - printk("DBRI: xmit_on_pipe called on active pipe\n"); + printk("DBRI: xmit_on_pipe: Called on receive pipe %d\n", + pipe); return; } @@ -707,10 +888,11 @@ while (len > 0) { int mylen; - for (td; td < DBRI_NO_DESCS; td ++) { + for (; td < DBRI_NO_DESCS; td ++) { if (! dbri->descs[td].inuse) break; } if (td == DBRI_NO_DESCS) { + printk("DBRI: xmit_on_pipe: No descriptors\n"); break; } @@ -744,15 +926,10 @@ len -= mylen; } - if (first_td == -1) { - printk("xmit_on_pipe: No descriptors available\n"); + if (first_td == -1 || last_td == -1) { return; } - if (len > 0) { - printk("xmit_on_pipe: Insufficient descriptors; data truncated\n"); - } - dbri->dma->desc[last_td].word1 |= DBRI_TD_I | DBRI_TD_F | DBRI_TD_B; dbri->descs[last_td].buffer = buffer; @@ -760,14 +937,54 @@ dbri->descs[last_td].output_callback = callback; dbri->descs[last_td].output_callback_arg = callback_arg; - dbri->pipes[pipe].desc = first_td; + for (td=first_td; td != -1; td = dbri->descs[td].next) { + dprintk(D_DESC, ("DBRI TD %d: %08x %08x %08x %08x\n", + td, + dbri->dma->desc[td].word1, + dbri->dma->desc[td].ba, + dbri->dma->desc[td].nda, + dbri->dma->desc[td].word4)); + } - cmd = dbri_cmdlock(dbri); + save_and_cli(flags); - *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C); - *(cmd++) = (int) & dbri->dma_dvma->desc[first_td]; + if (pipe_active(dbri, pipe)) { - dbri_cmdsend(dbri, cmd); + /* Pipe is already active - find last TD in use + * and link our first TD onto its end. Then issue + * a CDP command to let the DBRI know there's more data. + */ + + last_td = dbri->pipes[pipe].desc; + while (dbri->descs[last_td].next != -1) + last_td = dbri->descs[last_td].next; + + dbri->descs[last_td].next = first_td; + dbri->dma->desc[last_td].nda = + (int) & dbri->dma_dvma->desc[first_td]; + + cmd = dbri_cmdlock(dbri); + *(cmd++) = DBRI_CMD(D_CDP, 0, pipe); + dbri_cmdsend(dbri,cmd); + + } else { + + /* Pipe isn't active - issue an SDP command to start + * our chain of TDs running. + */ + + dbri->pipes[pipe].desc = first_td; + + cmd = dbri_cmdlock(dbri); + *(cmd++) = DBRI_CMD(D_SDP, 0, + dbri->pipes[pipe].sdp + | D_SDP_P | D_SDP_EVERY | D_SDP_C); + *(cmd++) = (int) & dbri->dma_dvma->desc[first_td]; + dbri_cmdsend(dbri, cmd); + + } + + restore_flags(flags); } static void recv_on_pipe(struct dbri *dbri, int pipe, @@ -776,69 +993,107 @@ void * callback_arg) { volatile int *cmd; + int first_rd = -1; + int last_rd = -1; int rd; + __u32 bus_buffer; if (pipe < 0 || pipe > 15) { - printk("DBRI: recv_on_pipe called with illegal pipe number\n"); + printk("DBRI: recv_on_pipe: Illegal pipe number\n"); return; } if (dbri->pipes[pipe].sdp == 0) { - printk("DBRI: recv_on_pipe called on uninitialized pipe\n"); + printk("DBRI: recv_on_pipe: Uninitialized pipe %d\n", pipe); return; } if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) { - printk("DBRI: recv_on_pipe called on transmit pipe\n"); + printk("DBRI: recv_on_pipe: Called on transmit pipe %d\n", + pipe); return; } /* XXX Fix this XXX - * Should be able to queue multiple buffers to send on a pipe + * Should be able to queue multiple buffers to receive on a pipe */ if (dbri->pipes[pipe].desc != -1) { - printk("DBRI: recv_on_pipe called on active pipe\n"); + printk("DBRI: recv_on_pipe: Called on active pipe %d\n", pipe); return; } - /* XXX Fix this XXX - * Use multiple descriptors, if needed, to fit in all the data - */ - - if (len > (1 << 13) - 1) { - printk("recv_on_pipe called with len=%d; truncated\n", len); - len = (1 << 13) - 1; - } - /* Make sure buffer size is multiple of four */ len &= ~3; - for (rd = 0; rd < DBRI_NO_DESCS; rd ++) { - if (! dbri->descs[rd].inuse) break; + bus_buffer = mmu_get_scsi_one(buffer, len, dbri->sdev->my_bus); + + while (len > 0) { + int rd; + int mylen; + + if (len > (1 << 13) - 4) { + mylen = (1 << 13) - 4; + } else { + mylen = len; + } + + for (rd = 0; rd < DBRI_NO_DESCS; rd ++) { + if (! dbri->descs[rd].inuse) break; + } + if (rd == DBRI_NO_DESCS) { + printk("DBRI recv_on_pipe: No descriptors\n"); + break; + } + + dbri->dma->desc[rd].word1 = 0; + dbri->dma->desc[rd].ba = bus_buffer; + dbri->dma->desc[rd].nda = 0; + dbri->dma->desc[rd].word4 = DBRI_RD_B | DBRI_RD_BCNT(mylen); + + dbri->descs[rd].buffer = NULL; + dbri->descs[rd].len = 0; + dbri->descs[rd].input_callback = NULL; + dbri->descs[rd].output_callback = NULL; + dbri->descs[rd].next = -1; + dbri->descs[rd].inuse = 1; + + if (first_rd == -1) first_rd = rd; + if (last_rd != -1) { + dbri->dma->desc[last_rd].nda = + (int) & dbri->dma_dvma->desc[rd]; + dbri->descs[last_rd].next = rd; + } + last_rd = rd; + + bus_buffer += mylen; + len -= mylen; } - if (rd == DBRI_NO_DESCS) { - printk("DBRI xmit_on_pipe: No descriptors available\n"); + + if (last_rd == -1 || first_rd == -1) { return; } - dbri->dma->desc[rd].word1 = 0; - dbri->dma->desc[rd].ba = mmu_get_scsi_one(buffer, len, - dbri->sdev->my_bus); - dbri->dma->desc[rd].nda = 0; - dbri->dma->desc[rd].word4 = DBRI_RD_B | DBRI_RD_BCNT(len); - - dbri->descs[rd].buffer = buffer; - dbri->descs[rd].len = len; - dbri->descs[rd].input_callback = callback; - dbri->descs[rd].input_callback_arg = callback_arg; + for (rd=first_rd; rd != -1; rd = dbri->descs[rd].next) { + dprintk(D_DESC, ("DBRI RD %d: %08x %08x %08x %08x\n", + rd, + dbri->dma->desc[rd].word1, + dbri->dma->desc[rd].ba, + dbri->dma->desc[rd].nda, + dbri->dma->desc[rd].word4)); + } + + dbri->descs[last_rd].buffer = buffer; + dbri->descs[last_rd].len = len; + dbri->descs[last_rd].input_callback = callback; + dbri->descs[last_rd].input_callback_arg = callback_arg; - dbri->pipes[pipe].desc = rd; + dbri->pipes[pipe].desc = first_rd; cmd = dbri_cmdlock(dbri); - *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P); - *(cmd++) = (int) & dbri->dma_dvma->desc[rd]; + *(cmd++) = DBRI_CMD(D_SDP, 0, dbri->pipes[pipe].sdp | D_SDP_P | D_SDP_C); + *(cmd++) = (int) & dbri->dma_dvma->desc[first_rd]; dbri_cmdsend(dbri, cmd); } @@ -846,8 +1101,123 @@ /* **************************************************************************** +************************** DBRI - CHI interface **************************** +**************************************************************************** + +The CHI is a four-wire (clock, frame sync, data in, data out) time-division +multiplexed serial interface which the DBRI can operate in either master +(give clock/frame sync) or slave (take clock/frame sync) mode. + +*/ + +enum master_or_slave { CHImaster, CHIslave }; + +static void reset_chi(struct dbri *dbri, enum master_or_slave master_or_slave, + int bits_per_frame) +{ + volatile int *cmd; + int val; + static int chi_initialized=0; + + if (!chi_initialized) { + + cmd = dbri_cmdlock(dbri); + + /* Set CHI Anchor: Pipe 16 */ + + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(16) | D_PIPE(16); + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16); + *(cmd++) = 0; + + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(16) | D_PIPE(16); + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = 0; + *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16); + + dbri->pipes[16].sdp = 1; + dbri->pipes[16].nextpipe = 16; + dbri->chi_in_pipe = 16; + dbri->chi_out_pipe = 16; + +#if 0 + chi_initialized ++; +#endif + } else { + int pipe; + + for (pipe = dbri->chi_in_pipe; + pipe != 16; + pipe = dbri->pipes[pipe].nextpipe) { + unlink_time_slot(dbri, pipe, PIPEinput, + 16, dbri->pipes[pipe].nextpipe); + } + for (pipe = dbri->chi_out_pipe; + pipe != 16; + pipe = dbri->pipes[pipe].nextpipe) { + unlink_time_slot(dbri, pipe, PIPEoutput, + 16, dbri->pipes[pipe].nextpipe); + } + + dbri->chi_in_pipe = 16; + dbri->chi_out_pipe = 16; + + cmd = dbri_cmdlock(dbri); + + } + + if (master_or_slave == CHIslave) { + /* Setup DBRI for CHI Slave - receive clock, frame sync (FS) + * + * CHICM = 0 (slave mode, 8 kHz frame rate) + * IR = give immediate CHI status interrupt + * EN = give CHI status interrupt upon change + */ + *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0)); + } else { + /* Setup DBRI for CHI Master - generate clock, FS + * + * BPF = bits per 8 kHz frame + * 12.288 MHz / CHICM_divisor = clock rate + * FD = 1 - drive CHIFS on rising edge of CHICK + */ + + int clockrate = bits_per_frame * 8; + int divisor = 12288 / clockrate; + + if (divisor > 255 || divisor * clockrate != 12288) { + printk("DBRI: illegal bits_per_frame in setup_chi\n"); + } + + *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD + | D_CHI_BPF(bits_per_frame)); + } + + dbri->chi_bpf = bits_per_frame; + + /* CHI Data Mode + * + * RCE = 0 - receive on falling edge of CHICK + * XCE = 1 - transmit on rising edge of CHICK + * XEN = 1 - enable transmitter + * REN = 1 - enable receiver + */ + + *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); + + *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE|D_CDM_XEN|D_CDM_REN); + + dbri_cmdsend(dbri, cmd); +} + +/* +**************************************************************************** *********************** CS4215 audio codec management ********************** **************************************************************************** + +In the standard SPARC audio configuration, the CS4215 codec is attached +to the DBRI via the CHI interface and few of the DBRI's PIO pins. + */ @@ -872,21 +1242,83 @@ * 2: Serial enable, CHI master, 128 bits per frame, clock 1 * 3: Tests disabled */ - mm->ctrl[0] = CS4215_RSRVD_1; + mm->ctrl[0] = CS4215_RSRVD_1 | CS4215_MLB; mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval; mm->ctrl[2] = CS4215_XCLK | CS4215_BSEL_128 | CS4215_FREQ[0].xtal; mm->ctrl[3] = 0; } +static void mmcodec_setup_pipes(struct dbri *dbri) +{ + /* + * Data mode: + * Pipe 4: Send timeslots 1-4 (audio data) + * Pipe 20: Send timeslots 5-8 (part of ctrl data) + * Pipe 6: Receive timeslots 1-4 (audio data) + * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via + * interrupt, and the rest of the data (slot 5 and 8) is + * not relevant for us (only for doublechecking). + * + * Control mode: + * Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly) + * Pipe 18: Receive timeslot 1 (clb). + * Pipe 19: Receive timeslot 7 (version). + */ + + setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB); + setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); + setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB); + setup_pipe(dbri, 21, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); + + setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); + setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); + setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); + + dbri->mm.status = 0; + + recv_fixed(dbri, 18, & dbri->mm.status); + recv_fixed(dbri, 19, & dbri->mm.version); +} + +static void mmcodec_setgain(struct dbri *dbri, int muted) +{ + if (muted || dbri->perchip_info.output_muted) { + dbri->mm.data[0] = 63; + dbri->mm.data[1] = 63; + } else { + int left_gain = (dbri->perchip_info.play.gain / 4) % 64; + int right_gain = (dbri->perchip_info.play.gain / 4) % 64; + + if (dbri->perchip_info.play.balance < AUDIO_MID_BALANCE) { + right_gain *= dbri->perchip_info.play.balance; + right_gain /= AUDIO_MID_BALANCE; + } else { + left_gain *= AUDIO_RIGHT_BALANCE + - dbri->perchip_info.play.balance; + left_gain /= AUDIO_MID_BALANCE; + } + + dprintk(D_MM, ("DBRI: Setting codec gain left: %d right: %d\n", + left_gain, right_gain)); + + dbri->mm.data[0] = CS4215_LE | CS4215_HE | (63 - left_gain); + dbri->mm.data[1] = CS4215_SE | (63 - right_gain); + } + + xmit_fixed(dbri, 20, *(int *)dbri->mm.data); +} + static void mmcodec_init_data(struct dbri *dbri) { + int data_width; + /* * Data mode: * Pipe 4: Send timeslots 1-4 (audio data) - * Pipe 17: Send timeslots 5-8 (part of ctrl data) + * Pipe 20: Send timeslots 5-8 (part of ctrl data) * Pipe 6: Receive timeslots 1-4 (audio data) - * Pipe 20: Receive timeslots 6-7. We can only receive 20 bits via + * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via * interrupt, and the rest of the data (slot 5 and 8) is * not relevant for us (only for doublechecking). * @@ -896,35 +1328,55 @@ */ + dbri->regs->reg0 &= ~D_C; /* Disable CHI */ + /* Switch CS4215 to data mode - set PIO3 to 1 */ dbri->regs->reg2 = D_ENPIO | D_PIO1 | D_PIO3 | (dbri->mm.onboard ? D_PIO0 : D_PIO2); - reset_chi(dbri, CHIslave, 0); + reset_chi(dbri, CHIslave, 128); + + /* Note: this next doesn't work for 8-bit stereo, because the two + * channels would be on timeslots 1 and 3, with 2 and 4 idle. + * (See CS4215 datasheet Fig 15) + * + * DBRI non-contiguous mode would be required to make this work. + */ - setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB); - setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); - setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB); - setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); + data_width = dbri->perchip_info.play.channels + * dbri->perchip_info.play.precision; - /* Pipes 4 and 6 - Single time slot, 8 bit mono */ + link_time_slot(dbri, 20, PIPEoutput, 16, + 32, dbri->mm.offset + 32); + link_time_slot(dbri, 4, PIPEoutput, 16, + data_width, dbri->mm.offset); + link_time_slot(dbri, 6, PIPEinput, 16, + data_width, dbri->mm.offset); + link_time_slot(dbri, 21, PIPEinput, 16, + 16, dbri->mm.offset + 40); - link_time_slot(dbri, 17, PIPEoutput, 16, 32, 32); - link_time_slot(dbri, 4, PIPEoutput, 17, 8, 128); - link_time_slot(dbri, 6, PIPEinput, 16, 8, 0); - link_time_slot(dbri, 20, PIPEinput, 6, 16, 40); + mmcodec_setgain(dbri, 0); - xmit_fixed(dbri, 17, *(int *)dbri->mm.data); + dbri->regs->reg0 |= D_C; /* Enable CHI */ } /* * Send the control information (i.e. audio format) */ -static void mmcodec_setctrl(struct dbri *dbri) +static int mmcodec_setctrl(struct dbri *dbri) { int i, val; + /* XXX - let the CPU do something useful during these delays */ + + /* Temporarily mute outputs, and wait 1/8000 sec (125 us) + * to make sure this takes. This avoids clicking noises. + */ + + mmcodec_setgain(dbri, 1); + udelay(125); + /* * Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait * 12 cycles <= 12/(5512.5*64) sec = 34.01 usec @@ -952,6 +1404,8 @@ * frame sync signal by eight clock cycles. Anybody know why? */ + dbri->regs->reg0 &= ~D_C; /* Disable CHI */ + reset_chi(dbri, CHImaster, 128); /* @@ -961,27 +1415,26 @@ * Pipe 19: Receive timeslot 7 (version). */ - setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); - setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB); - setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_CHANGE | D_SDP_MSB); - - link_time_slot(dbri, 17, PIPEoutput, 16, 32, 128); - link_time_slot(dbri, 18, PIPEinput, 16, 8, 0); - link_time_slot(dbri, 19, PIPEinput, 18, 8, 48); - - recv_fixed(dbri, 18, & dbri->mm.status); - recv_fixed(dbri, 19, & dbri->mm.version); + link_time_slot(dbri, 17, PIPEoutput, 16, + 32, dbri->mm.offset); + link_time_slot(dbri, 18, PIPEinput, 16, + 8, dbri->mm.offset); + link_time_slot(dbri, 19, PIPEinput, 16, + 8, dbri->mm.offset + 48); /* Wait for the chip to echo back CLB (Control Latch Bit) as zero */ dbri->mm.ctrl[0] &= ~CS4215_CLB; xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl); - i = 1000000; - while ((! dbri->mm.status & CS4215_CLB) && i--); + dbri->regs->reg0 |= D_C; /* Enable CHI */ + + i = 10; + while (((dbri->mm.status & 0xe4) != 0x20) && --i) udelay(125); if (i == 0) { - printk("CS4215 didn't respond to CLB\n"); - return; + dprintk(D_MM, ("DBRI: CS4215 didn't respond to CLB (0x%02x)\n", + dbri->mm.status)); + return -1; } /* Terminate CS4215 control mode - data sheet says @@ -993,6 +1446,10 @@ /* Two frames of control info @ 8kHz frame rate = 250 us delay */ udelay(250); + + mmcodec_setgain(dbri, 0); + + return 0; } static int mmcodec_init(struct sparcaudio_driver *drv) @@ -1024,16 +1481,24 @@ } - /* Now talk to our baby */ - dbri->regs->reg0 |= D_C; /* Enable CHI */ + mmcodec_setup_pipes(dbri); mmcodec_default(&dbri->mm); dbri->mm.version = 0xff; - mmcodec_setctrl(dbri); - if(dbri->mm.version == 0xff) + dbri->mm.offset = dbri->mm.onboard ? 0 : 8; + if (mmcodec_setctrl(dbri) == -1 || dbri->mm.version == 0xff) { + dprintk(D_MM, ("DBRI: CS4215 failed probe at offset %d\n", + dbri->mm.offset)); return -EIO; + } + + dprintk(D_MM, ("DBRI: Found CS4215 at offset %d\n", dbri->mm.offset)); + dbri->perchip_info.play.channels = 1; + dbri->perchip_info.play.precision = 8; + dbri->perchip_info.play.gain = 255; + dbri->perchip_info.play.balance = AUDIO_MID_BALANCE; mmcodec_init_data(dbri); return 0; @@ -1044,37 +1509,25 @@ **************************************************************************** ******************** Interface with sparcaudio midlevel ******************** **************************************************************************** -*/ +The sparcaudio midlevel is contained in the file audio.c. It interfaces +to the user process and performs buffering, intercepts SunOS-style ioctl's, +etc. It interfaces to a abstract audio device via a struct sparcaudio_driver. +This code presents such an interface for the DBRI with an attached CS4215. +All our routines are defined, and then comes our struct sparcaudio_driver. -static int dbri_open(struct inode * inode, struct file * file, - struct sparcaudio_driver *drv) -{ - struct dbri *dbri = (struct dbri *)drv->private; - - MOD_INC_USE_COUNT; +*/ - return 0; -} +/******************* sparcaudio midlevel - audio output *******************/ -static void dbri_release(struct inode * inode, struct file * file, - struct sparcaudio_driver *drv) -{ - MOD_DEC_USE_COUNT; -} - -static int dbri_ioctl(struct inode * inode, struct file * file, - unsigned int x, unsigned long y, - struct sparcaudio_driver *drv) -{ - return 0; -} static void dbri_audio_output_callback(void * callback_arg, int status) { struct sparcaudio_driver *drv = callback_arg; - sparcaudio_output_done(drv, 1); + if (status != -1) { + sparcaudio_output_done(drv, 1); + } } static void dbri_start_output(struct sparcaudio_driver *drv, @@ -1082,8 +1535,31 @@ { struct dbri *dbri = (struct dbri *)drv->private; + dprintk(D_USR, ("DBRI: start audio output buf=%lx/%ld\n", + (unsigned long) buffer, count)); + /* Pipe 4 is audio transmit */ - xmit_on_pipe(dbri, 4, buffer, count, &dbri_audio_output_callback, drv); + + xmit_on_pipe(dbri, 4, buffer, count, + &dbri_audio_output_callback, drv); + +#if 0 + /* Notify midlevel that we're a DMA-capable driver that + * can accept another buffer immediately. We should probably + * check that we've got enough resources (i.e, descriptors) + * available before doing this, but the default midlevel + * settings only buffer 64KB, which we can handle with 16 + * of our DBRI_NO_DESCS (64) descriptors. + * + * This code is #ifdef'ed out because it's caused me more + * problems than it solved. It'd be nice to provide the + * DBRI with a chain of buffers, but the midlevel code is + * so tricky that I really don't want to deal with it. + */ + + sparcaudio_output_done(drv, 2); +#endif + } static void dbri_stop_output(struct sparcaudio_driver *drv) @@ -1093,28 +1569,55 @@ reset_pipe(dbri, 4); } +/******************* sparcaudio midlevel - audio input ********************/ + +static void dbri_audio_input_callback(void * callback_arg, int status, + unsigned int len) +{ + struct sparcaudio_driver * drv = + (struct sparcaudio_driver *) callback_arg; + + if (status != -1) { + sparcaudio_input_done(drv, 3); + } +} + static void dbri_start_input(struct sparcaudio_driver *drv, __u8 * buffer, unsigned long len) { + struct dbri *dbri = (struct dbri *)drv->private; + + /* Pipe 6 is audio receive */ + recv_on_pipe(dbri, 6, buffer, len, + &dbri_audio_input_callback, (void *)drv); + dprintk(D_USR, ("DBRI: start audio input buf=%lx/%ld\n", + (unsigned long) buffer, len)); } static void dbri_stop_input(struct sparcaudio_driver *drv) { -} + struct dbri *dbri = (struct dbri *)drv->private; -static void dbri_audio_getdev(struct sparcaudio_driver *drv, - audio_device_t *devptr) -{ + reset_pipe(dbri, 6); } +/******************* sparcaudio midlevel - volume & balance ***************/ + static int dbri_set_output_volume(struct sparcaudio_driver *drv, int volume) { + struct dbri *dbri = (struct dbri *)drv->private; + + dbri->perchip_info.play.gain = volume; + mmcodec_setgain(dbri, 0); + return 0; } static int dbri_get_output_volume(struct sparcaudio_driver *drv) { - return 0; + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.play.gain; } static int dbri_set_input_volume(struct sparcaudio_driver *drv, int volume) @@ -1139,12 +1642,19 @@ static int dbri_set_output_balance(struct sparcaudio_driver *drv, int balance) { + struct dbri *dbri = (struct dbri *)drv->private; + + dbri->perchip_info.play.balance = balance; + mmcodec_setgain(dbri, 0); + return 0; } static int dbri_get_output_balance(struct sparcaudio_driver *drv) { - return 0; + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.play.balance; } static int dbri_set_input_balance(struct sparcaudio_driver *drv, int balance) @@ -1157,107 +1667,205 @@ return 0; } +static int dbri_set_output_muted(struct sparcaudio_driver *drv, int mute) +{ + struct dbri *dbri = (struct dbri *)drv->private; + + dbri->perchip_info.output_muted = mute; + + return 0; +} + +static int dbri_get_output_muted(struct sparcaudio_driver *drv) +{ + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.output_muted; +} + +/******************* sparcaudio midlevel - encoding format ****************/ + static int dbri_set_output_channels(struct sparcaudio_driver *drv, int chan) { + struct dbri *dbri = (struct dbri *)drv->private; + + switch (chan) { + case 0: + return 0; + case 1: + dbri->mm.ctrl[1] &= ~CS4215_DFR_STEREO; + break; + case 2: + dbri->mm.ctrl[1] |= CS4215_DFR_STEREO; + break; + default: + return -1; + } + + dbri->perchip_info.play.channels = chan; + mmcodec_setctrl(dbri); + mmcodec_init_data(dbri); return 0; } static int dbri_get_output_channels(struct sparcaudio_driver *drv) { - return 0; + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.play.channels; } static int dbri_set_input_channels(struct sparcaudio_driver *drv, int chan) { - return 0; + return dbri_set_output_channels(drv, chan); } static int dbri_get_input_channels(struct sparcaudio_driver *drv) { - return 0; + return dbri_get_output_channels(drv); } static int dbri_set_output_precision(struct sparcaudio_driver *drv, int prec) { - return 8; + return 0; } static int dbri_get_output_precision(struct sparcaudio_driver *drv) { - return 8; + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.play.precision; } static int dbri_set_input_precision(struct sparcaudio_driver *drv, int prec) { - return 8; + return 0; } static int dbri_get_input_precision(struct sparcaudio_driver *drv) { - return 8; -} + struct dbri *dbri = (struct dbri *)drv->private; -static int dbri_set_output_port(struct sparcaudio_driver *drv, int port) -{ - return 0; + return dbri->perchip_info.play.precision; } -static int dbri_get_output_port(struct sparcaudio_driver *drv) +static int dbri_set_output_encoding(struct sparcaudio_driver *drv, int enc) { + struct dbri *dbri = (struct dbri *)drv->private; + + /* For ULAW and ALAW, audio.c enforces precision = 8, + * for LINEAR, precision must be 16 + */ + + switch (enc) { + case AUDIO_ENCODING_NONE: + return 0; + case AUDIO_ENCODING_ULAW: + dbri->mm.ctrl[1] &= ~3; + dbri->mm.ctrl[1] |= CS4215_DFR_ULAW; + dbri->perchip_info.play.encoding = enc; + dbri->perchip_info.play.precision = 8; + break; + case AUDIO_ENCODING_ALAW: + dbri->mm.ctrl[1] &= ~3; + dbri->mm.ctrl[1] |= CS4215_DFR_ALAW; + dbri->perchip_info.play.encoding = enc; + dbri->perchip_info.play.precision = 8; + break; + case AUDIO_ENCODING_LINEAR: + dbri->mm.ctrl[1] &= ~3; + dbri->mm.ctrl[1] |= CS4215_DFR_LINEAR16; + dbri->perchip_info.play.encoding = enc; + dbri->perchip_info.play.precision = 16; + break; + default: + return -1; + } + mmcodec_setctrl(dbri); + mmcodec_init_data(dbri); return 0; } -static int dbri_set_input_port(struct sparcaudio_driver *drv, int port) +static int dbri_get_output_encoding(struct sparcaudio_driver *drv) { - return 0; + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.play.encoding; } -static int dbri_get_input_port(struct sparcaudio_driver *drv) +static int dbri_set_input_encoding(struct sparcaudio_driver *drv, int enc) { - return 0; + return dbri_set_output_encoding(drv, enc); } -static int dbri_set_output_encoding(struct sparcaudio_driver *drv, int enc) +static int dbri_get_input_encoding(struct sparcaudio_driver *drv) { - return 0; + return dbri_get_output_encoding(drv); } -static int dbri_get_output_encoding(struct sparcaudio_driver *drv) +static int dbri_set_output_rate(struct sparcaudio_driver *drv, int rate) { + struct dbri *dbri = (struct dbri *)drv->private; + int i; + + if (rate == 0) { + return 0; + } + + for (i=0; CS4215_FREQ[i].freq; i++) { + if (CS4215_FREQ[i].freq == rate) break; + } + if (CS4215_FREQ[i].freq == 0) { + return -1; + } + + dbri->mm.ctrl[1] &= ~ 0x38; + dbri->mm.ctrl[1] |= CS4215_FREQ[i].csval; + dbri->mm.ctrl[2] &= ~ 0x70; + dbri->mm.ctrl[2] |= CS4215_FREQ[i].xtal; + + dbri->perchip_info.play.sample_rate = rate; + + mmcodec_setctrl(dbri); + mmcodec_init_data(dbri); return 0; } -static int dbri_set_input_encoding(struct sparcaudio_driver *drv, int enc) +static int dbri_get_output_rate(struct sparcaudio_driver *drv) { - return 0; + struct dbri *dbri = (struct dbri *)drv->private; + + return dbri->perchip_info.play.sample_rate; } -static int dbri_get_input_encoding(struct sparcaudio_driver *drv) +static int dbri_set_input_rate(struct sparcaudio_driver *drv, int rate) { - return 0; + return dbri_set_output_rate(drv, rate); } -static int dbri_set_output_rate(struct sparcaudio_driver *drv, int rate) +static int dbri_get_input_rate(struct sparcaudio_driver *drv) { - return 0; + return dbri_get_output_rate(drv); } -static int dbri_get_output_rate(struct sparcaudio_driver *drv) +/******************* sparcaudio midlevel - ports ***********************/ + +static int dbri_set_output_port(struct sparcaudio_driver *drv, int port) { return 0; } -static int dbri_set_input_rate(struct sparcaudio_driver *drv, int rate) +static int dbri_get_output_port(struct sparcaudio_driver *drv) { return 0; } -static int dbri_get_input_rate(struct sparcaudio_driver *drv) +static int dbri_set_input_port(struct sparcaudio_driver *drv, int port) { return 0; } -static int dbri_sunaudio_getdev_sunos(struct sparcaudio_driver *drv) +static int dbri_get_input_port(struct sparcaudio_driver *drv) { return 0; } @@ -1272,17 +1880,66 @@ return 0; } -static int dbri_set_output_muted(struct sparcaudio_driver *drv, int mute) +/******************* sparcaudio midlevel - driver ID ********************/ + +static void dbri_audio_getdev(struct sparcaudio_driver *drv, + audio_device_t *audinfo) { - return 0; + struct dbri *dbri = (struct dbri *)drv->private; + + strncpy(audinfo->name, "SUNW,DBRI", sizeof(audinfo->name) - 1); + + audinfo->version[0] = dbri->dbri_version; + audinfo->version[1] = '\0'; + + strncpy(audinfo->config, "onboard1", sizeof(audinfo->config) - 1); } -static int dbri_get_output_muted(struct sparcaudio_driver *drv) +static int dbri_sunaudio_getdev_sunos(struct sparcaudio_driver *drv) { - return 0; + return AUDIO_DEV_CODEC; } +/******************* sparcaudio midlevel - open & close ******************/ +static int dbri_open(struct inode * inode, struct file * file, + struct sparcaudio_driver *drv) +{ + MOD_INC_USE_COUNT; + + /* SunOS 5.5.1 audio(7I) man page says: + * "Upon the initial open() of the audio device, the driver + * will reset the data format of the device to the default + * state of 8-bit, 8KHz, mono u-law data." + * + * I've also taken the liberty of setting half gain and + * mid balance, to put the codec in a known state. + */ + + dbri_set_output_channels(drv, 1); + dbri_set_output_encoding(drv, AUDIO_ENCODING_ULAW); + dbri_set_output_rate(drv, 8000); + + dbri_set_output_balance(drv, AUDIO_MID_BALANCE); + dbri_set_output_volume(drv, AUDIO_MAX_GAIN/2); + + return 0; +} + +static void dbri_release(struct inode * inode, struct file * file, + struct sparcaudio_driver *drv) +{ + MOD_DEC_USE_COUNT; +} + +static int dbri_ioctl(struct inode * inode, struct file * file, + unsigned int x, unsigned long y, + struct sparcaudio_driver *drv) +{ + return -EINVAL; +} + +/*********** sparcaudio midlevel - struct sparcaudio_driver ************/ static struct sparcaudio_operations dbri_ops = { dbri_open, @@ -1357,8 +2014,8 @@ setup_pipe(dbri,11, D_SDP_HDLC | D_SDP_TO_SER | D_SDP_LSB); link_time_slot(dbri, 0, PIPEinput, 0, 2, 17); - link_time_slot(dbri, 8, PIPEinput, 8, 8, 0); - link_time_slot(dbri, 9, PIPEinput, 9, 8, 8); + link_time_slot(dbri, 8, PIPEinput, 0, 8, 0); + link_time_slot(dbri, 9, PIPEinput, 8, 8, 8); link_time_slot(dbri, 1, PIPEoutput, 1, 2, 17); link_time_slot(dbri, 10, PIPEoutput, 1, 8, 0); @@ -1375,6 +2032,8 @@ dbri = (struct dbri *) drivers[dev].private; + tprintk(("dbri_get_irqnum()\n")); + /* On the sparc, the cpu's irq number is only part of the "irq" */ return (dbri->irq & NR_IRQS); } @@ -1389,6 +2048,8 @@ dbri = (struct dbri *) drivers[dev].private; + tprintk(("dbri_get_liu_state() returns %d\n", dbri->liu_state)); + return dbri->liu_state; } @@ -1404,12 +2065,14 @@ dbri = (struct dbri *) drivers[dev].private; + tprintk(("dbri_liu_init()\n")); + /* Set callback for LIU state change */ - dbri->liu_callback = callback; + dbri->liu_callback = callback; dbri->liu_callback_arg = callback_arg; - dbri_isdn_init(dbri); - dbri_liu_activate(dev, 0); + dbri_isdn_init(dbri); + dbri_liu_activate(dev, 0); } void dbri_liu_activate(int dev, int priority) @@ -1424,16 +2087,24 @@ dbri = (struct dbri *) drivers[dev].private; - cmd = dbri_cmdlock(dbri); + tprintk(("dbri_liu_activate()\n")); - /* Turn on the ISDN TE interface and request activation */ - val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT; - *(cmd++) = DBRI_CMD(D_TE, 0, val); + if (dbri->liu_state <= 3) { - dbri_cmdsend(dbri, cmd); + cmd = dbri_cmdlock(dbri); + + /* Turn on the ISDN TE interface and request activation */ + val = D_NT_IRM_IMM | D_NT_IRM_EN | D_NT_ACT; +#ifdef LOOPBACK_D + val |= D_NT_LLB(4); +#endif + *(cmd++) = DBRI_CMD(D_TE, 0, val); - /* Activate the interface */ - dbri->regs->reg0 |= D_T; + dbri_cmdsend(dbri, cmd); + + /* Activate the interface */ + dbri->regs->reg0 |= D_T; + } } void dbri_liu_deactivate(int dev) @@ -1446,8 +2117,14 @@ dbri = (struct dbri *) drivers[dev].private; + tprintk(("dbri_liu_deactivate()\n")); + +#if 0 /* Turn off the ISDN TE interface */ dbri->regs->reg0 &= ~D_T; + + dbri->liu_state = 0; +#endif } void dbri_dxmit(int dev, __u8 *buffer, unsigned int count, @@ -1613,6 +2290,11 @@ "DBRI DMA Cmd Block", &dma_dvma); dbri->dma_dvma = (struct dbri_dma *) dma_dvma; + memset((void *) dbri->dma, 0, sizeof(struct dbri_dma)); + + dprintk(D_GEN, ("DBRI: DMA Cmd Block 0x%08x (0x%08x)\n", + (int)dbri->dma, (int)dbri->dma_dvma)); + dbri->dbri_version = sdev->prom_name[9]; dbri->sdev = sdev; @@ -1625,6 +2307,8 @@ "DBRI Registers", sdev->reg_addrs[0].which_io, 0); if (!dbri->regs) { printk(KERN_ERR "DBRI: could not allocate registers\n"); + release_region((unsigned long) dbri->dma, + sizeof(struct dbri_dma)); kfree(drv->private); return -EIO; } @@ -1637,6 +2321,8 @@ if (err) { printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq); sparc_free_io(dbri->regs, dbri->regs_size); + release_region((unsigned long) dbri->dma, + sizeof(struct dbri_dma)); kfree(drv->private); return err; } diff -u --recursive --new-file v2.3.6/linux/drivers/sbus/audio/dbri.h linux/drivers/sbus/audio/dbri.h --- v2.3.6/linux/drivers/sbus/audio/dbri.h Wed May 12 08:41:15 1999 +++ linux/drivers/sbus/audio/dbri.h Thu Jun 17 01:08:50 1999 @@ -46,13 +46,19 @@ struct dbri_mem desc[DBRI_NO_DESCS]; /* Xmit/receive descriptors */ }; +enum in_or_out { PIPEinput, PIPEoutput }; + +enum direction { in, out }; + struct dbri_pipe { u32 sdp; /* SDP command word */ + enum direction direction; int nextpipe; /* Next pipe in linked list */ + int prevpipe; int cycle; /* Offset of timeslot (bits) */ int length; /* Length of timeslot (bits) */ int desc; /* Index of active descriptor*/ - __u32 *recv_fixed_ptr; /* Ptr to receive fixed data */ + volatile __u32 *recv_fixed_ptr; /* Ptr to receive fixed data */ }; struct dbri_desc { @@ -78,10 +84,15 @@ struct dbri_regs *regs; /* dbri HW regs */ int dbri_version; /* 'e' and up is OK */ int dbri_irqp; /* intr queue pointer */ + int wait_seen; struct dbri_pipe pipes[32]; /* DBRI's 32 data pipes */ struct dbri_desc descs[DBRI_NO_DESCS]; + int chi_in_pipe; + int chi_out_pipe; + int chi_bpf; + struct cs4215 mm; /* mmcodec special info */ #if 0 @@ -190,7 +201,7 @@ /* Time Slot defines */ #define D_TS_LEN(v) ((v)<<24) /* Number of bits in this time slot */ #define D_TS_CYCLE(v) ((v)<<14) /* Bit Count at start of TS */ -#define D_TS_DI(v) (1<<13) /* Data Invert */ +#define D_TS_DI (1<<13) /* Data Invert */ #define D_TS_1CHANNEL (0<<10) /* Single Channel / Normal mode */ #define D_TS_MONITOR (2<<10) /* Monitor pipe */ #define D_TS_NONCONTIG (3<<10) /* Non contiguous mode */ @@ -218,8 +229,8 @@ #define D_NT_IFA (1<<10) /* Inhibit Final Activation */ #define D_NT_ACT (1<<9) /* Activate Interface */ #define D_NT_MFE (1<<8) /* Multiframe Enable */ -#define D_NT_RLB(v) (1<<5) /* Remote Loopback */ -#define D_NT_LLB(v) (1<<2) /* Local Loopback */ +#define D_NT_RLB(v) ((v)<<5) /* Remote Loopback */ +#define D_NT_LLB(v) ((v)<<2) /* Local Loopback */ #define D_NT_FACT (1<<1) /* Force Activation */ #define D_NT_ABV (1<<0) /* Activate Bipolar Violation */ diff -u --recursive --new-file v2.3.6/linux/drivers/sbus/char/su.c linux/drivers/sbus/char/su.c --- v2.3.6/linux/drivers/sbus/char/su.c Wed Jun 9 14:44:25 1999 +++ linux/drivers/sbus/char/su.c Thu Jun 17 01:08:50 1999 @@ -1,4 +1,4 @@ -/* $Id: su.c,v 1.20 1999/06/03 15:02:40 davem Exp $ +/* $Id: su.c,v 1.21 1999/06/11 10:23:42 davem Exp $ * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -1133,9 +1133,9 @@ struct su_struct *info = su_table; int lsr; - if (!info->port_type != SU_PORT_KBD) + if (info->port_type != SU_PORT_KBD) ++info; - if (!info->port_type != SU_PORT_KBD) + if (info->port_type != SU_PORT_KBD) return; do { @@ -1151,9 +1151,9 @@ { struct su_struct *info = su_table; - if (!info->port_type != SU_PORT_MS) + if (info->port_type != SU_PORT_MS) ++info; - if (!info->port_type != SU_PORT_MS) + if (info->port_type != SU_PORT_MS) return; info->cflag &= ~(CBAUDEX | CBAUD); @@ -2215,7 +2215,7 @@ */ __initfunc(static __inline__ void show_su_version(void)) { - char *revision = "$Revision: 1.20 $"; + char *revision = "$Revision: 1.21 $"; char *version, *p; version = strchr(revision, ' '); diff -u --recursive --new-file v2.3.6/linux/drivers/scsi/in2000.h linux/drivers/scsi/in2000.h --- v2.3.6/linux/drivers/scsi/in2000.h Tue Jun 8 23:04:31 1999 +++ linux/drivers/scsi/in2000.h Sun Jun 20 17:47:14 1999 @@ -67,7 +67,7 @@ orl %%ecx, %%ecx \n \ jz 1f \n \ rep \n \ - insw %%dx \n \ + insw (%%dx),%%es:(%%edi) \n \ 1: " \ : "=D" (sp) /* output */ \ : "d" (f), "D" (sp), "c" (i) /* input */ \ @@ -79,7 +79,7 @@ orl %%ecx, %%ecx \n \ jz 1f \n \ rep \n \ - outsw %%dx \n \ + outsw %%ds:(%%esi),(%%dx) \n \ 1: " \ : "=S" (sp) /* output */ \ : "d" (f), "S" (sp), "c" (i) /* input */ \ diff -u --recursive --new-file v2.3.6/linux/drivers/scsi/scsi_error.c linux/drivers/scsi/scsi_error.c --- v2.3.6/linux/drivers/scsi/scsi_error.c Tue May 11 14:37:40 1999 +++ linux/drivers/scsi/scsi_error.c Wed Jun 16 19:26:27 1999 @@ -1926,6 +1926,7 @@ int rtn; DECLARE_MUTEX_LOCKED(sem); unsigned long flags; + struct fs_struct *fs; lock_kernel(); @@ -1936,16 +1937,18 @@ */ exit_mm(current); - current->session = 1; current->pgrp = 1; - /* - * FIXME(eric) this is still a child process of the one that did the insmod. - * This needs to be attached to task[0] instead. - */ + + /* Become as one with the init task */ + + exit_fs(current); /* current->fs->count--; */ + fs = init_task.fs; + current->fs = fs; + atomic_inc(&fs->count); siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); - current->fs->umask = 0; + /* * Set the name of this process. diff -u --recursive --new-file v2.3.6/linux/drivers/sound/es1370.c linux/drivers/sound/es1370.c --- v2.3.6/linux/drivers/sound/es1370.c Sat May 22 13:05:35 1999 +++ linux/drivers/sound/es1370.c Fri Jun 18 10:45:58 1999 @@ -98,6 +98,8 @@ * (micz). From Kim.Berts@fisub.mail.abb.com * 11.05.99 0.22 Implemented the IMIX call to mute recording monitor. * Guenter Geiger + * 15.06.99 0.23 Fix bad allocation bug. + * Thanks to Deti Fliegl * * some important things missing in Ensoniq documentation: * @@ -531,8 +533,9 @@ db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; if (!db->rawbuf) { db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) - db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order); + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order))) + break; if (!db->rawbuf) return -ENOMEM; db->buforder = order; @@ -2317,7 +2320,7 @@ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1370: version v0.22 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1370: version v0.23 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) { if (pcidev->base_address[0] == 0 || diff -u --recursive --new-file v2.3.6/linux/drivers/sound/es1371.c linux/drivers/sound/es1371.c --- v2.3.6/linux/drivers/sound/es1371.c Wed May 12 13:27:37 1999 +++ linux/drivers/sound/es1371.c Fri Jun 18 10:45:58 1999 @@ -65,6 +65,8 @@ * reported by "Ivan N. Kokshaysky" * Note: joystick address handling might still be wrong on archs * other than i386 + * 15.06.99 0.12 Fix bad allocation bug. + * Thanks to Deti Fliegl * */ @@ -759,8 +761,9 @@ db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; if (!db->rawbuf) { db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) - db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order); + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order))) + break; if (!db->rawbuf) return -ENOMEM; db->buforder = order; @@ -2732,7 +2735,7 @@ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1371: version v0.11 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1371: version v0.12 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) { if (pcidev->base_address[0] == 0 || diff -u --recursive --new-file v2.3.6/linux/drivers/sound/sonicvibes.c linux/drivers/sound/sonicvibes.c --- v2.3.6/linux/drivers/sound/sonicvibes.c Wed May 12 13:27:37 1999 +++ linux/drivers/sound/sonicvibes.c Fri Jun 18 10:45:58 1999 @@ -68,6 +68,8 @@ * SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS; * Alpha fixes reported by Peter Jones * Note: dmaio hack might still be wrong on archs other than i386 + * 15.06.99 0.15 Fix bad allocation bug. + * Thanks to Deti Fliegl * */ @@ -699,8 +701,9 @@ db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; if (!db->rawbuf) { db->ready = db->mapped = 0; - for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--) - db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order); + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order))) + break; if (!db->rawbuf) return -ENOMEM; db->buforder = order; @@ -2321,7 +2324,7 @@ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "sv: version v0.14 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "sv: version v0.15 time " __TIME__ " " __DATE__ "\n"); #if 0 if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); diff -u --recursive --new-file v2.3.6/linux/drivers/sound/vidc.c linux/drivers/sound/vidc.c --- v2.3.6/linux/drivers/sound/vidc.c Wed Dec 16 12:52:01 1998 +++ linux/drivers/sound/vidc.c Thu Jun 17 01:11:35 1999 @@ -24,7 +24,7 @@ #define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) fillertype = TYPE(format, channels); -printk("filler type: %X\n", fillertype); + switch (fillertype) { default: @@ -61,10 +61,13 @@ sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype); conf_printf(name, hw_config); + memset(dma_buf, 0, sizeof(dma_buf)); for (i = 0; i < 2; i++) { dma_buf[i] = get_free_page(GFP_KERNEL); + if (!dma_buf[i]) + goto nomem; dma_pbuf[i] = virt_to_phys(dma_buf[i]); } @@ -78,9 +81,16 @@ printk(KERN_ERR "VIDCsound: can't allocate DMA interrupt\n"); return; } + // vidc_synth_init(hw_config); vidc_audio_init(hw_config); vidc_mixer_init(hw_config); + return; + +nomem: + for (i = 0; i < 2; i++) + free_page(dma_buf[i]); + printk(KERN_ERR "VIDCsound: can't allocate required buffers\n"); } int probe_vidc(struct address_info *hw_config) diff -u --recursive --new-file v2.3.6/linux/drivers/sound/vidc_audio.c linux/drivers/sound/vidc_audio.c --- v2.3.6/linux/drivers/sound/vidc_audio.c Wed Dec 16 12:52:01 1998 +++ linux/drivers/sound/vidc_audio.c Thu Jun 17 01:11:35 1999 @@ -9,6 +9,7 @@ #include #include #include +#include #include "sound_config.h" #include "vidc.h" @@ -42,7 +43,6 @@ static int vidc_audio_set_bits(int fmt) { -printk("setting format: %d\n", fmt); switch (fmt) { case AFMT_QUERY: @@ -219,10 +219,11 @@ if (!(adev->flags & DMA_ACTIVE)) { unsigned long flags; -printk("kicking output: %lX+%lX [%lX]\n", dma_start, dma_count, *(unsigned long *)dma_start); save_flags_cli(flags); + vidc_sound_dma_irq(0, NULL, NULL); outb(DMA_CR_E | 0x10, IOMD_SD0CR); + restore_flags(flags); } } diff -u --recursive --new-file v2.3.6/linux/drivers/sound/vidc_fill.S linux/drivers/sound/vidc_fill.S --- v2.3.6/linux/drivers/sound/vidc_fill.S Wed Dec 16 12:52:01 1998 +++ linux/drivers/sound/vidc_fill.S Thu Jun 17 01:11:35 1999 @@ -9,6 +9,7 @@ #include #include #include +#include .text diff -u --recursive --new-file v2.3.6/linux/drivers/sound/waveartist.c linux/drivers/sound/waveartist.c --- v2.3.6/linux/drivers/sound/waveartist.c Thu Jan 7 09:24:00 1999 +++ linux/drivers/sound/waveartist.c Thu Jun 17 01:11:35 1999 @@ -40,12 +40,20 @@ #include #include +#include #include #include "soundmodule.h" #include "sound_config.h" #include "waveartist.h" +#ifndef _ISA_DMA +#define _ISA_DMA(x) (x) +#endif +#ifndef _ISA_IRQ +#define _ISA_IRQ(x) (x) +#endif + #define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec #define MIXER_PRIVATE3_RESET 0x53570000 @@ -141,8 +149,269 @@ static int nr_waveartist_devs; static wavnc_info adev_info[MAX_AUDIO_DEV]; + +static int waveartist_mixer_set(wavnc_info *devc, int whichDev, unsigned int level); + +/* + * Corel Netwinder specifics... + */ static struct timer_list vnc_timer; +extern spinlock_t gpio_lock; + +static void +vnc_mute(wavnc_info *devc, int mute) +{ + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE); + spin_unlock_irqrestore(&gpio_lock, flags); + + devc->mute_state = mute; +} + +static int +vnc_volume_slider(wavnc_info *devc) +{ + static signed int old_slider_volume; + unsigned long flags; + signed int volume = 255; + + *CSR_TIMER1_LOAD = 0x00ffffff; + + save_flags(flags); + cli(); + + outb(0xFF, 0x201); + *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1; + + while (volume && (inb(0x201) & 0x01)) + volume--; + + *CSR_TIMER1_CNTL = 0; + + restore_flags(flags); + + volume = 0x00ffffff - *CSR_TIMER1_VALUE; + +#ifndef REVERSE + volume = 150 - (volume >> 5); +#else + volume = (volume >> 6) - 25; +#endif + + if (volume < 0) + volume = 0; + + if (volume > 100) + volume = 100; + + /* + * slider quite often reads +-8, so debounce this random noise + */ + if ((volume - old_slider_volume) > 7 || + (old_slider_volume - volume) > 7) { + old_slider_volume = volume; + + DEB(printk("Slider volume: %d.\n", old_slider_volume)); + } + + return old_slider_volume; +} + +static int +vnc_slider(wavnc_info *devc) +{ + signed int slider_volume; + unsigned int temp; + + /* + * read the "buttons" state. + * Bit 4 = handset present, + * Bit 5 = offhook + */ + // the state should be "querable" via a private IOCTL call + temp = inb(0x201) & 0x30; + + if (!devc->handset_mute_sw && + (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) { + devc->handset_state = temp; + devc->handset_mute_sw = 0; + + vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0); + } + + slider_volume = vnc_volume_slider(devc); + + /* + * If we're using software controlled volume, and + * the slider moves by more than 20%, then we + * switch back to slider controlled volume. + */ + if (devc->slider_vol > slider_volume) { + if (devc->slider_vol - slider_volume > 20) + devc->use_slider = 1; + } else { + if (slider_volume - devc->slider_vol > 20) + devc->use_slider = 1; + } + + /* + * use only left channel + */ + temp = levels[SOUND_MIXER_VOLUME] & 0xFF; + + if (slider_volume != temp && devc->use_slider) { + devc->slider_vol = slider_volume; + + waveartist_mixer_set(devc, SOUND_MIXER_VOLUME, + slider_volume | slider_volume << 8); + + return 1; + } + + return 0; +} + +static void +vnc_slider_tick(unsigned long data) +{ + int next_timeout; + + if (vnc_slider(adev_info + data)) + next_timeout = 5; // mixer reported change + else + next_timeout = VNC_TIMER_PERIOD; + + mod_timer(&vnc_timer, jiffies + next_timeout); +} + +static int +vnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc; + int val, temp; + + if (cmd == SOUND_MIXER_PRIVATE1) { + /* + * Use this call to override the automatic handset + * behaviour - ignore handset. + * bit 7 = total control over handset - do not + * to plug/unplug + * bit 4 = internal mic + * bit 0 = mute internal speaker + */ + if (get_user(val, (int *)arg)) + return -EFAULT; + + devc->handset_mute_sw = val; + + temp = val & VNC_INTERNAL_SPKR; + if (devc->mute_state != temp) + vnc_mute(devc, temp); + + devc->handset_state = val & VNC_INTERNAL_MIC; + waveartist_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC); + return 0; + } +#if 0 + if (cmd == SOUND_MIXER_PRIVATE2) { +#define VNC_SOUND_PAUSE 0x53 //to pause the DSP +#define VNC_SOUND_RESUME 0x57 //to unpause the DSP + int val; + + val = *(int *) arg; + + printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val); + + if (val == VNC_SOUND_PAUSE) { + wa_sendcmd(0x16); //PAUSE the ADC + } else if (val == VNC_SOUND_RESUME) { + wa_sendcmd(0x18); //RESUME the ADC + } else { + return -EINVAL; //invalid parameters... + } + return 0; + } + + if (cmd == SOUND_MIXER_PRIVATE3) { + long unsigned flags; + int mixer_reg[15]; //reg 14 is actually a command: read,write,reset + + int val; + int i; + + val = *(int *) arg; + + if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT)) + return (-EFAULT); + + memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg)); + + if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { //reset command?? + wavnc_mixer_reset(devc); + return (0); + } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) { //write command?? +// printk("WaveArtist Mixer: Private write command.\n"); + + wa_sendcmd(0x32); //Pair1 - word 1 and 5 + wa_sendcmd(mixer_reg[0]); + wa_sendcmd(mixer_reg[4]); + + wa_sendcmd(0x32); //Pair2 - word 2 and 6 + wa_sendcmd(mixer_reg[1]); + wa_sendcmd(mixer_reg[5]); + + wa_sendcmd(0x32); //Pair3 - word 3 and 7 + wa_sendcmd(mixer_reg[2]); + wa_sendcmd(mixer_reg[6]); + + wa_sendcmd(0x32); //Pair4 - word 4 and 8 + wa_sendcmd(mixer_reg[3]); + wa_sendcmd(mixer_reg[7]); + + wa_sendcmd(0x32); //Pair5 - word 9 and 10 + wa_sendcmd(mixer_reg[8]); + wa_sendcmd(mixer_reg[9]); + + wa_sendcmd(0x0031); //set left and right PCM + wa_sendcmd(mixer_reg[0x0A]); + wa_sendcmd(mixer_reg[0x0B]); + + wa_sendcmd(0x0131); //set left and right FM + wa_sendcmd(mixer_reg[0x0C]); + wa_sendcmd(mixer_reg[0x0D]); + + return 0; + } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) { //read command? +// printk("WaveArtist Mixer: Private read command.\n"); + + //first read all current values... + save_flags(flags); + cli(); + + for (i = 0; i < 14; i++) { + wa_sendcmd((i << 8) + 0x30); // get ready for command nn30H + + while (!(inb(STATR) & CMD_RF)) { + }; //wait for response ready... + + mixer_reg[i] = inw(CMDR); + } + restore_flags(flags); + + if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT)) + return (-EFAULT); + + memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg)); + return 0; + } else + return -EINVAL; + } +#endif + return -EINVAL; +} static inline void waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set) @@ -1368,138 +1637,12 @@ waveartist_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) { wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc; -#if 0 - //use this call to override the automatic handset behaviour - ignore handset - //bit 0x80 = total control over handset - do not react to plug/unplug - //bit 0x10 = 1 == internal mic, otherwise handset mic - //bit 0x01 = 1 == mute internal speaker, otherwise unmute - - if (cmd == SOUND_MIXER_PRIVATE1) { - int val, temp; - - val = *(int *) arg; - -// printk("MIXER_PRIVATE1: passed parameter = 0x%X.\n",val); - return -EINVAL; //check if parameter is logical... - - devc->soft_mute_flag = val; - - temp = val & VNC_INTERNAL_SPKR; - if (temp != devc->mute_state) { -// printk("MIXER_PRIVATE1: mute_mono(0x%X).\n",temp); - vnc_mute(devc, temp); - } - -// temp = devc->handset_state; - - // do not check if it is not already in - // the right setting, since we are - // laying about the current state... - -// if ((val & VNC_INTERNAL_MIC) != temp) { - devc->handset_state = val & VNC_INTERNAL_MIC; -// printk("MIXER_PRIVATE1: mixer_set(0x%X).\n",devc->handset_state); - wa_mixer_set(devc, SOUND_MIXER_RECSRC, SOUND_MASK_MIC); -// devc->handset_state = temp; - } - return 0; - } -#if 0 - if (cmd == SOUND_MIXER_PRIVATE2) { -#define VNC_SOUND_PAUSE 0x53 //to pause the DSP -#define VNC_SOUND_RESUME 0x57 //to unpause the DSP - int val; - - val = *(int *) arg; - - printk("MIXER_PRIVATE2: passed parameter = 0x%X.\n",val); - - if (val == VNC_SOUND_PAUSE) { - wa_sendcmd(0x16); //PAUSE the ADC - } else if (val == VNC_SOUND_RESUME) { - wa_sendcmd(0x18); //RESUME the ADC - } else { - return -EINVAL; //invalid parameters... - } - return 0; - } -#endif - if (cmd == SOUND_MIXER_PRIVATE3) { - long unsigned flags; - int mixer_reg[15]; //reg 14 is actually a command: read,write,reset - - int val; - int i; - - val = *(int *) arg; - - if (verify_area(VERIFY_READ, (void *) val, sizeof(mixer_reg) == -EFAULT)) - return (-EFAULT); - - memcpy_fromfs(&mixer_reg, (void *) val, sizeof(mixer_reg)); - - if (mixer_reg[0x0E] == MIXER_PRIVATE3_RESET) { //reset command?? - wavnc_mixer_reset(devc); - return (0); - } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_WRITE) { //write command?? -// printk("WaveArtist Mixer: Private write command.\n"); - - wa_sendcmd(0x32); //Pair1 - word 1 and 5 - wa_sendcmd(mixer_reg[0]); - wa_sendcmd(mixer_reg[4]); + if (cmd == SOUND_MIXER_PRIVATE1 || + cmd == SOUND_MIXER_PRIVATE2 || + cmd == SOUND_MIXER_PRIVATE3) + return vnc_private_ioctl(dev, cmd, arg); - wa_sendcmd(0x32); //Pair2 - word 2 and 6 - wa_sendcmd(mixer_reg[1]); - wa_sendcmd(mixer_reg[5]); - - wa_sendcmd(0x32); //Pair3 - word 3 and 7 - wa_sendcmd(mixer_reg[2]); - wa_sendcmd(mixer_reg[6]); - - wa_sendcmd(0x32); //Pair4 - word 4 and 8 - wa_sendcmd(mixer_reg[3]); - wa_sendcmd(mixer_reg[7]); - - wa_sendcmd(0x32); //Pair5 - word 9 and 10 - wa_sendcmd(mixer_reg[8]); - wa_sendcmd(mixer_reg[9]); - - wa_sendcmd(0x0031); //set left and right PCM - wa_sendcmd(mixer_reg[0x0A]); - wa_sendcmd(mixer_reg[0x0B]); - - wa_sendcmd(0x0131); //set left and right FM - wa_sendcmd(mixer_reg[0x0C]); - wa_sendcmd(mixer_reg[0x0D]); - - return 0; - } else if (mixer_reg[0x0E] == MIXER_PRIVATE3_READ) { //read command? -// printk("WaveArtist Mixer: Private read command.\n"); - - //first read all current values... - save_flags(flags); - cli(); - - for (i = 0; i < 14; i++) { - wa_sendcmd((i << 8) + 0x30); // get ready for command nn30H - - while (!(inb(STATR) & CMD_RF)) { - }; //wait for response ready... - - mixer_reg[i] = inw(CMDR); - } - restore_flags(flags); - - if (verify_area(VERIFY_WRITE, (void *) val, sizeof(mixer_reg) == -EFAULT)) - return (-EFAULT); - - memcpy_tofs((void *) val, &mixer_reg, sizeof(mixer_reg)); - return 0; - } else - return -EINVAL; - } -#endif if (((cmd >> 8) & 0xff) == 'M') { if (_SIOC_DIR(cmd) & _SIOC_WRITE) { int val; @@ -1660,139 +1803,6 @@ return -1; } -/* - * Corel Netwinder specifics... - */ -static void -vnc_mute(wavnc_info *devc, int mute) -{ - extern spinlock_t gpio_lock; - unsigned long flags; - - spin_lock_irqsave(&gpio_lock, flags); - cpld_modify(CPLD_UNMUTE, mute ? 0 : CPLD_UNMUTE); - spin_unlock_irqrestore(&gpio_lock, flags); - - devc->mute_state = mute; -} - -static int -vnc_volume_slider(wavnc_info *devc) -{ - static signed int old_slider_volume; - unsigned long flags; - signed int volume = 255; - - *CSR_TIMER1_LOAD = 0x00ffffff; - - save_flags(flags); - cli(); - - outb(0xFF, 0x201); - *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1; - - while (volume && (inb(0x201) & 0x01)) - volume--; - - *CSR_TIMER1_CNTL = 0; - - restore_flags(flags); - - volume = 0x00ffffff - *CSR_TIMER1_VALUE; - - -#ifndef REVERSE - volume = 150 - (volume >> 5); -#else - volume = (volume >> 6) - 25; -#endif - - if (volume < 0) - volume = 0; - - if (volume > 100) - volume = 100; - - /* - * slider quite often reads +-8, so debounce this random noise - */ - if ((volume - old_slider_volume) > 7 || - (old_slider_volume - volume) > 7) { - old_slider_volume = volume; - - DEB(printk("Slider volume: %d.\n", old_slider_volume)); - } - - return old_slider_volume; -} - -static int -vnc_slider(wavnc_info *devc) -{ - signed int slider_volume; - unsigned int temp; - - /* - * read the "buttons" state. - * Bit 4 = handset present, - * Bit 5 = offhook - */ - // the state should be "querable" via a private IOCTL call - temp = inb(0x201) & 0x30; - - if (!devc->handset_mute_sw && - (temp ^ devc->handset_state) & VNC_INTERNAL_MIC) { - devc->handset_state = temp; - devc->handset_mute_sw = 0; - - vnc_mute(devc, (temp & VNC_INTERNAL_MIC) ? 1 : 0); - } - - slider_volume = vnc_volume_slider(devc); - - /* - * If we're using software controlled volume, and - * the slider moves by more than 20%, then we - * switch back to slider controlled volume. - */ - if (devc->slider_vol > slider_volume) { - if (devc->slider_vol - slider_volume > 20) - devc->use_slider = 1; - } else { - if (slider_volume - devc->slider_vol > 20) - devc->use_slider = 1; - } - - /* - * use only left channel - */ - temp = levels[SOUND_MIXER_VOLUME] & 0xFF; - - if (slider_volume != temp && devc->use_slider) { - devc->slider_vol = slider_volume; - - waveartist_mixer_set(devc, SOUND_MIXER_VOLUME, - slider_volume | slider_volume << 8); - - return 1; - } - - return 0; -} - -static void -vnc_slider_tick(unsigned long data) -{ - int next_timeout; - - if (vnc_slider(adev_info + data)) - next_timeout = 5; // mixer reported change - else - next_timeout = VNC_TIMER_PERIOD; - - mod_timer(&vnc_timer, jiffies + next_timeout); -} - int probe_waveartist(struct address_info *hw_config) { @@ -1808,12 +1818,12 @@ return 0; } - if (hw_config->irq > 31 || hw_config->irq < 16) { + if (hw_config->irq > _ISA_IRQ(15) || hw_config->irq < _ISA_IRQ(0)) { printk("WaveArtist: Bad IRQ %d\n", hw_config->irq); return 0; } - if (hw_config->dma != 3) { + if (hw_config->dma != _ISA_DMA(3)) { printk("WaveArtist: Bad DMA %d\n", hw_config->dma); return 0; } diff -u --recursive --new-file v2.3.6/linux/drivers/sound/waveartist.h linux/drivers/sound/waveartist.h --- v2.3.6/linux/drivers/sound/waveartist.h Thu Jan 7 09:24:00 1999 +++ linux/drivers/sound/waveartist.h Thu Jun 17 01:11:35 1999 @@ -2,16 +2,11 @@ // def file for Rockwell RWA010 chip set, as installed in Corel NetWinder //registers -#define WA_BASE 0 -//x250 - -#define CMDR WA_BASE+0 -#define DATR WA_BASE+2 - -#define CTLR WA_BASE+4 -#define STATR WA_BASE+5 - -#define IRQSTAT WA_BASE+12 +#define CMDR 0 +#define DATR 2 +#define CTLR 4 +#define STATR 5 +#define IRQSTAT 12 //bit defs //reg STATR diff -u --recursive --new-file v2.3.6/linux/drivers/usb/acm.c linux/drivers/usb/acm.c --- v2.3.6/linux/drivers/usb/acm.c Fri Jun 4 13:33:43 1999 +++ linux/drivers/usb/acm.c Sun Jun 20 18:51:52 1999 @@ -50,7 +50,7 @@ spinlock_t usb_acm_lock = SPIN_LOCK_UNLOCKED; -static int acm_irq(int state, void *__buffer, void *dev_id) +static int acm_irq(int state, void *__buffer, int len, void *dev_id) { // unsigned char *data = __buffer; struct acm_state *acm = &static_acm_state; diff -u --recursive --new-file v2.3.6/linux/drivers/usb/audio.c linux/drivers/usb/audio.c --- v2.3.6/linux/drivers/usb/audio.c Fri Jun 4 13:33:43 1999 +++ linux/drivers/usb/audio.c Sun Jun 20 18:51:52 1999 @@ -27,7 +27,7 @@ }; -static int usb_audio_irq(int state, void *buffer, void *dev_id) +static int usb_audio_irq(int state, void *buffer, int len, void *dev_id) { struct usb_audio *aud = (struct usb_audio*) dev_id; return 1; diff -u --recursive --new-file v2.3.6/linux/drivers/usb/cpia.c linux/drivers/usb/cpia.c --- v2.3.6/linux/drivers/usb/cpia.c Fri Jun 4 13:33:43 1999 +++ linux/drivers/usb/cpia.c Sun Jun 20 18:51:52 1999 @@ -451,7 +451,7 @@ } } -static int cpia_isoc_irq(int status, void *__buffer, void *dev_id) +static int cpia_isoc_irq(int status, void *__buffer, int len, void *dev_id) { struct usb_cpia *cpia = dev_id; struct usb_device *dev = cpia->dev; diff -u --recursive --new-file v2.3.6/linux/drivers/usb/hub.c linux/drivers/usb/hub.c --- v2.3.6/linux/drivers/usb/hub.c Tue Jun 8 10:52:26 1999 +++ linux/drivers/usb/hub.c Sun Jun 20 18:51:52 1999 @@ -33,7 +33,7 @@ * the low-level driver that it wants to be re-activated, * or zero to say "I'm done". */ -static int hub_irq(int status, void *__buffer, void *dev_id) +static int hub_irq(int status, void *__buffer, int len, void *dev_id) { struct usb_hub *hub = dev_id; unsigned long flags; diff -u --recursive --new-file v2.3.6/linux/drivers/usb/keyboard.c linux/drivers/usb/keyboard.c --- v2.3.6/linux/drivers/usb/keyboard.c Tue Jun 8 10:52:26 1999 +++ linux/drivers/usb/keyboard.c Sun Jun 20 18:51:52 1999 @@ -92,7 +92,7 @@ } static int -usb_kbd_irq(int state, void *buffer, void *dev_id) +usb_kbd_irq(int state, void *buffer, int len, void *dev_id) { struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id; unsigned long *down = (unsigned long*) buffer; diff -u --recursive --new-file v2.3.6/linux/drivers/usb/mouse.c linux/drivers/usb/mouse.c --- v2.3.6/linux/drivers/usb/mouse.c Mon Jun 7 13:36:40 1999 +++ linux/drivers/usb/mouse.c Sun Jun 20 18:51:52 1999 @@ -60,7 +60,7 @@ spinlock_t usb_mouse_lock = SPIN_LOCK_UNLOCKED; -static int mouse_irq(int state, void *__buffer, void *dev_id) +static int mouse_irq(int state, void *__buffer, int len, void *dev_id) { signed char *data = __buffer; /* finding the mouse is easy when there's only one */ diff -u --recursive --new-file v2.3.6/linux/drivers/usb/ohci-hcd.c linux/drivers/usb/ohci-hcd.c --- v2.3.6/linux/drivers/usb/ohci-hcd.c Mon May 31 09:01:50 1999 +++ linux/drivers/usb/ohci-hcd.c Sun Jun 20 18:51:52 1999 @@ -102,7 +102,7 @@ OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) OHCI_DEBUG( printk(" ret_status: %x\n", status); }) - ret = handler(cc_to_status[status & 0xf], data, dev_id); + ret = handler(cc_to_status[status & 0xf], data, data_len, dev_id); if(ret == 0) return 0; /* 0 .. do not requeue */ if(status > 0) return -1; /* error occured do not requeue ? */ ohci_trans_req(ohci, ep_addr, 0, NULL, data, 8, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id); /* requeue int request */ diff -u --recursive --new-file v2.3.6/linux/drivers/usb/ohci.c linux/drivers/usb/ohci.c --- v2.3.6/linux/drivers/usb/ohci.c Tue Jun 8 10:52:26 1999 +++ linux/drivers/usb/ohci.c Sun Jun 20 18:55:52 1999 @@ -121,7 +121,7 @@ u32 new_dummy; if (ed->tail_td == 0) { - printk("eek! an ED without a dummy_td\n"); + printk(KERN_ERR "eek! an ED without a dummy_td\n"); return td; } @@ -234,8 +234,8 @@ */ int_ed = &root_hub->ed[ms_to_ed_int(period)]; #ifdef OHCI_DEBUG - printk("usb-ohci: Using INT ED queue %d for %dms period\n", - ms_to_ed_int(period), period); + printk(KERN_DEBUG "usb-ohci: Using INT ED queue %d for %dms period\n", + ms_to_ed_int(period), period); #endif spin_lock_irqsave(&ohci_edtd_lock, flags); @@ -264,6 +264,21 @@ */ DECLARE_WAIT_QUEUE_HEAD(start_of_frame_wakeup); +static void ohci_wait_sof(struct ohci_regs *regs) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&start_of_frame_wakeup, &wait); + + /* clear the SOF interrupt status and enable it */ + writel(OHCI_INTR_SF, ®s->intrstatus); + writel(OHCI_INTR_SF, ®s->intrenable); + + schedule_timeout(HZ/10); + + remove_wait_queue(&start_of_frame_wakeup, &wait); +} + /* * Guarantee that an ED is safe to be modified by the HCD (us). * @@ -296,21 +311,11 @@ * at least the next frame. */ if (virt_to_bus(ed) == readl(hw_listcurrent)) { - DECLARE_WAITQUEUE(wait, current); - #ifdef OHCI_DEBUG - printk("Waiting a frame for OHC to finish with ED %p [%x %x %x %x]\n", ed, FIELDS_OF_ED(ed)); + printk(KERN_INFO "Waiting a frame for OHC to finish with ED %p [%x %x %x %x]\n", ed, FIELDS_OF_ED(ed)); #endif + ohci_wait_sof(regs); - add_wait_queue(&start_of_frame_wakeup, &wait); - - /* clear the SOF interrupt status and enable it */ - writel(OHCI_INTR_SF, ®s->intrstatus); - writel(OHCI_INTR_SF, ®s->intrenable); - - schedule_timeout(HZ/10); - - remove_wait_queue(&start_of_frame_wakeup, &wait); } return; /* The ED is now safe */ @@ -334,6 +339,7 @@ if (ed == NULL || !bus_ed) return; + ed->status |= cpu_to_le32(OHCI_ED_SKIP); switch (ed_type) { case HCD_ED_CONTROL: @@ -343,16 +349,10 @@ hw_listhead_p = ®s->ed_bulkhead; break; default: - printk("Unknown HCD ED type %d.\n", ed_type); + printk(KERN_ERR "Unknown HCD ED type %d.\n", ed_type); return; } - /* - * Tell the controller to this skip ED and make sure it is not the - * being accessed by the HC as we speak. - */ - ohci_wait_for_ed_safe(regs, ed, ed_type); - bus_cur = readl(hw_listhead_p); if (bus_cur == 0) @@ -364,7 +364,7 @@ /* if its the head ED, move the head */ if (bus_cur == bus_ed) { - writel(cur->next_ed, hw_listhead_p); + writel(le32_to_cpup(&cur->next_ed), hw_listhead_p); } else if (cur->next_ed != 0) { struct ohci_ed *prev; @@ -373,7 +373,7 @@ prev = cur; cur = bus_to_virt(le32_to_cpup(&cur->next_ed)); - if (virt_to_bus(cur) == bus_ed) { + if (cur == ed) { /* unlink from the list */ prev->next_ed = cur->next_ed; break; @@ -381,6 +381,11 @@ } while (cur->next_ed != 0); } + /* + * Make sure this ED is not being accessed by the HC as we speak. + */ + ohci_wait_for_ed_safe(regs, ed, ed_type); + /* clear any links from the ED for safety */ ed->next_ed = 0; @@ -405,6 +410,68 @@ ohci_remove_norm_ed_from_hw(ohci, ed, HCD_ED_BULK); } +/* + * Remove all the EDs which have a given device address from a list. + * Used when the device is unplugged. + * Returns 1 if anything was changed. + */ +static int ohci_remove_device_list(__u32 *headp, int devnum) +{ + struct ohci_ed *ed; + __u32 *prevp = headp; + int removed = 0; + + while (*prevp != 0) { + ed = bus_to_virt(le32_to_cpup(prevp)); + if ((le32_to_cpup(&ed->status) & OHCI_ED_FA) == devnum) { + /* set the controller to skip this one + and remove it from the list */ + ed->status |= cpu_to_le32(OHCI_ED_SKIP); + *prevp = ed->next_ed; + removed = 1; + } else { + prevp = &ed->next_ed; + } + } + wmb(); + + return removed; +} + +/* + * Remove all the EDs for a given device from all lists. + */ +void ohci_remove_device(struct ohci *ohci, int devnum) +{ + unsigned long flags; + __u32 head; + struct ohci_regs *regs = ohci->regs; + struct ohci_device *root_hub=usb_to_ohci(ohci->bus->root_hub); + + spin_lock_irqsave(&ohci_edtd_lock, flags); + + /* Control list */ + head = cpu_to_le32(readl(®s->ed_controlhead)); + if (ohci_remove_device_list(&head, devnum)) + writel(le32_to_cpup(&head), ®s->ed_controlhead); + + /* Bulk list */ + head = cpu_to_le32(readl(®s->ed_bulkhead)); + if (ohci_remove_device_list(&head, devnum)) + writel(le32_to_cpup(&head), ®s->ed_bulkhead); + + /* Interrupt/iso list */ + head = cpu_to_le32(virt_to_bus(&root_hub->ed[ED_INT_32])); + ohci_remove_device_list(&head, devnum); + + /* + * Wait until the start of the next frame to ensure + * that the HC has seen any changes. + */ + ohci_wait_sof(ohci->regs); + + spin_unlock_irqrestore(&ohci_edtd_lock, flags); +} /* * Remove a TD from the given EDs TD list. @@ -494,7 +561,7 @@ } } - printk("usb-ohci: unable to allocate a TD\n"); + printk(KERN_ERR "usb-ohci: unable to allocate a TD\n"); return NULL; } /* ohci_get_free_td() */ @@ -521,7 +588,7 @@ } } - printk("usb-ohci: unable to allocate an ED\n"); + printk(KERN_ERR "usb-ohci: unable to allocate an ED\n"); return NULL; } /* ohci_get_free_ed() */ @@ -594,12 +661,12 @@ struct ohci_td *dummy_td; if (ed_head_td(ed) != ed_tail_td(ed)) - printk("Reusing a non-empty ED %p!\n", ed); + printk(KERN_ERR "Reusing a non-empty ED %p!\n", ed); if (!ed->tail_td) { dummy_td = ohci_get_free_td(dev); if (dummy_td == NULL) { - printk("Error allocating dummy TD for ED %p\n", ed); + printk(KERN_ERR "Error allocating dummy TD for ED %p\n", ed); return NULL; /* no dummy available! */ } make_dumb_td(dummy_td); /* flag it as a dummy */ @@ -607,7 +674,7 @@ } else { dummy_td = bus_to_virt(ed_tail_td(ed)); if (!td_dummy(*dummy_td)) - printk("ED %p's dummy %p is screwy\n", ed, dummy_td); + printk(KERN_ERR "ED %p's dummy %p is screwy\n", ed, dummy_td); } /* set the head TD to the dummy and clear the Carry & Halted bits */ @@ -650,13 +717,13 @@ /* Get an ED and TD */ interrupt_ed = ohci_get_free_ed(dev); if (!interrupt_ed) { - printk("Out of EDs on device %p in ohci_request_irq\n", dev); + printk(KERN_ERR "Out of EDs on device %p in ohci_request_irq\n", dev); return -1; } td = ohci_get_free_td(dev); if (!td) { - printk("Out of TDs in ohci_request_irq\n"); + printk(KERN_ERR "Out of TDs in ohci_request_irq\n"); ohci_free_ed(interrupt_ed); return -1; } @@ -710,7 +777,7 @@ * * This function is called from the interrupt handler. */ -static int ohci_control_completed(int stats, void *buffer, void *dev_id) +static int ohci_control_completed(int stats, void *buffer, int len, void *dev_id) { /* pass the TDs completion status back to control_msg */ if (dev_id) { @@ -759,14 +826,14 @@ printk(KERN_DEBUG "ohci_control_msg %p (ohci_dev: %p) pipe %x, cmd %p, data %p, len %d\n", usb, dev, pipe, cmd, data, len); #endif if (!control_ed) { - printk("usb-ohci: couldn't get ED for dev %p\n", dev); + printk(KERN_ERR "usb-ohci: couldn't get ED for dev %p\n", dev); return -1; } /* get a TD to send this control message with */ setup_td = ohci_get_free_td(dev); if (!setup_td) { - printk("usb-ohci: couldn't get TD for dev %p [cntl setup]\n", dev); + printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl setup]\n", dev); ohci_free_ed(control_ed); return -1; } @@ -799,7 +866,7 @@ /* allocate the next TD */ data_td = ohci_get_free_td(dev); if (!data_td) { - printk("usb-ohci: couldn't get TD for dev %p [cntl data]\n", dev); + printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl data]\n", dev); ohci_free_td(setup_td); ohci_free_ed(control_ed); return -1; @@ -833,7 +900,7 @@ status_td = ohci_get_free_td(dev); /* TODO check for NULL */ if (!status_td) { - printk("usb-ohci: couldn't get TD for dev %p [cntl status]\n", dev); + printk(KERN_ERR "usb-ohci: couldn't get TD for dev %p [cntl status]\n", dev); ohci_free_td(setup_td); ohci_free_td(data_td); ohci_free_ed(control_ed); @@ -918,15 +985,17 @@ #ifdef OHCI_DEBUG if (completion_status != 0) { - printk(KERN_ERR "ohci_control_msg: %s on cmd %x %x %x %x %x\n", - cc_names[completion_status & 0xf], cmd->requesttype, - cmd->request, cmd->value, cmd->index, cmd->length); + char *what = (completion_status < 0)? "timed out": + cc_names[completion_status & 0xf]; + printk(KERN_ERR "ohci_control_msg: %s on pipe %x cmd %x %x %x %x %x\n", + what, pipe, cmd->requesttype, cmd->request, + cmd->value, cmd->index, cmd->length); } else if (!usb_pipeout(pipe)) { unsigned char *q = data; int i; - printk(KERN_DEBUG "ctrl msg %x %x %x %x %x returned:", + printk(KERN_DEBUG "ctrl msg %x %x %x %x %x on pipe %x returned:", cmd->requesttype, cmd->request, cmd->value, cmd->index, - cmd->length); + cmd->length, pipe); for (i = 0; i < len; ++i) { if (i % 16 == 0) printk("\n" KERN_DEBUG); @@ -1001,8 +1070,12 @@ */ static int ohci_usb_deallocate(struct usb_device *usb_dev) { - kfree(usb_to_ohci(usb_dev)); - kfree(usb_dev); + struct ohci_device *dev = usb_to_ohci(usb_dev); + + ohci_remove_device(dev->ohci, usb_dev->devnum); + + /* kfree(usb_to_ohci(usb_dev)); */ + /* kfree(usb_dev); */ return 0; } @@ -1042,13 +1115,13 @@ while ((readl(&ohci->regs->cmdstatus) & OHCI_CMDSTAT_HCR) != 0) { if (!--timeout) { - printk("usb-ohci: USB HC reset timed out!\n"); + printk(KERN_ERR "usb-ohci: USB HC reset timed out!\n"); return -1; } udelay(1); } - printk(KERN_INFO "usb-ohci: HC %p reset.\n", ohci); + printk(KERN_DEBUG "usb-ohci: HC %p reset.\n", ohci); return 0; } /* reset_hc() */ @@ -1080,7 +1153,8 @@ * XXX Should fminterval also be set here? * The spec suggests 0x2edf [11,999]. (FIXME: make this a constant) */ - fminterval |= (0x2edf << 16); + /* fminterval |= (0x2edf << 16); */ + fminterval = (10240 << 16) | 11999; writel(fminterval, &ohci->regs->fminterval); /* Start periodic transfers at 90% of fminterval (fmremaining * counts down; this will put them in the first 10% of the @@ -1122,7 +1196,7 @@ /* Turn on power to the root hub ports (thanks Roman!) */ writel( OHCI_ROOT_LPSC, &ohci->regs->roothub.status ); - printk("usb-ohci: host controller operational\n"); + printk(KERN_INFO "usb-ohci: host controller operational\n"); return ret; } /* start_hc() */ @@ -1137,7 +1211,7 @@ /* Don't allow overflows. */ if (port >= MAX_ROOT_PORTS) { - printk("usb-ohci: bad port #%d in ohci_reset_port\n", port); + printk(KERN_ERR "usb-ohci: bad port #%d in ohci_reset_port\n", port); port = MAX_ROOT_PORTS-1; } @@ -1152,7 +1226,7 @@ status = readl(&ohci->regs->roothub.portstatus[port]); if (status & PORT_PRS) { /* reset failed, try harder? */ - printk("usb-ohci: port %d reset failed, retrying\n", port); + printk(KERN_ERR "usb-ohci: port %d reset failed, retrying\n", port); writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]); wait_ms(50); } @@ -1340,7 +1414,7 @@ int cc = OHCI_TD_CC_GET(le32_to_cpup(&td->info)); if (td_dummy(*td)) - printk("yikes! reaping a dummy TD\n"); + printk(KERN_ERR "yikes! reaping a dummy TD\n"); /* FIXME: munge td->info into a future standard status format */ @@ -1382,7 +1456,7 @@ /* Check if TD should be re-queued */ if ((td->completed != NULL) && - (td->completed(cc, td->data, td->dev_id))) { + (td->completed(cc, td->data, -1 /* XXX */, td->dev_id))) { /* Mark the TD as active again: * Set the not accessed condition code * Reset the Error count @@ -1598,14 +1672,14 @@ /* Get the number of ports on the root hub */ usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff; if (usb->maxchild > MAX_ROOT_PORTS) { - printk("usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS); + printk(KERN_INFO "usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS); usb->maxchild = MAX_ROOT_PORTS; } if (usb->maxchild < 1) { - printk("usb-ohci: Less than one root hub port? Impossible!\n"); + printk(KERN_ERR "usb-ohci: Less than one root hub port? Impossible!\n"); usb->maxchild = 1; } - printk("usb-ohci: %d root hub ports found\n", usb->maxchild); + printk(KERN_DEBUG "usb-ohci: %d root hub ports found\n", usb->maxchild); /* * Initialize the ED polling "tree" (for simplicity's sake in @@ -1650,14 +1724,13 @@ writel(0, &ohci->regs->ed_bulkhead); #ifdef OHCI_DEBUG - printk(KERN_INFO "alloc_ohci(): controller\n"); + printk(KERN_DEBUG "alloc_ohci(): controller\n"); show_ohci_status(ohci); #endif #if 0 printk(KERN_DEBUG "leaving alloc_ohci %p\n", ohci); #endif -printk("alloc_ohci done\n"); return ohci; } /* alloc_ohci() */ @@ -1722,7 +1795,7 @@ * This thread doesn't need any user-level access, * so get rid of all of our resources.. */ - printk(KERN_INFO "ohci-control thread code for 0x%p code at 0x%p\n", __ohci, &ohci_control_thread); + printk(KERN_DEBUG "ohci-control thread code for 0x%p code at 0x%p\n", __ohci, &ohci_control_thread); exit_mm(current); exit_files(current); exit_fs(current); @@ -1735,7 +1808,7 @@ * Damn the torpedoes, full speed ahead */ if (start_hc(ohci) < 0) { - printk("usb-ohci: failed to start the controller\n"); + printk(KERN_ERR "usb-ohci: failed to start the controller\n"); release_ohci(ohci); usb_deregister_bus(ohci->bus); printk(KERN_INFO "leaving ohci_control_thread %p\n", __ohci); @@ -1756,7 +1829,7 @@ writel(OHCI_INTR_RHSC, &ohci->regs->intrenable); #endif - printk(KERN_INFO "ohci-control thread sleeping\n"); + printk(KERN_DEBUG "ohci-control thread sleeping\n"); interruptible_sleep_on(&ohci_configure); #ifdef CONFIG_APM if (apm_resume) { @@ -1796,7 +1869,7 @@ reset_hc(ohci); release_ohci(ohci); usb_deregister_bus(ohci->bus); - printk(KERN_INFO "ohci-control thread for 0x%p exiting\n", __ohci); + printk(KERN_DEBUG "ohci-control thread for 0x%p exiting\n", __ohci); return 0; } /* ohci_control_thread() */ @@ -1891,7 +1964,7 @@ ohci->irq = irq; #ifdef OHCI_DEBUG - printk(KERN_INFO "usb-ohci: forking ohci-control thread for 0x%p\n", ohci); + printk(KERN_DEBUG "usb-ohci: forking ohci-control thread for 0x%p\n", ohci); #endif /* fork off the handler */ @@ -1903,7 +1976,7 @@ retval = pid; } else { - printk("usb-ohci: Couldn't allocate interrupt %d\n", irq); + printk(KERN_ERR "usb-ohci: Couldn't allocate interrupt %d\n", irq); } release_ohci(ohci); @@ -1931,7 +2004,7 @@ /* no interrupt won't work... */ if (dev->irq == 0) { - printk("usb-ohci: no irq assigned? check your BIOS settings.\n"); + printk(KERN_ERR "usb-ohci: no irq assigned? check your BIOS settings.\n"); return -ENODEV; } @@ -1944,14 +2017,14 @@ mem_base = (unsigned long) ioremap_nocache(mem_base, 4096); if (!mem_base) { - printk("Error mapping OHCI memory\n"); + printk(KERN_ERR "Error mapping OHCI memory\n"); return -EFAULT; } MOD_INC_USE_COUNT; #ifdef OHCI_DEBUG - printk("usb-ohci: Warning! Gobs of debugging output has been enabled.\n"); - printk(" Check your kern.debug logs for the bulk of it.\n"); + printk(KERN_INFO "usb-ohci: Warning! Gobs of debugging output has been enabled.\n"); + printk(KERN_INFO " Check your kern.debug logs for the bulk of it.\n"); #endif if (found_ohci(dev->irq, (void *) mem_base) < 0) { @@ -1978,11 +2051,11 @@ /*u8 type;*/ if (sizeof(struct ohci_device) > 4096) { - printk("usb-ohci: struct ohci_device to large\n"); + printk(KERN_ERR "usb-ohci: struct ohci_device to large\n"); return -ENODEV; } - printk("OHCI USB Driver loading\n"); + printk(KERN_INFO "OHCI USB Driver loading\n"); retval = -ENODEV; for (;;) { @@ -2022,7 +2095,7 @@ # ifdef CONFIG_APM apm_unregister_callback(&handle_apm_event); # endif - printk("usb-ohci: module unloaded\n"); + printk(KERN_ERR "usb-ohci: module unloaded\n"); } int init_module(void){ diff -u --recursive --new-file v2.3.6/linux/drivers/usb/uhci-debug.c linux/drivers/usb/uhci-debug.c --- v2.3.6/linux/drivers/usb/uhci-debug.c Mon May 31 09:01:50 1999 +++ linux/drivers/usb/uhci-debug.c Sun Jun 20 11:43:42 1999 @@ -131,7 +131,7 @@ #if 0 printk(" link = %p, element = %p\n", qh->link, qh->element); #endif - if(!qh->element) { + if(!(qh->element & ~0xF)) { printk(" td 0 = NULL\n"); return; } diff -u --recursive --new-file v2.3.6/linux/drivers/usb/uhci.c linux/drivers/usb/uhci.c --- v2.3.6/linux/drivers/usb/uhci.c Tue Jun 8 10:53:43 1999 +++ linux/drivers/usb/uhci.c Sun Jun 20 18:54:01 1999 @@ -126,7 +126,7 @@ tmp = td->first; printk("uhci_td_result() failed with status %x\n", status); - show_status(dev->uhci); + //show_status(dev->uhci); do { show_td(tmp); if ((tmp->link & 1) || (tmp->link & 2)) @@ -422,7 +422,7 @@ /* notify removal */ - td->completed(USB_ST_REMOVED, NULL, td->dev_id); + td->completed(USB_ST_REMOVED, NULL, 0, td->dev_id); /* this is DANGEROUS - not sure whether this is right */ @@ -645,7 +645,7 @@ */ static DECLARE_WAIT_QUEUE_HEAD(control_wakeup); -static int uhci_control_completed(int status, void *buffer, void *dev_id) +static int uhci_control_completed(int status, void *buffer, int len, void *dev_id) { wake_up(&control_wakeup); return 0; /* Don't re-instate */ @@ -692,7 +692,7 @@ // show_status(dev->uhci); // show_queues(dev->uhci); - schedule_timeout(HZ/10); + schedule_timeout(HZ*5); // control should be empty here... // show_status(dev->uhci); @@ -736,8 +736,7 @@ * information, that's just ridiculously high. Most * control messages have just a few bytes of data. */ -static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, - devrequest *cmd, void *data, int len) +static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len) { struct uhci_device *dev = usb_to_uhci(usb_dev); struct uhci_td *first, *td, *prevtd; @@ -805,17 +804,18 @@ } /* - * Build the final TD for control status + * Build the final TD for control status */ destination ^= (0xE1 ^ 0x69); /* OUT -> IN */ destination |= 1 << 19; /* End in Data1 */ - td->link = 1; /* Terminate */ - td->status = status | (1 << 24); /* IOC */ + td->backptr = &prevtd->link; + td->status = (status /* & ~(3 << 27) */) | (1 << 24); /* no limit on final packet */ td->info = destination | (0x7ff << 21); /* 0 bytes of data */ td->buffer = 0; td->first = first; - td->backptr = &prevtd->link; + td->link = 1; /* Terminate */ + /* Start it up.. */ ret = uhci_run_control(dev, first, td); @@ -841,7 +841,7 @@ } if (uhci_debug && ret) { - __u8 *p = (__u8 *) cmd; + __u8 *p = cmd; printk("Failed cmd - %02X %02X %02X %02X %02X %02X %02X %02X\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); @@ -860,7 +860,7 @@ */ static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup); -static int uhci_bulk_completed(int status, void *buffer, void *dev_id) +static int uhci_bulk_completed(int status, void *buffer, int len, void *dev_id) { wake_up(&bulk_wakeup); return 0; /* Don't re-instate */ @@ -908,10 +908,11 @@ // show_status(dev->uhci); // show_queues(dev->uhci); - schedule_timeout(HZ/10); + schedule_timeout(HZ*5); // show_status(dev->uhci); // show_queues(dev->uhci); + //show_queue(first->qh); remove_wait_queue(&bulk_wakeup, &wait); /* Clean up in case it failed.. */ @@ -1243,6 +1244,7 @@ { struct list_head *head = &uhci->interrupt_list; struct list_head *tmp; + int status; spin_lock(&irqlist_lock); tmp = head->next; @@ -1252,19 +1254,22 @@ next = tmp->next; - if (!(td->status & (1 << 23))) { /* No longer active? */ + if (!((status = td->status) & (1 << 23)) || /* No longer active? */ + ((td->qh->element & ~15) && + !((status = uhci_link_to_td(td->qh->element)->status) & (1 <<23)) && + (status & 0x760000) /* is in error state (Stall, db, babble, timeout, bitstuff) */)) { /* remove from IRQ list */ __list_del(tmp->prev, next); INIT_LIST_HEAD(tmp); - if (td->completed(uhci_map_status((td->status & 0xff)>> 16, 0), - bus_to_virt(td->buffer), td->dev_id)) { + if (td->completed(uhci_map_status(status, 0), bus_to_virt(td->buffer), -1, td->dev_id)) { list_add(&td->irq_list, &uhci->interrupt_list); if (!(td->status & (1 << 25))) { struct uhci_qh *interrupt_qh = td->qh; usb_dotoggle(td->dev, usb_pipeendpoint(td->info)); - td->info |= 1 << 19; /* toggle between data0 and data1 */ + td->info &= ~(1 << 19); /* clear data toggle */ + td->info |= usb_gettoggle(td->dev, usb_pipeendpoint(td->info)) << 19; /* toggle between data0 and data1 */ td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24); /* active */ /* Remove then readd? Is that necessary */ @@ -1283,7 +1288,7 @@ /* If completed wants to not reactivate, then it's */ /* responsible for free'ing the TD's and QH's */ /* or another function (such as run_control) */ - } + } tmp = next; } spin_unlock(&irqlist_lock); @@ -1563,6 +1568,7 @@ { struct uhci *uhci = (struct uhci *)__uhci; struct uhci_device * root_hub =usb_to_uhci(uhci->bus->root_hub); + lock_kernel(); request_region(uhci->io_addr, 32, "usb-uhci"); diff -u --recursive --new-file v2.3.6/linux/drivers/usb/usb-core.c linux/drivers/usb/usb-core.c --- v2.3.6/linux/drivers/usb/usb-core.c Mon Jun 7 20:04:01 1999 +++ linux/drivers/usb/usb-core.c Wed Jun 16 19:26:27 1999 @@ -53,7 +53,7 @@ usb_acm_init(); # endif # ifdef CONFIG_USB_PRINTER - usb_print_init(); + usb_printer_init(); # endif # ifdef CONFIG_USB_CPIA usb_cpia_init(); diff -u --recursive --new-file v2.3.6/linux/drivers/usb/usb.h linux/drivers/usb/usb.h --- v2.3.6/linux/drivers/usb/usb.h Tue Jun 8 23:04:17 1999 +++ linux/drivers/usb/usb.h Sun Jun 20 18:51:52 1999 @@ -242,10 +242,12 @@ * until we come up with a common meaning. * void *buffer - This is a pointer to the data used in this * USB transfer. + * int length - This is the number of bytes transferred in or out + * of the buffer by this transfer. (-1 = unknown/unsupported) * void *dev_id - This is a user defined pointer set when the IRQ * is requested that is passed back. */ -typedef int (*usb_device_irq)(int, void *, void *); +typedef int (*usb_device_irq)(int, void *, int, void *); struct usb_operations { struct usb_device *(*allocate)(struct usb_device *); diff -u --recursive --new-file v2.3.6/linux/drivers/usb/usb_scsi.c linux/drivers/usb/usb_scsi.c --- v2.3.6/linux/drivers/usb/usb_scsi.c Mon Jun 7 20:04:01 1999 +++ linux/drivers/usb/usb_scsi.c Sun Jun 20 18:51:52 1999 @@ -74,7 +74,9 @@ __u8 ep_int; /* interrupt . */ __u8 subclass; /* as in overview */ __u8 protocol; /* .............. */ + __u8 attention_done; /* force attention on first command */ int (*pop)(Scsi_Cmnd *); /* protocol specific do cmd */ + int (*pop_reset)(struct us_data *); /* ................. device reset */ GUID(guid); /* unique dev id */ struct Scsi_Host *host; /* our dummy host data */ Scsi_Host_Template *htmplt; /* own host template */ @@ -142,6 +144,9 @@ /* we want to retry if the device reported NAK */ if (result == USB_ST_TIMEOUT) { + if (partial != this_xfer) { + return 0; /* I do not like this */ + } if (!maxtry--) break; this_xfer -= partial; @@ -150,6 +155,11 @@ /* short data - assume end */ result = USB_ST_DATAUNDERRUN; break; + } else if (result == USB_ST_STALL && us->protocol == US_PR_CB) { + if (!maxtry--) + break; + this_xfer -= partial; + buf += partial; } else break; } while ( this_xfer ); @@ -216,27 +226,57 @@ } -static int pop_CBI_irq(int state, void *buffer, void *dev_id) +static int pop_CBI_irq(int state, void *buffer, int len, void *dev_id) { struct us_data *us = (struct us_data *)dev_id; if (state != USB_ST_REMOVED) { us->ip_data = *(__u16 *)buffer; - us->ip_wanted = 0; + US_DEBUGP("Interrupt Status %x\n", us->ip_data); } - wake_up(&us->ip_waitq); + if (us->ip_wanted) + wake_up(&us->ip_waitq); + us->ip_wanted = 0; /* we dont want another interrupt */ return 0; } + +static int pop_CB_reset(struct us_data *us) +{ + unsigned char cmd[12]; + devrequest dr; + int result; + + dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE; + dr.request = US_CBI_ADSC; + dr.value = 0; + dr.index = us->pusb_dev->ifnum; + dr.length = 12; + memset(cmd, -1, sizeof(cmd)); + cmd[0] = SEND_DIAGNOSTIC; + cmd[1] = 4; + us->pusb_dev->bus->op->control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev,0), + &dr, cmd, 12); + + usb_clear_halt(us->pusb_dev, us->ep_in | 0x80); + usb_clear_halt(us->pusb_dev, us->ep_out); + + /* long wait for reset */ + + schedule_timeout(HZ*5); + return 0; +} + static int pop_CB_command(Scsi_Cmnd *srb) { struct us_data *us = (struct us_data *)srb->host_scribble; devrequest dr; unsigned char cmd[16]; int result; - int retry = 1; + int retry = 5; int done_start = 0; while (retry--) { @@ -279,7 +319,8 @@ result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, cmd, us->fixedlength); - if (!done_start && us->subclass == US_SC_UFI && cmd[0] == TEST_UNIT_READY && result) { + if (!done_start && (us->subclass == US_SC_UFI /*|| us->subclass == US_SC_8070*/) + && cmd[0] == TEST_UNIT_READY && result) { /* as per spec try a start command, wait and retry */ done_start++; @@ -302,35 +343,47 @@ return result; } -/* Protocol command handlers */ +/* + * Control/Bulk status handler + */ -static int pop_CBI(Scsi_Cmnd *srb) +static int pop_CB_status(Scsi_Cmnd *srb) { struct us_data *us = (struct us_data *)srb->host_scribble; int result; + __u8 status[2]; + devrequest dr; + int retry = 5; - /* run the command */ - - if ((result = pop_CB_command(srb))) { - US_DEBUGP("CBI command %x\n", result); - if (result == USB_ST_STALL || result == USB_ST_TIMEOUT) - return (DID_OK << 16) | 2; - return DID_ABORT << 16; - } - - /* transfer the data */ + switch (us->protocol) { + case US_PR_CB: + /* get from control */ - if (us_transfer_length(srb)) { - result = us_transfer(srb, US_DIRECTION(srb->cmnd[0])); - if (result && result != USB_ST_DATAUNDERRUN) { - US_DEBUGP("CBI transfer %x\n", result); + while (retry--) { + dr.requesttype = 0x80 | USB_TYPE_STANDARD | USB_RT_DEVICE; + dr.request = USB_REQ_GET_STATUS; + dr.index = 0; + dr.value = 0; + dr.length = 2; + result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, + usb_rcvctrlpipe(us->pusb_dev,0), + &dr, status, sizeof(status)); + if (result != USB_ST_TIMEOUT) + break; + } + if (result) { + US_DEBUGP("Bad AP status request %d\n", result); return DID_ABORT << 16; } - } - - /* get status */ + US_DEBUGP("Got AP status %x %x\n", status[0], status[1]); + if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY && + ( (status[0] & ~3) || status[1])) + return (DID_OK << 16) | 2; + else + return DID_OK << 16; + break; - if (us->protocol == US_PR_CBI) { + case US_PR_CBI: /* get from interrupt pipe */ /* add interrupt transfer, marked for removal */ @@ -367,12 +420,48 @@ return DID_ABORT << 16; } return (DID_OK << 16) + ((us->ip_data & 0x300) ? 2 : 0); - } else { - /* get from where? */ } return DID_ERROR << 16; } +/* Protocol command handlers */ + +static int pop_CBI(Scsi_Cmnd *srb) +{ + struct us_data *us = (struct us_data *)srb->host_scribble; + int result; + + /* run the command */ + + if ((result = pop_CB_command(srb))) { + US_DEBUGP("CBI command %x\n", result); + if (result == USB_ST_STALL || result == USB_ST_TIMEOUT) { + return (DID_OK << 16) | 2; + } + return DID_ABORT << 16; + } + + /* transfer the data */ + + if (us_transfer_length(srb)) { + result = us_transfer(srb, US_DIRECTION(srb->cmnd[0])); + if (result && result != USB_ST_DATAUNDERRUN) { + US_DEBUGP("CBI transfer %x\n", result); + return DID_ABORT << 16; + } else if (result == USB_ST_DATAUNDERRUN) { + return DID_OK << 16; + } + } else { + if (!result) { + return DID_OK << 16; + } + } + + /* get status */ + + return pop_CB_status(srb); +} + static int pop_Bulk_reset(struct us_data *us) { devrequest dr; @@ -380,21 +469,20 @@ dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE; dr.request = US_BULK_RESET; - dr.value = US_BULK_RESET_SOFT; + dr.value = US_BULK_RESET_HARD; dr.index = 0; dr.length = 0; - US_DEBUGP("Bulk soft reset\n"); result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0); - if (result) { - US_DEBUGP("Bulk soft reset failed %d\n", result); - dr.value = US_BULK_RESET_HARD; - result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0); - if (result) - US_DEBUGP("Bulk hard reset failed %d\n", result); - } + if (result) + US_DEBUGP("Bulk hard reset failed %d\n", result); usb_clear_halt(us->pusb_dev, us->ep_in | 0x80); usb_clear_halt(us->pusb_dev, us->ep_out); + + /* long wait for reset */ + + schedule_timeout(HZ*5); + return result; } /* @@ -453,8 +541,6 @@ stall = 0; do { - //usb_settoggle(us->pusb_dev, us->ep_in, 0); /* AAARgh!! */ - US_DEBUGP("Toggle is %d\n", usb_gettoggle(us->pusb_dev, us->ep_in)); result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in), &bcs, US_BULK_CS_WRAP_LEN, &partial); @@ -564,6 +650,9 @@ struct us_data *us = (struct us_data *)srb->host->hostdata[0]; US_DEBUGP("Command wakeup\n"); + if (us->srb) { + /* busy */ + } srb->host_scribble = (unsigned char *)us; us->srb = srb; srb->scsi_done = done; @@ -581,9 +670,12 @@ return 0; } -static int us_device_reset( Scsi_Cmnd *srb ) +static int us_bus_reset( Scsi_Cmnd *srb ) { - return 0; + struct us_data *us = (struct us_data *)srb->host->hostdata[0]; + + us->pop_reset(us); + return SUCCESS; } static int us_host_reset( Scsi_Cmnd *srb ) @@ -591,10 +683,6 @@ return 0; } -static int us_bus_reset( Scsi_Cmnd *srb ) -{ - return 0; -} #undef SPRINTF #define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); } @@ -623,9 +711,9 @@ if (inout) return length; - if (!(vendor = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer))) + if (!us->pusb_dev || !(vendor = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer))) vendor = "?"; - if (!(product = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct))) + if (!us->pusb_dev || !(product = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct))) product = "?"; switch (us->protocol) { @@ -677,7 +765,7 @@ us_queuecommand, NULL, /* eh_strategy */ us_abort, - us_device_reset, + us_bus_reset, us_bus_reset, us_host_reset, NULL, /* abort */ @@ -695,6 +783,25 @@ TRUE /* emulated */ }; +static unsigned char sense_notready[] = { + 0x70, /* current error */ + 0x00, + 0x02, /* not ready */ + 0x00, + 0x00, + 10, /* additional length */ + 0x00, + 0x00, + 0x00, + 0x00, + 0x04, /* not ready */ + 0x03, /* manual intervention */ + 0x00, + 0x00, + 0x00, + 0x00 +}; + static int usbscsi_control_thread(void * __us) { struct us_data *us = (struct us_data *)__us; @@ -710,7 +817,7 @@ exit_files(current); //exit_fs(current); - sprintf(current->comm, "usbscsi%d", us->host_no); + sprintf(current->comm, "usbscsi%d", us->host_number); unlock_kernel(); @@ -727,18 +834,160 @@ switch (action) { case US_ACT_COMMAND : - if (!us->pusb_dev || us->srb->target || us->srb->lun) { + if (us->srb->target || us->srb->lun) { /* bad device */ US_DEBUGP( "Bad device number (%d/%d) or dev %x\n", us->srb->target, us->srb->lun, (unsigned int)us->pusb_dev); us->srb->result = DID_BAD_TARGET << 16; + } else if (!us->pusb_dev) { + + /* our device has gone - pretend not ready */ + + if (us->srb->cmnd[0] == REQUEST_SENSE) { + memcpy(us->srb->request_buffer, sense_notready, sizeof(sense_notready)); + us->srb->result = DID_OK << 16; + } else { + us->srb->result = (DID_OK << 16) | 2; + } } else { US_DEBUG(us_show_command(us->srb)); + + /* check for variable length - do properly if so */ + if (us->filter && us->filter->command) us->srb->result = us->filter->command(us->fdata, us->srb); - else + else if (us->srb->cmnd[0] == START_STOP && + us->pusb_dev->descriptor.idProduct == 0x0001 && + us->pusb_dev->descriptor.idVendor == 0x04e6) + us->srb->result = DID_OK << 16; + else { + unsigned int savelen = us->srb->request_bufflen; + unsigned int saveallocation; + + switch (us->srb->cmnd[0]) { + case REQUEST_SENSE: + if (us->srb->request_bufflen > 18) + us->srb->request_bufflen = 18; + else + break; + saveallocation = us->srb->cmnd[4]; + us->srb->cmnd[4] = 18; + break; + + case INQUIRY: + if (us->srb->request_bufflen > 36) + us->srb->request_bufflen = 36; + else + break; + saveallocation = us->srb->cmnd[4]; + us->srb->cmnd[4] = 36; + break; + + case MODE_SENSE: + if (us->srb->request_bufflen > 4) + us->srb->request_bufflen = 4; + else + break; + saveallocation = us->srb->cmnd[4]; + us->srb->cmnd[4] = 4; + break; + + case LOG_SENSE: + case MODE_SENSE_10: + if (us->srb->request_bufflen > 8) + us->srb->request_bufflen = 8; + else + break; + saveallocation = (us->srb->cmnd[7] << 8) | us->srb->cmnd[8]; + us->srb->cmnd[7] = 0; + us->srb->cmnd[8] = 8; + break; + + default: + break; + } us->srb->result = us->pop(us->srb); + + if (savelen != us->srb->request_bufflen && + us->srb->result == (DID_OK << 16)) { + unsigned char *p = (unsigned char *)us->srb->request_buffer; + unsigned int length; + + /* set correct length and retry */ + switch (us->srb->cmnd[0]) { + case REQUEST_SENSE: + /* simply return 18 bytes */ + p[7] = 10; + length = us->srb->request_bufflen;; + break; + + case INQUIRY: + length = p[4] + 5 > savelen ? savelen : p[4] + 5; + us->srb->cmnd[4] = length; + break; + + case MODE_SENSE: + length = p[0] + 4 > savelen ? savelen : p[0] + 4; + us->srb->cmnd[4] = 4; + break; + + case LOG_SENSE: + length = ((p[2] << 8) + p[3]) + 4 > savelen ? savelen : ((p[2] << 8) + p[3]) + 4; + us->srb->cmnd[7] = length >> 8; + us->srb->cmnd[8] = length; + break; + + case MODE_SENSE_10: + length = ((p[0] << 8) + p[1]) + 8 > savelen ? savelen : ((p[0] << 8) + p[1]) + 8; + us->srb->cmnd[7] = length >> 8; + us->srb->cmnd[8] = length; + break; + } + + US_DEBUGP("Old/New length = %d/%d\n", savelen, length); + + if (us->srb->request_bufflen != length) { + us->srb->request_bufflen = length; + us->srb->result = us->pop(us->srb); + } + /* reset back to original values */ + + us->srb->request_bufflen = savelen; + switch (us->srb->cmnd[0]) { + case REQUEST_SENSE: + case INQUIRY: + case MODE_SENSE: + us->srb->cmnd[4] = saveallocation; + break; + + case LOG_SENSE: + case MODE_SENSE_10: + us->srb->cmnd[7] = saveallocation >> 8; + us->srb->cmnd[8] = saveallocation; + break; + } + } + /* force attention on first command */ + if (!us->attention_done) { + if (us->srb->cmnd[0] == REQUEST_SENSE) { + if (us->srb->result == (DID_OK << 16)) { + unsigned char *p = (unsigned char *)us->srb->request_buffer; + + us->attention_done = 1; + if ((p[2] & 0x0f) != UNIT_ATTENTION) { + p[2] = UNIT_ATTENTION; + p[12] = 0x29; /* power on, reset or bus-reset */ + p[13] = 0; + } + } + } else if (us->srb->cmnd[0] != INQUIRY && + us->srb->result == (DID_OK << 16)) { + us->srb->result |= 2; /* force check condition */ + } + } + } } us->srb->scsi_done(us->srb); + us->srb = NULL; break; case US_ACT_ABORT : @@ -820,7 +1069,7 @@ if (dev->descriptor.idVendor == 0x04e6 && dev->descriptor.idProduct == 0x0001) { /* shuttle E-USB */ - protocol = US_PR_ZIP; + protocol = US_PR_CB; subclass = US_SC_8070; /* an assumption */ } else if (dev->descriptor.bDeviceClass != 0 || dev->config->altsetting->interface->bInterfaceClass != 8 || @@ -835,11 +1084,15 @@ usb_string(dev, dev->descriptor.iSerialNumber) ) { make_guid(guid, dev->descriptor.idVendor, dev->descriptor.idProduct, usb_string(dev, dev->descriptor.iSerialNumber)); - for (ss = us_list; ss; ss = ss->next) { - if (GUID_EQUAL(guid, ss->guid)) { - US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); - break; - } + } else { + make_guid(guid, dev->descriptor.idVendor, dev->descriptor.idProduct, + "0"); + } + for (ss = us_list; ss; ss = ss->next) { + if (!ss->pusb_dev && GUID_EQUAL(guid, ss->guid)) { + US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); + flags = ss->flags; + break; } } } @@ -865,6 +1118,7 @@ ss->subclass = interface->bInterfaceSubClass; ss->protocol = interface->bInterfaceProtocol; } + ss->attention_done = 0; /* set the protocol op */ @@ -873,16 +1127,19 @@ case US_PR_CB: US_DEBUGPX("Control/Bulk\n"); ss->pop = pop_CBI; + ss->pop_reset = pop_CB_reset; break; case US_PR_CBI: US_DEBUGPX("Control/Bulk/Interrupt\n"); ss->pop = pop_CBI; + ss->pop_reset = pop_CB_reset; break; default: US_DEBUGPX("Bulk\n"); ss->pop = pop_Bulk; + ss->pop_reset = pop_Bulk_reset; break; } @@ -907,6 +1164,7 @@ /* exit if strange looking */ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue) || + usb_set_interface(dev, interface->bInterfaceNumber, 0) || !ss->ep_in || !ss->ep_out || (ss->protocol == US_PR_CBI && ss->ep_int == 0)) { US_DEBUGP("Problems with device\n"); if (ss->host) { @@ -933,13 +1191,8 @@ /* make unique id if possible */ - if (dev->descriptor.iSerialNumber && - usb_string(dev, dev->descriptor.iSerialNumber) ) { - make_guid(ss->guid, dev->descriptor.idVendor, dev->descriptor.idProduct, - usb_string(dev, dev->descriptor.iSerialNumber)); - } - US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); + memcpy(ss->guid, guid, sizeof(guid)); /* set class specific stuff */ @@ -986,8 +1239,29 @@ (struct us_data *)htmplt->proc_dir = ss; - if (ss->protocol == US_PR_CBI) + + if (dev->descriptor.idVendor == 0x04e6 && + dev->descriptor.idProduct == 0x0001) { + devrequest dr; + __u8 qstat[2]; + + /* shuttle E-USB */ + dr.requesttype = 0xC0; + dr.request = 1; + dr.index = 0; + dr.value = 0; + dr.length = 0; + ss->pusb_dev->bus->op->control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0), &dr, qstat, 2); + US_DEBUGP("C0 status %x %x\n", qstat[0], qstat[1]); + init_waitqueue_head(&ss->ip_waitq); + ss->pusb_dev->bus->op->request_irq(ss->pusb_dev, + usb_rcvctrlpipe(ss->pusb_dev, ss->ep_int), + pop_CBI_irq, 0, (void *)ss); + interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*5); + + } else if (ss->protocol == US_PR_CBI) init_waitqueue_head(&ss->ip_waitq); + /* start up our thread */ diff -u --recursive --new-file v2.3.6/linux/drivers/usb/usb_scsi_debug.c linux/drivers/usb/usb_scsi_debug.c --- v2.3.6/linux/drivers/usb/usb_scsi_debug.c Mon Jun 7 20:04:01 1999 +++ linux/drivers/usb/usb_scsi_debug.c Sun Jun 20 11:43:42 1999 @@ -95,7 +95,7 @@ case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break; case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break; case WRITE_LONG_2: what = "WRITE_LONG_2"; break; - default: what = "??"; break; + default: break; } printk(KERN_DEBUG USB_SCSI "Command %s (%d bytes)\n", what, srb->cmd_len); printk(KERN_DEBUG USB_SCSI " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", diff -u --recursive --new-file v2.3.6/linux/drivers/video/acornfb.c linux/drivers/video/acornfb.c --- v2.3.6/linux/drivers/video/acornfb.c Tue May 11 16:30:45 1999 +++ linux/drivers/video/acornfb.c Thu Jun 17 01:11:35 1999 @@ -254,7 +254,9 @@ bandwidth = var->pixclock * 8 / var->bits_per_pixel; /* 25.175, 4bpp = 79.444ns per byte, 317.776ns per word: fifo = 2,6 */ - if (bandwidth > 71750) + if (bandwidth > 143500) + vidc_ctl |= VIDC_CTRL_FIFO_3_7; + else if (bandwidth > 71750) vidc_ctl |= VIDC_CTRL_FIFO_2_6; else if (bandwidth > 35875) vidc_ctl |= VIDC_CTRL_FIFO_1_5; diff -u --recursive --new-file v2.3.6/linux/drivers/video/cyber2000fb.c linux/drivers/video/cyber2000fb.c --- v2.3.6/linux/drivers/video/cyber2000fb.c Tue May 11 16:30:45 1999 +++ linux/drivers/video/cyber2000fb.c Thu Jun 17 01:11:35 1999 @@ -121,7 +121,7 @@ 1600, 1200, { 0xff, 0xc7, 0xc9, 0x9f, 0xcf, 0xa0, 0xfe, 0x10, - 0x00, 0x40, + 0x00, 0x40, 0xcf, 0x89, 0xaf, 0xc8, 0x00, 0xbc, 0xf1, 0xe3 }, 0x1f, @@ -154,54 +154,54 @@ debug_printf("init vga hw for %dx%d\n", res->xres, res->yres); cyber2000_outb(0xef, 0x3c2); - cyber2000_crtcw(0x0b, 0x11); - cyber2000_attrw(0x00, 0x11); + cyber2000_crtcw(0x11, 0x0b); + cyber2000_attrw(0x11, 0x00); - cyber2000_seqw(0x01, 0x00); + cyber2000_seqw(0x00, 0x01); cyber2000_seqw(0x01, 0x01); - cyber2000_seqw(0x0f, 0x02); - cyber2000_seqw(0x00, 0x03); - cyber2000_seqw(0x0e, 0x04); + cyber2000_seqw(0x02, 0x0f); cyber2000_seqw(0x03, 0x00); + cyber2000_seqw(0x04, 0x0e); + cyber2000_seqw(0x00, 0x03); for (i = 0; i < sizeof(crtc_idx); i++) - cyber2000_crtcw(res->crtc_regs[i], crtc_idx[i]); + cyber2000_crtcw(crtc_idx[i], res->crtc_regs[i]); for (i = 0x0a; i < 0x10; i++) - cyber2000_crtcw(0, i); + cyber2000_crtcw(i, 0); - cyber2000_crtcw(0xff, 0x18); + cyber2000_crtcw(0x18, 0xff); cyber2000_grphw(0x00, 0x00); - cyber2000_grphw(0x00, 0x01); - cyber2000_grphw(0x00, 0x02); - cyber2000_grphw(0x00, 0x03); - cyber2000_grphw(0x00, 0x04); - cyber2000_grphw(0x60, 0x05); - cyber2000_grphw(0x05, 0x06); - cyber2000_grphw(0x0f, 0x07); - cyber2000_grphw(0xff, 0x08); + cyber2000_grphw(0x01, 0x00); + cyber2000_grphw(0x02, 0x00); + cyber2000_grphw(0x03, 0x00); + cyber2000_grphw(0x04, 0x00); + cyber2000_grphw(0x05, 0x60); + cyber2000_grphw(0x06, 0x05); + cyber2000_grphw(0x07, 0x0f); + cyber2000_grphw(0x08, 0xff); for (i = 0; i < 16; i++) cyber2000_attrw(i, i); - cyber2000_attrw(0x01, 0x10); - cyber2000_attrw(0x00, 0x11); - cyber2000_attrw(0x0f, 0x12); - cyber2000_attrw(0x00, 0x13); - cyber2000_attrw(0x00, 0x14); + cyber2000_attrw(0x10, 0x01); + cyber2000_attrw(0x11, 0x00); + cyber2000_attrw(0x12, 0x0f); + cyber2000_attrw(0x13, 0x00); + cyber2000_attrw(0x14, 0x00); for (i = 0; i < sizeof(igs_regs); i += 2) - cyber2000_grphw(igs_regs[i+1], igs_regs[i]); + cyber2000_grphw(igs_regs[i], igs_regs[i+1]); - cyber2000_grphw(res->crtc_ofl, 0x11); + cyber2000_grphw(0x11, res->crtc_ofl); for (i = 0; i < 4; i += 1) - cyber2000_grphw(res->clk_regs[i], 0xb0 + i); + cyber2000_grphw(0xb0 + i, res->clk_regs[i]); - cyber2000_grphw(0x01, 0x90); - cyber2000_grphw(0x80, 0xb9); - cyber2000_grphw(0x00, 0xb9); + cyber2000_grphw(0x90, 0x01); + cyber2000_grphw(0xb9, 0x80); + cyber2000_grphw(0xb9, 0x00); cyber2000_outb(0x56, 0x3ce); i = cyber2000_inb(0x3cf); @@ -311,6 +311,7 @@ cyber2000_outw(height, 0xbf062); switch (p->var.bits_per_pixel) { + case 15: case 16: bgx = ((u16 *)p->dispsw_data)[bgx]; case 8: @@ -415,14 +416,27 @@ current_par.palette[regno].blue = blue; switch (fb_display[current_par.currcon].var.bits_per_pixel) { +#ifdef FBCON_HAS_CFB8 case 8: cyber2000_outb(regno, 0x3c8); cyber2000_outb(red, 0x3c9); cyber2000_outb(green, 0x3c9); cyber2000_outb(blue, 0x3c9); break; +#endif #ifdef FBCON_HAS_CFB16 + case 15: + if (regno < 32) { + cyber2000_outb(regno << 3, 0x3c8); + cyber2000_outb(red, 0x3c9); + cyber2000_outb(green, 0x3c9); + cyber2000_outb(blue, 0x3c9); + } + if (regno < 16) + current_par.c_table.cfb16[regno] = regno | regno << 5 | regno << 10; + break; + case 16: if (regno < 64) { /* write green */ @@ -464,36 +478,123 @@ return 0; } -static int cyber2000fb_set_timing(struct fb_var_screeninfo *var) +static void cyber2000fb_calculate_timing(unsigned char *v, struct fb_var_screeninfo *var) { - int width = var->xres_virtual; - int scr_pitch, fetchrow; - int i; - char b, col; + int Htotal, Hdispend, Hblankstart, Hblankend, Hsyncstart, Hsyncend; + int Vtotal, Vdispend, Vblankstart, Vblankend, Vsyncstart, Vsyncend; +#define BIT(v,b1,m,b2) (((v >> b1) & m) << b2) + + Hdispend = var->xres; + Hsyncstart = var->xres + var->right_margin; + Hsyncend = var->xres + var->right_margin + var->hsync_len; + Htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin; + + Hblankstart = var->xres; + Hblankend = Htotal - 4*8; + + Vdispend = var->yres; + Vsyncstart = var->yres + var->lower_margin; + Vsyncend = var->yres + var->lower_margin + var->vsync_len; + Vtotal = var->yres + var->lower_margin + var->vsync_len + var->upper_margin; + + Vblankstart = var->yres + 7; + Vblankend = Vtotal - 11; + + Hdispend >>= 3; + Hsyncstart >>= 3; + Hsyncend >>= 3; + Htotal >>= 3; + Hblankstart >>= 3; + Hblankend >>= 3; + + Htotal -= 5; + Hdispend -= 1; + Vtotal -= 2; + Vdispend -= 1; + Vblankstart -= 1; + Vblankend -= 1; + + v[0] = Htotal; + v[1] = Hdispend; + v[2] = Hblankstart; + v[3] = BIT(Hblankend, 0, 0x1f, 0) | + BIT(1, 0, 0x01, 7); + v[4] = Hsyncstart; + v[5] = BIT(Hsyncend, 0, 0x1f, 0) | + BIT(Hblankend, 5, 0x01, 7); + + v[6] = Vtotal; + v[7] = BIT(Vtotal, 8, 0x01, 0) | + BIT(Vdispend, 8, 0x01, 1) | + BIT(Vsyncstart, 8, 0x01, 2) | + BIT(Vblankstart,8, 0x01, 3) | + BIT(1, 0, 0x01, 4) | + BIT(Vtotal, 9, 0x01, 5) | + BIT(Vdispend, 9, 0x01, 6) | + BIT(Vsyncstart, 9, 0x01, 7); + v[8] = 0; + v[9] = BIT(0, 0, 0x1f, 0) | + BIT(Vblankstart,9, 0x01, 5) | + BIT(1, 0, 0x01, 6); + v[10] = Vsyncstart; + v[11] = BIT(Vsyncend, 0, 0x0f, 0) | + BIT(1, 0, 0x01, 7); + v[12] = Vdispend; + v[14] = 0; + v[15] = Vblankstart; + v[16] = Vblankend; + v[17] = 0xe3; + + /* overflow - graphics reg 0x11 */ + v[18] = BIT(Vtotal, 10, 0x01, 0) | /* guess */ + BIT(Vdispend, 10, 0x01, 1) | + BIT(Vsyncstart, 10, 0x01, 2) | /* guess */ + BIT(Vblankstart,10, 0x01, 3) | /* guess */ + BIT(Hblankend, 6, 0x01, 4); /* guess */ +} + +static void cyber2000fb_set_timing(struct fb_var_screeninfo *var) +{ + unsigned int width = var->xres_virtual; + unsigned int scr_pitch, fetchrow, i; + char b, graph_r77, crtc[32]; switch (var->bits_per_pixel) { case 8: /* PSEUDOCOLOUR, 256 */ b = 0; - col = 1; - scr_pitch = var->xres_virtual / 8; + graph_r77 = 1; + scr_pitch = width; + break; + + case 15:/* DIRECTCOLOUR, 32k */ + b = 1; + graph_r77 = 6; + scr_pitch = width * 2; break; case 16:/* DIRECTCOLOUR, 64k */ b = 1; - col = 2; - scr_pitch = var->xres_virtual / 8 * 2; + graph_r77 = 2; + scr_pitch = width * 2; break; + case 24:/* TRUECOLOUR, 16m */ b = 2; - col = 4; - scr_pitch = var->xres_virtual / 8 * 3; + graph_r77 = 4; width *= 3; + scr_pitch = width; break; default: - return 1; + return; } + width -= 1; + scr_pitch >>= 3; + fetchrow = scr_pitch + 1; + + cyber2000fb_calculate_timing(crtc, var); + for (i = 0; i < NUM_TOTAL_MODES; i++) if (var->xres == cyber2000_res[i].xres && var->yres == cyber2000_res[i].yres) @@ -502,30 +603,41 @@ if (i < NUM_TOTAL_MODES) cyber2000_init_hw(cyber2000_res + i); - fetchrow = scr_pitch + 1; + crtc[13] = scr_pitch; - debug_printf("Setting regs: pitch=%X, fetchrow=%X, col=%X, b=%X\n", - scr_pitch, fetchrow, col, b); + /* + * reprogram the CRTC with the values we calculated + * above. This should be cleaned up once we're + * confident that we're generating the correct + * values. Disable this if you're having problems, + * and report the values obtained from the kernel + * messages. + */ +#if 1 + cyber2000_crtcw(0x11, 0x0b); + for (i = 0; i < sizeof(crtc_idx); i++) + cyber2000_crtcw(crtc_idx[i], crtc[i]); +#else + cyber2000_crtcw(0x13, crtc[13]); +#endif - cyber2000_outb(0x13, 0x3d4); - cyber2000_outb(scr_pitch, 0x3d5); - cyber2000_outb(0x14, 0x3ce); - cyber2000_outb(fetchrow, 0x3cf); - cyber2000_outb(0x15, 0x3ce); + cyber2000_grphw(0x14, fetchrow); /* FIXME: is this the right way round? */ - cyber2000_outb(((fetchrow >> 4) & 0xf0) | ((scr_pitch >> 8) & 0x0f), 0x3cf); - cyber2000_outb(0x77, 0x3ce); - cyber2000_outb(col, 0x3cf); - - - cyber2000_outb(0x33, 0x3ce); - cyber2000_outb(0x1c, 0x3cf); - - cyber2000_outw(width - 1, 0xbf018); - cyber2000_outw(width - 1, 0xbf218); - cyber2000_outb(b, 0xbf01c); - - return 0; + cyber2000_grphw(0x15, ((fetchrow >> 4) & 0xf0) | ((scr_pitch >> 8) & 0x0f)); + cyber2000_grphw(0x77, graph_r77); + cyber2000_grphw(0x33, 0x1c); + + cyber2000_outw(width, 0xbf018); + cyber2000_outw(width, 0xbf218); + cyber2000_outb(b, 0xbf01c); + +{ int j; + printk(KERN_DEBUG); + for (j = 0; j < 19; j++) printk("%2d ", j); printk("\n"KERN_DEBUG); + for (j = 0; j < 19; j++) printk("%02X ", crtc[j]); printk("\n"KERN_DEBUG); + for (j = 0; j < 18; j++) printk("%02X ", cyber2000_res[i].crtc_regs[j]); + printk("%02X\n", cyber2000_res[i].crtc_ofl); +} } static inline void @@ -536,13 +648,10 @@ base = var->yoffset * var->xres_virtual + var->xoffset; - cyber2000_outb(0x0c, 0x3d4); - cyber2000_outb(base, 0x3d5); - cyber2000_outb(0x0d, 0x3d4); - cyber2000_outb(base >> 8, 0x3d5); + cyber2000_crtcw(0x0c, base); + cyber2000_crtcw(0x0d, base >> 8); /* FIXME: need the upper bits of the start offset */ -/* cyber2000_outb(0x??, 0x3d4); - cyber2000_outb(base >> 16, 0x3d5);*/ +/* cyber2000_crtcw(0x??, base >> 16);*/ #endif } @@ -622,6 +731,7 @@ break; #endif #ifdef FBCON_HAS_CFB16 + case 15: case 16: *visual = FB_VISUAL_DIRECTCOLOR; break; @@ -737,6 +847,10 @@ } display->var = *var; + display->var.activate &= ~FB_ACTIVATE_ALL; + + if (var->activate & FB_ACTIVATE_ALL) + global_disp.var = display->var; display->screen_base = (char *)current_par.screen_base; display->visual = visual; @@ -744,8 +858,6 @@ display->type_aux = 0; display->ypanstep = 0; display->ywrapstep = 0; - display->line_length = - display->next_line = (var->xres_virtual * var->bits_per_pixel) / 8; display->can_soft_blank = 1; display->inverse = 0; @@ -754,18 +866,22 @@ case 8: dispsw = &fbcon_cfb8; display->dispsw_data = NULL; + display->next_line = var->xres_virtual; break; #endif #ifdef FBCON_HAS_CFB16 + case 15: case 16: dispsw = &fbcon_cfb16; display->dispsw_data = current_par.c_table.cfb16; + display->next_line = var->xres_virtual * 2; break; #endif #ifdef FBCON_HAS_CFB24 case 24: dispsw = &fbcon_cfb24; display->dispsw_data = current_par.c_table.cfb24; + display->next_line = var->xres_virtual * 3; break; #endif default: @@ -775,6 +891,8 @@ break; } + display->line_length = display->next_line; + if (display->var.accel_flags & FB_ACCELF_TEXT && dispsw != &fbcon_dummy) display->dispsw = &fbcon_cyber_accel; @@ -818,6 +936,7 @@ return -EINVAL; if (y_bottom > fb_display[con].var.yres_virtual) return -EINVAL; +/*disabled until we can update the start address properly */ return -EINVAL; cyber2000fb_update_start(var); @@ -947,12 +1066,26 @@ init_var.yres = DEFAULT_YRES; init_var.bits_per_pixel = DEFAULT_BPP; + /* + * These parameters give + * 640x480, hsync 31.5kHz, vsync 60Hz + */ + init_var.left_margin = 56; + init_var.right_margin = 16; + init_var.upper_margin = 34; + init_var.lower_margin = 9; + init_var.hsync_len = 88; + init_var.vsync_len = 2; + init_var.pixclock = 39722; + init_var.red.msb_right = 0; init_var.green.msb_right = 0; init_var.blue.msb_right = 0; switch(init_var.bits_per_pixel) { - case 8: + default: + init_var.bits_per_pixel = 8; + case 8: /* PSEUDOCOLOUR */ init_var.bits_per_pixel = 8; init_var.red.offset = 0; init_var.red.length = 8; @@ -962,7 +1095,17 @@ init_var.blue.length = 8; break; - case 16: + case 15: /* RGB555 */ + init_var.bits_per_pixel = 15; + init_var.red.offset = 10; + init_var.red.length = 5; + init_var.green.offset = 5; + init_var.green.length = 5; + init_var.blue.offset = 0; + init_var.blue.length = 5; + break; + + case 16: /* RGB565 */ init_var.bits_per_pixel = 16; init_var.red.offset = 11; init_var.red.length = 5; @@ -972,7 +1115,7 @@ init_var.blue.length = 5; break; - case 24: + case 24: /* RGB888 */ init_var.bits_per_pixel = 24; init_var.red.offset = 16; init_var.red.length = 8; diff -u --recursive --new-file v2.3.6/linux/drivers/video/cyber2000fb.h linux/drivers/video/cyber2000fb.h --- v2.3.6/linux/drivers/video/cyber2000fb.h Tue May 11 16:30:45 1999 +++ linux/drivers/video/cyber2000fb.h Thu Jun 17 01:11:35 1999 @@ -13,19 +13,19 @@ #define cyber2000_inw(reg) (*(unsigned short *)&CyberRegs[reg]) #define cyber2000_inl(reg) (*(unsigned long *)&CyberRegs[reg]) -static inline void cyber2000_crtcw(int val, int reg) +static inline void cyber2000_crtcw(int reg, int val) { cyber2000_outb(reg, 0x3d4); cyber2000_outb(val, 0x3d5); } -static inline void cyber2000_grphw(int val, int reg) +static inline void cyber2000_grphw(int reg, int val) { cyber2000_outb(reg, 0x3ce); cyber2000_outb(val, 0x3cf); } -static inline void cyber2000_attrw(int val, int reg) +static inline void cyber2000_attrw(int reg, int val) { cyber2000_inb(0x3da); cyber2000_outb(reg, 0x3c0); @@ -33,7 +33,7 @@ cyber2000_outb(val, 0x3c0); } -static inline void cyber2000_seqw(int val, int reg) +static inline void cyber2000_seqw(int reg, int val) { cyber2000_outb(reg, 0x3c4); cyber2000_outb(val, 0x3c5); diff -u --recursive --new-file v2.3.6/linux/drivers/video/vgacon.c linux/drivers/video/vgacon.c --- v2.3.6/linux/drivers/video/vgacon.c Tue May 11 16:30:45 1999 +++ linux/drivers/video/vgacon.c Wed Jun 16 19:26:27 1999 @@ -135,9 +135,17 @@ */ static inline void write_vga(unsigned char reg, unsigned int val) { -#ifndef SLOW_VGA unsigned int v1, v2; + unsigned long flags; + + /* + * ddprintk might set the console position from interrupt + * handlers, thus the write has to be IRQ-atomic. + */ + save_flags(flags); + cli(); +#ifndef SLOW_VGA v1 = reg + (val & 0xff00); v2 = reg + 1 + ((val << 8) & 0xff00); outw(v1, vga_video_port_reg); @@ -148,6 +156,7 @@ outb_p(reg+1, vga_video_port_reg); outb_p(val & 0xff, vga_video_port_val); #endif + restore_flags(flags); } __initfunc(static const char *vgacon_startup(void)) diff -u --recursive --new-file v2.3.6/linux/fs/Config.in linux/fs/Config.in --- v2.3.6/linux/fs/Config.in Wed Jun 9 18:42:08 1999 +++ linux/fs/Config.in Wed Jun 16 19:26:27 1999 @@ -90,9 +90,6 @@ fi fi tristate 'SMB filesystem support (to mount WfW shares etc.)' CONFIG_SMB_FS - if [ "$CONFIG_SMB_FS" != "n" ]; then - bool 'SMB Win95 bug work-around' CONFIG_SMB_WIN95 - fi fi if [ "$CONFIG_IPX" != "n" -o "$CONFIG_INET" != "n" ]; then tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS diff -u --recursive --new-file v2.3.6/linux/fs/affs/dir.c linux/fs/affs/dir.c --- v2.3.6/linux/fs/affs/dir.c Fri Apr 23 21:20:37 1999 +++ linux/fs/affs/dir.c Sat Jun 19 11:45:28 1999 @@ -63,7 +63,6 @@ NULL, /* truncate */ NULL, /* permissions */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/affs/file.c linux/fs/affs/file.c --- v2.3.6/linux/fs/affs/file.c Mon Aug 24 13:02:44 1998 +++ linux/fs/affs/file.c Sat Jun 19 11:45:28 1999 @@ -80,7 +80,6 @@ affs_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; @@ -121,7 +120,6 @@ affs_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/autofs/dir.c linux/fs/autofs/dir.c --- v2.3.6/linux/fs/autofs/dir.c Thu May 13 10:53:59 1999 +++ linux/fs/autofs/dir.c Sat Jun 19 11:45:28 1999 @@ -79,7 +79,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/autofs/root.c linux/fs/autofs/root.c --- v2.3.6/linux/fs/autofs/root.c Thu May 13 10:53:59 1999 +++ linux/fs/autofs/root.c Sat Jun 19 11:45:29 1999 @@ -60,7 +60,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/autofs/symlink.c linux/fs/autofs/symlink.c --- v2.3.6/linux/fs/autofs/symlink.c Tue Oct 27 14:13:53 1998 +++ linux/fs/autofs/symlink.c Sat Jun 19 11:45:29 1999 @@ -55,6 +55,5 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/bad_inode.c linux/fs/bad_inode.c --- v2.3.6/linux/fs/bad_inode.c Mon Sep 21 14:37:20 1998 +++ linux/fs/bad_inode.c Sat Jun 19 11:45:28 1999 @@ -60,13 +60,13 @@ EIO_ERROR, /* rename */ EIO_ERROR, /* readlink */ bad_follow_link, /* follow_link */ + EIO_ERROR, /* bmap */ EIO_ERROR, /* readpage */ EIO_ERROR, /* writepage */ - EIO_ERROR, /* bmap */ + EIO_ERROR, /* flushpage */ EIO_ERROR, /* truncate */ EIO_ERROR, /* permission */ EIO_ERROR, /* smap */ - EIO_ERROR, /* update_page */ EIO_ERROR /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/binfmt_aout.c linux/fs/binfmt_aout.c --- v2.3.6/linux/fs/binfmt_aout.c Tue Jun 8 23:01:35 1999 +++ linux/fs/binfmt_aout.c Fri Jun 18 08:00:52 1999 @@ -59,11 +59,7 @@ static int dump_write(struct file *file, const void *addr, int nr) { - int r; - down(&file->f_dentry->d_inode->i_sem); - r = file->f_op->write(file, addr, nr, &file->f_pos) == nr; - up(&file->f_dentry->d_inode->i_sem); - return r; + return file->f_op->write(file, addr, nr, &file->f_pos) == nr; } #define DUMP_WRITE(addr, nr) \ diff -u --recursive --new-file v2.3.6/linux/fs/binfmt_elf.c linux/fs/binfmt_elf.c --- v2.3.6/linux/fs/binfmt_elf.c Thu Jun 3 23:15:29 1999 +++ linux/fs/binfmt_elf.c Fri Jun 18 08:01:10 1999 @@ -918,11 +918,7 @@ */ static int dump_write(struct file *file, const void *addr, int nr) { - int r; - down(&file->f_dentry->d_inode->i_sem); - r = file->f_op->write(file, addr, nr, &file->f_pos) == nr; - up(&file->f_dentry->d_inode->i_sem); - return r; + return file->f_op->write(file, addr, nr, &file->f_pos) == nr; } static int dump_seek(struct file *file, off_t off) diff -u --recursive --new-file v2.3.6/linux/fs/block_dev.c linux/fs/block_dev.c --- v2.3.6/linux/fs/block_dev.c Fri May 28 09:20:31 1999 +++ linux/fs/block_dev.c Sun Jun 20 01:09:22 1999 @@ -124,6 +124,7 @@ } buffercount=0; } + balance_dirty(dev); if(write_error) break; } diff -u --recursive --new-file v2.3.6/linux/fs/buffer.c linux/fs/buffer.c --- v2.3.6/linux/fs/buffer.c Tue Jun 8 10:47:58 1999 +++ linux/fs/buffer.c Sun Jun 20 15:58:20 1999 @@ -24,6 +24,8 @@ * - RMK */ +#include +#include #include #include #include @@ -113,7 +115,7 @@ /* These are the min and max parameter values that we will allow to be assigned */ int bdflush_min[N_PARAM] = { 0, 10, 5, 25, 0, 1*HZ, 1*HZ, 1, 1}; -int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 600*HZ, 600*HZ, 2047, 5}; +int bdflush_max[N_PARAM] = {100,50000, 20000, 20000,1000, 6000*HZ, 6000*HZ, 2047, 5}; void wakeup_bdflush(int); @@ -422,7 +424,25 @@ #define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block)) & bh_hash_mask) #define hash(dev,block) hash_table[_hashfn(dev,block)] -static inline void remove_from_hash_queue(struct buffer_head * bh) +static void insert_into_hash_list(struct buffer_head * bh) +{ + bh->b_next = NULL; + bh->b_pprev = NULL; + if (bh->b_dev) { + struct buffer_head **bhp = &hash(bh->b_dev, bh->b_blocknr); + struct buffer_head *next = *bhp; + + if (next) { + bh->b_next = next; + next->b_pprev = &bh->b_next; + } + *bhp = bh; + bh->b_pprev = bhp; + nr_hashed_buffers++; + } +} + +static void remove_from_hash_queue(struct buffer_head * bh) { struct buffer_head **pprev = bh->b_pprev; if (pprev) { @@ -433,16 +453,43 @@ } *pprev = next; bh->b_pprev = NULL; + nr_hashed_buffers--; } - nr_hashed_buffers--; } -static inline void remove_from_lru_list(struct buffer_head * bh) +static void insert_into_lru_list(struct buffer_head * bh) { - if (!(bh->b_prev_free) || !(bh->b_next_free)) - panic("VFS: LRU block list corrupted"); + struct buffer_head **bhp = &lru_list[bh->b_list]; + if (bh->b_dev == B_FREE) - panic("LRU list corrupted"); + BUG(); + + if(!*bhp) { + *bhp = bh; + bh->b_prev_free = bh; + } + + if (bh->b_next_free) + panic("VFS: buffer LRU pointers corrupted"); + + bh->b_next_free = *bhp; + bh->b_prev_free = (*bhp)->b_prev_free; + (*bhp)->b_prev_free->b_next_free = bh; + (*bhp)->b_prev_free = bh; + + nr_buffers++; + nr_buffers_type[bh->b_list]++; +} + +static void remove_from_lru_list(struct buffer_head * bh) +{ + if (!(bh->b_prev_free) || !(bh->b_next_free)) + return; + + if (bh->b_dev == B_FREE) { + printk("LRU list corrupted"); + *(int*)0 = 0; + } bh->b_prev_free->b_next_free = bh->b_next_free; bh->b_next_free->b_prev_free = bh->b_prev_free; @@ -451,9 +498,12 @@ if (lru_list[bh->b_list] == bh) lru_list[bh->b_list] = NULL; bh->b_next_free = bh->b_prev_free = NULL; + + nr_buffers--; + nr_buffers_type[bh->b_list]--; } -static inline void remove_from_free_list(struct buffer_head * bh) +static void remove_from_free_list(struct buffer_head * bh) { int isize = BUFSIZE_INDEX(bh->b_size); if (!(bh->b_prev_free) || !(bh->b_next_free)) @@ -475,21 +525,20 @@ static void remove_from_queues(struct buffer_head * bh) { - if(bh->b_dev == B_FREE) { - remove_from_free_list(bh); /* Free list entries should not be - in the hash queue */ - return; - } - nr_buffers_type[bh->b_list]--; + if (bh->b_dev == B_FREE) + BUG(); remove_from_hash_queue(bh); remove_from_lru_list(bh); } -static inline void put_last_free(struct buffer_head * bh) +static void put_last_free(struct buffer_head * bh) { if (bh) { struct buffer_head **bhp = &free_list[BUFSIZE_INDEX(bh->b_size)]; + if (bh->b_count) + BUG(); + bh->b_dev = B_FREE; /* So it is obvious we are on the free list. */ /* Add to back of free list. */ @@ -505,47 +554,6 @@ } } -static void insert_into_queues(struct buffer_head * bh) -{ - /* put at end of free list */ - if(bh->b_dev == B_FREE) { - put_last_free(bh); - } else { - struct buffer_head **bhp = &lru_list[bh->b_list]; - - if(!*bhp) { - *bhp = bh; - bh->b_prev_free = bh; - } - - if (bh->b_next_free) - panic("VFS: buffer LRU pointers corrupted"); - - bh->b_next_free = *bhp; - bh->b_prev_free = (*bhp)->b_prev_free; - (*bhp)->b_prev_free->b_next_free = bh; - (*bhp)->b_prev_free = bh; - - nr_buffers_type[bh->b_list]++; - - /* Put the buffer in new hash-queue if it has a device. */ - bh->b_next = NULL; - bh->b_pprev = NULL; - if (bh->b_dev) { - struct buffer_head **bhp = &hash(bh->b_dev, bh->b_blocknr); - struct buffer_head *next = *bhp; - - if (next) { - bh->b_next = next; - next->b_pprev = &bh->b_next; - } - *bhp = bh; - bh->b_pprev = bhp; - } - nr_hashed_buffers++; - } -} - struct buffer_head * find_buffer(kdev_t dev, int block, int size) { struct buffer_head * next; @@ -636,6 +644,7 @@ if (bh->b_size == size) continue; bhnext->b_count++; + bh->b_count++; wait_on_buffer(bh); bhnext->b_count--; if (bh->b_dev == dev && bh->b_size != size) { @@ -644,9 +653,10 @@ clear_bit(BH_Req, &bh->b_state); bh->b_flushtime = 0; } + if (--bh->b_count) + continue; remove_from_queues(bh); - bh->b_dev=B_FREE; - insert_into_queues(bh); + put_last_free(bh); } } } @@ -666,7 +676,6 @@ void init_buffer(struct buffer_head *bh, kdev_t dev, int block, bh_end_io_t *handler, void *dev_id) { - bh->b_count = 1; bh->b_list = BUF_CLEAN; bh->b_flushtime = 0; bh->b_dev = dev; @@ -702,7 +711,7 @@ if (!buffer_dirty(bh)) { bh->b_flushtime = 0; } - return bh; + goto out; } isize = BUFSIZE_INDEX(size); @@ -716,9 +725,13 @@ * and that it's unused (b_count=0), unlocked, and clean. */ init_buffer(bh, dev, block, end_buffer_io_sync, NULL); - bh->b_state=0; - insert_into_queues(bh); - return bh; + bh->b_count = 1; + bh->b_state = 0; + + /* Insert the buffer into the regular lists */ + insert_into_lru_list(bh); + insert_into_hash_list(bh); + goto out; /* * If we block while refilling the free list, somebody may @@ -729,6 +742,8 @@ if (!find_buffer(dev,block,size)) goto get_free; goto repeat; +out: + return bh; } void set_writetime(struct buffer_head * buf, int flag) @@ -746,15 +761,56 @@ } } - /* * Put a buffer into the appropriate list, without side-effects. */ -static inline void file_buffer(struct buffer_head *bh, int list) +static void file_buffer(struct buffer_head *bh, int list) { - remove_from_queues(bh); + remove_from_lru_list(bh); bh->b_list = list; - insert_into_queues(bh); + insert_into_lru_list(bh); +} + +/* + * if a new dirty buffer is created we need to balance bdflush. + * + * in the future we might want to make bdflush aware of different + * pressures on different devices - thus the (currently unused) + * 'dev' parameter. + */ +void balance_dirty(kdev_t dev) +{ + int dirty = nr_buffers_type[BUF_DIRTY]; + int ndirty = bdf_prm.b_un.ndirty; + + if (dirty > ndirty) { + int wait = 0; + if (dirty > 2*ndirty) + wait = 1; + wakeup_bdflush(wait); + } +} + +atomic_t too_many_dirty_buffers; + +static inline void __mark_dirty(struct buffer_head *bh, int flag) +{ + set_writetime(bh, flag); + refile_buffer(bh); + if (atomic_read(&too_many_dirty_buffers)) + balance_dirty(bh->b_dev); +} + +void __mark_buffer_dirty(struct buffer_head *bh, int flag) +{ + __mark_dirty(bh, flag); +} + +void __atomic_mark_buffer_dirty(struct buffer_head *bh, int flag) +{ + lock_kernel(); + __mark_dirty(bh, flag); + unlock_kernel(); } /* @@ -765,36 +821,19 @@ { int dispose; - if(buf->b_dev == B_FREE) { + if (buf->b_dev == B_FREE) { printk("Attempt to refile free buffer\n"); return; } + + dispose = BUF_CLEAN; + if (buffer_locked(buf)) + dispose = BUF_LOCKED; if (buffer_dirty(buf)) dispose = BUF_DIRTY; - else if (buffer_locked(buf)) - dispose = BUF_LOCKED; - else - dispose = BUF_CLEAN; - if(dispose != buf->b_list) { - file_buffer(buf, dispose); - if(dispose == BUF_DIRTY) { - int too_many = (nr_buffers * bdf_prm.b_un.nfract/100); - - /* This buffer is dirty, maybe we need to start flushing. - * If too high a percentage of the buffers are dirty... - */ - if (nr_buffers_type[BUF_DIRTY] > too_many) - wakeup_bdflush(1); - /* If this is a loop device, and - * more than half of the buffers are dirty... - * (Prevents no-free-buffers deadlock with loop device.) - */ - if (MAJOR(buf->b_dev) == LOOP_MAJOR && - nr_buffers_type[BUF_DIRTY]*2>nr_buffers) - wakeup_bdflush(1); - } - } + if (dispose != buf->b_list) + file_buffer(buf, dispose); } /* @@ -809,6 +848,7 @@ if (buf->b_count) { buf->b_count--; + wake_up(&buffer_wait); return; } printk("VFS: brelse: Trying to free free buffer\n"); @@ -890,7 +930,6 @@ /* if (blocks) printk("breada (new) %d blocks\n",blocks); */ - bhlist[0] = bh; j = 1; for(i=1; ib_blocknr = -1; init_waitqueue_head(&bh->b_wait); nr_unused_buffer_heads++; bh->b_next_free = unused_list; @@ -1153,17 +1193,12 @@ struct page *page; mark_buffer_uptodate(bh, uptodate); - unlock_buffer(bh); /* This is a temporary buffer used for page I/O. */ page = mem_map + MAP_NR(bh->b_data); - if (!PageLocked(page)) - goto not_locked; - if (bh->b_count != 1) - goto bad_count; - if (!test_bit(BH_Uptodate, &bh->b_state)) - set_bit(PG_error, &page->flags); + if (!uptodate) + SetPageError(page); /* * Be _very_ careful from here on. Bad things can happen if @@ -1179,69 +1214,63 @@ */ save_flags(flags); cli(); - bh->b_count--; - tmp = bh; - do { - if (tmp->b_count) + unlock_buffer(bh); + tmp = bh->b_this_page; + while (tmp != bh) { + if (buffer_locked(tmp)) goto still_busy; tmp = tmp->b_this_page; - } while (tmp != bh); + } /* OK, the async IO on this page is complete. */ - free_async_buffers(bh); restore_flags(flags); - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + after_unlock_page(page); + /* + * if none of the buffers had errors then we can set the + * page uptodate: + */ + if (!PageError(page)) + SetPageUptodate(page); + if (page->owner != -1) + PAGE_BUG(page); + page->owner = (int)current; + UnlockPage(page); + return; still_busy: restore_flags(flags); return; - -not_locked: - printk ("Whoops: end_buffer_io_async: async io complete on unlocked page\n"); - return; - -bad_count: - printk ("Whoops: end_buffer_io_async: b_count != 1 on async io.\n"); - return; } -/* - * Start I/O on a page. - * This function expects the page to be locked and may return before I/O is complete. - * You then have to check page->locked, page->uptodate, and maybe wait on page->wait. - */ -int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) +static int create_page_buffers (int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) { - struct buffer_head *bh, *prev, *next, *arr[MAX_BUF_PER_PAGE]; - int block, nr; + struct buffer_head *head, *bh, *tail; + int block; if (!PageLocked(page)) - panic("brw_page: page not locked for I/O"); - clear_bit(PG_uptodate, &page->flags); - clear_bit(PG_error, &page->flags); + BUG(); + if (page->owner != (int)current) + PAGE_BUG(page); /* * Allocate async buffer heads pointing to this page, just for I/O. - * They do _not_ show up in the buffer hash table! - * They are _not_ registered in page->buffers either! + * They show up in the buffer hash table and are registered in + * page->buffers. */ - bh = create_buffers(page_address(page), size, 1); - if (!bh) { - /* WSH: exit here leaves page->count incremented */ - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); - return -ENOMEM; - } - nr = 0; - next = bh; - do { - struct buffer_head * tmp; + lock_kernel(); + head = create_buffers(page_address(page), size, 1); + unlock_kernel(); + if (page->buffers) + BUG(); + if (!head) + BUG(); + tail = head; + for (bh = head; bh; bh = bh->b_this_page) { block = *(b++); - init_buffer(next, dev, block, end_buffer_io_async, NULL); - set_bit(BH_Uptodate, &next->b_state); + tail = bh; + init_buffer(bh, dev, block, end_buffer_io_async, NULL); /* * When we use bmap, we define block zero to represent @@ -1250,51 +1279,379 @@ * two cases. */ if (bmap && !block) { - memset(next->b_data, 0, size); - next->b_count--; - continue; + set_bit(BH_Uptodate, &bh->b_state); + memset(bh->b_data, 0, size); + } + } + tail->b_this_page = head; + get_page(page); + page->buffers = head; + return 0; +} + +/* + * We don't have to release all buffers here, but + * we have to be sure that no dirty buffer is left + * and no IO is going on (no buffer is locked), because + * we have truncated the file and are going to free the + * blocks on-disk.. + */ +int block_flushpage(struct inode *inode, struct page *page, unsigned long offset) +{ + struct buffer_head *head, *bh, *next; + unsigned int curr_off = 0; + + if (!PageLocked(page)) + BUG(); + if (!page->buffers) + return 0; + lock_kernel(); + + head = page->buffers; + bh = head; + do { + unsigned int next_off = curr_off + bh->b_size; + next = bh->b_this_page; + + /* + * is this block fully flushed? + */ + if (offset <= curr_off) { + if (bh->b_blocknr) { + bh->b_count++; + wait_on_buffer(bh); + if (bh->b_dev == B_FREE) + BUG(); + mark_buffer_clean(bh); + bh->b_blocknr = 0; + bh->b_count--; + } + } + curr_off = next_off; + bh = next; + } while (bh != head); + + /* + * subtle. We release buffer-heads only if this is + * the 'final' flushpage. We have invalidated the bmap + * cached value unconditionally, so real IO is not + * possible anymore. + */ + if (!offset) + try_to_free_buffers(page); + + unlock_kernel(); + return 0; +} + +static void create_empty_buffers (struct page *page, + struct inode *inode, unsigned long blocksize) +{ + struct buffer_head *bh, *head, *tail; + + lock_kernel(); + head = create_buffers(page_address(page), blocksize, 1); + unlock_kernel(); + if (page->buffers) + BUG(); + + bh = head; + do { + bh->b_dev = inode->i_dev; + bh->b_blocknr = 0; + tail = bh; + bh = bh->b_this_page; + } while (bh); + tail->b_this_page = head; + page->buffers = head; + get_page(page); +} + +/* + * block_write_full_page() is SMP-safe - currently it's still + * being called with the kernel lock held, but the code is ready. + */ +int block_write_full_page (struct file *file, struct page *page, fs_getblock_t fs_get_block) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + int err, created, i; + unsigned long block, phys, offset; + struct buffer_head *bh, *head; + + if (!PageLocked(page)) + BUG(); + + if (!page->buffers) + create_empty_buffers(page, inode, inode->i_sb->s_blocksize); + head = page->buffers; + + offset = page->offset; + block = offset >> inode->i_sb->s_blocksize_bits; + + // FIXME: currently we assume page alignment. + if (offset & (PAGE_SIZE-1)) + BUG(); + + bh = head; + i = 0; + do { + if (!bh) + BUG(); + + if (!bh->b_blocknr) { + err = -EIO; + down(&inode->i_sem); + phys = fs_get_block (inode, block, 1, &err, &created); + up(&inode->i_sem); + if (!phys) + goto out; + + init_buffer(bh, inode->i_dev, phys, end_buffer_io_sync, NULL); + bh->b_state = (1<b_end_io = end_buffer_io_sync; + set_bit(BH_Uptodate, &bh->b_state); } - tmp = get_hash_table(dev, block, size); - if (tmp) { - if (!buffer_uptodate(tmp)) { - if (rw == READ) - ll_rw_block(READ, 1, &tmp); - wait_on_buffer(tmp); + atomic_mark_buffer_dirty(bh,0); + + bh = bh->b_this_page; + block++; + } while (bh != head); + + SetPageUptodate(page); + return 0; +out: + ClearPageUptodate(page); + return err; +} + +int block_write_partial_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf, fs_getblock_t fs_get_block) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + unsigned long block; + int err, created, partial; + unsigned long blocksize, start_block, end_block; + unsigned long start_offset, start_bytes, end_bytes; + unsigned long bbits, phys, blocks, i, len; + struct buffer_head *bh, *head; + char * target_buf; + + target_buf = (char *)page_address(page) + offset; + + if (!PageLocked(page)) + BUG(); + + blocksize = inode->i_sb->s_blocksize; + if (!page->buffers) + create_empty_buffers(page, inode, blocksize); + head = page->buffers; + + bbits = inode->i_sb->s_blocksize_bits; + block = page->offset >> bbits; + blocks = PAGE_SIZE >> bbits; + start_block = offset >> bbits; + end_block = (offset + bytes - 1) >> bbits; + start_offset = offset & (blocksize - 1); + start_bytes = blocksize - start_offset; + if (start_bytes > bytes) + start_bytes = bytes; + end_bytes = (offset+bytes) & (blocksize - 1); + if (end_bytes > bytes) + end_bytes = bytes; + + if (offset < 0 || offset >= PAGE_SIZE) + BUG(); + if (bytes+offset < 0 || bytes+offset > PAGE_SIZE) + BUG(); + if (start_block < 0 || start_block >= blocks) + BUG(); + if (end_block < 0 || end_block >= blocks) + BUG(); + // FIXME: currently we assume page alignment. + if (page->offset & (PAGE_SIZE-1)) + BUG(); + + i = 0; + bh = head; + partial = 0; + do { + if (!bh) + BUG(); + + if ((i < start_block) || (i > end_block)) { + if (!buffer_uptodate(bh)) + partial = 1; + goto skip; + } + if (!bh->b_blocknr) { + err = -EIO; + down(&inode->i_sem); + phys = fs_get_block (inode, block, 1, &err, &created); + up(&inode->i_sem); + if (!phys) + goto out; + + init_buffer(bh, inode->i_dev, phys, end_buffer_io_sync, NULL); + + /* + * if partially written block which has contents on + * disk, then we have to read it first. + * We also rely on the fact that filesystem holes + * cannot be written. + */ + if (!created && (start_offset || + (end_bytes && (i == end_block)))) { + bh->b_state = 0; + ll_rw_block(READ, 1, &bh); + lock_kernel(); + wait_on_buffer(bh); + unlock_kernel(); + err = -EIO; + if (!buffer_uptodate(bh)) + goto out; } - if (rw == READ) - memcpy(next->b_data, tmp->b_data, size); - else { - memcpy(tmp->b_data, next->b_data, size); - mark_buffer_dirty(tmp, 0); + + bh->b_state = (1<b_end_io = end_buffer_io_sync; + set_bit(BH_Uptodate, &bh->b_state); + } + + err = -EFAULT; + if (start_offset) { + len = start_bytes; + start_offset = 0; + } else + if (end_bytes && (i == end_block)) { + len = end_bytes; + end_bytes = 0; + } else { + /* + * Overwritten block. + */ + len = blocksize; + } + if (copy_from_user(target_buf, buf, len)) + goto out; + target_buf += len; + buf += len; + + /* + * we dirty buffers only after copying the data into + * the page - this way we can dirty the buffer even if + * the bh is still doing IO. + */ + atomic_mark_buffer_dirty(bh,0); +skip: + i++; + block++; + bh = bh->b_this_page; + } while (bh != head); + + /* + * is this a partial write that happened to make all buffers + * uptodate then we can optimize away a bogus readpage() for + * the next read(). Here we 'discover' wether the page went + * uptodate as a result of this (potentially partial) write. + */ + if (!partial) + SetPageUptodate(page); + return bytes; +out: + ClearPageUptodate(page); + return err; +} + +/* + * Start I/O on a page. + * This function expects the page to be locked and may return + * before I/O is complete. You then have to check page->locked, + * page->uptodate, and maybe wait on page->wait. + * + * brw_page() is SMP-safe, although it's being called with the + * kernel lock held - but the code is ready. + */ +int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap) +{ + struct buffer_head *head, *bh, *arr[MAX_BUF_PER_PAGE]; + int nr, fresh /* temporary debugging flag */, block; + + if (!PageLocked(page)) + panic("brw_page: page not locked for I/O"); +// clear_bit(PG_error, &page->flags); + /* + * We pretty much rely on the page lock for this, because + * create_page_buffers() might sleep. + */ + fresh = 0; + if (!page->buffers) { + create_page_buffers(rw, page, dev, b, size, bmap); + fresh = 1; + } + if (!page->buffers) + BUG(); + page->owner = -1; + + head = page->buffers; + bh = head; + nr = 0; + do { + block = *(b++); + + if (fresh && (bh->b_count != 0)) + BUG(); + if (rw == READ) { + if (!fresh) + BUG(); + if (bmap && !block) { + if (block) + BUG(); + } else { + if (bmap && !block) + BUG(); + if (!buffer_uptodate(bh)) { + arr[nr++] = bh; + } } - brelse(tmp); - next->b_count--; - continue; + } else { /* WRITE */ + if (!bh->b_blocknr) { + if (!block) + BUG(); + bh->b_blocknr = block; + } else { + if (!block) + BUG(); + } + set_bit(BH_Uptodate, &bh->b_state); + atomic_mark_buffer_dirty(bh, 0); + arr[nr++] = bh; } - if (rw == READ) - clear_bit(BH_Uptodate, &next->b_state); - else - set_bit(BH_Dirty, &next->b_state); - arr[nr++] = next; - } while (prev = next, (next = next->b_this_page) != NULL); - prev->b_this_page = bh; - - if (nr) { + bh = bh->b_this_page; + } while (bh != head); + if (rw == READ) + ++current->maj_flt; + if ((rw == READ) && nr) { + if (Page_Uptodate(page)) + BUG(); ll_rw_block(rw, nr, arr); - /* The rest of the work is done in mark_buffer_uptodate() - * and unlock_buffer(). */ } else { - unsigned long flags; - clear_bit(PG_locked, &page->flags); - set_bit(PG_uptodate, &page->flags); - wake_up(&page->wait); - save_flags(flags); - cli(); - free_async_buffers(bh); - restore_flags(flags); - after_unlock_page(page); + if (!nr && rw == READ) { + SetPageUptodate(page); + page->owner = (int)current; + UnlockPage(page); + } + if (nr && (rw == WRITE)) + ll_rw_block(rw, nr, arr); } - ++current->maj_flt; return 0; } @@ -1305,6 +1662,7 @@ { if (on) { struct buffer_head *tmp = bh; + struct page *page; set_bit(BH_Uptodate, &bh->b_state); /* If a page has buffers and all these buffers are uptodate, * then the page is uptodate. */ @@ -1313,7 +1671,8 @@ return; tmp=tmp->b_this_page; } while (tmp && tmp != bh); - set_bit(PG_uptodate, &mem_map[MAP_NR(bh->b_data)].flags); + page = mem_map + MAP_NR(bh->b_data); + SetPageUptodate(page); return; } clear_bit(BH_Uptodate, &bh->b_state); @@ -1326,30 +1685,70 @@ * mark_buffer_uptodate() functions propagate buffer state into the * page struct once IO has completed. */ -int generic_readpage(struct file * file, struct page * page) +int block_read_full_page(struct file * file, struct page * page) { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - unsigned long block; - int *p, nr[PAGE_SIZE/512]; - int i; + unsigned long iblock, phys_block; + struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE]; + unsigned int blocksize, blocks; + int nr; - atomic_inc(&page->count); - set_bit(PG_locked, &page->flags); - set_bit(PG_free_after, &page->flags); - - i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; - block = page->offset >> inode->i_sb->s_blocksize_bits; - p = nr; + if (!PageLocked(page)) + PAGE_BUG(page); + blocksize = inode->i_sb->s_blocksize; + if (!page->buffers) + create_empty_buffers(page, inode, blocksize); + head = page->buffers; + + blocks = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; + iblock = page->offset >> inode->i_sb->s_blocksize_bits; + page->owner = -1; + head = page->buffers; + bh = head; + nr = 0; do { - *p = inode->i_op->bmap(inode, block); - i--; - block++; - p++; - } while (i > 0); + phys_block = bh->b_blocknr; + /* + * important, we have to retry buffers that already have + * their bnr cached but had an IO error! + */ + if (!buffer_uptodate(bh)) { + phys_block = inode->i_op->bmap(inode, iblock); + /* + * this is safe to do because we hold the page lock: + */ + if (phys_block) { + init_buffer(bh, inode->i_dev, phys_block, + end_buffer_io_async, NULL); + arr[nr] = bh; + nr++; + } else { + /* + * filesystem 'hole' represents zero-contents: + */ + memset(bh->b_data, 0, blocksize); + set_bit(BH_Uptodate, &bh->b_state); + } + } + iblock++; + bh = bh->b_this_page; + } while (bh != head); - /* IO start */ - brw_page(READ, page, inode->i_dev, nr, inode->i_sb->s_blocksize, 1); + ++current->maj_flt; + if (nr) { + if (Page_Uptodate(page)) + BUG(); + ll_rw_block(READ, nr, arr); + } else { + /* + * all buffers are uptodate - we can set the page + * uptodate as well. + */ + SetPageUptodate(page); + page->owner = (int)current; + UnlockPage(page); + } return 0; } @@ -1392,7 +1791,6 @@ tmp->b_next_free = tmp; } insert_point = tmp; - ++nr_buffers; if (tmp->b_this_page) tmp = tmp->b_this_page; else @@ -1409,7 +1807,7 @@ * Can the buffer be thrown out? */ #define BUFFER_BUSY_BITS ((1<b_count || ((bh)->b_state & BUFFER_BUSY_BITS)) +#define buffer_busy(bh) ((bh)->b_count || ((bh)->b_state & BUFFER_BUSY_BITS)) /* * try_to_free_buffers() checks if all the buffers on this particular page @@ -1418,9 +1816,9 @@ * Wake up bdflush() if this fails - if we're running low on memory due * to dirty buffers, we need to flush them out as quickly as possible. */ -int try_to_free_buffers(struct page * page_map) +int try_to_free_buffers(struct page * page) { - struct buffer_head * tmp, * bh = page_map->buffers; + struct buffer_head * tmp, * bh = page->buffers; tmp = bh; do { @@ -1429,8 +1827,6 @@ tmp = tmp->b_this_page; if (!buffer_busy(p)) continue; - - wakeup_bdflush(0); return 0; } while (tmp != bh); @@ -1438,8 +1834,13 @@ do { struct buffer_head * p = tmp; tmp = tmp->b_this_page; - nr_buffers--; - remove_from_queues(p); + + /* The buffer can be either on the regular queues or on the free list.. */ + if (p->b_dev == B_FREE) + remove_from_free_list(p); + else + remove_from_queues(p); + put_unused_buffer_head(p); } while (tmp != bh); @@ -1447,10 +1848,12 @@ wake_up(&buffer_wait); /* And free the page */ - buffermem -= PAGE_SIZE; - page_map->buffers = NULL; - __free_page(page_map); - return 1; + page->buffers = NULL; + if (__free_page(page)) { + buffermem -= PAGE_SIZE; + return 1; + } + return 0; } /* ================== Debugging =================== */ @@ -1509,11 +1912,11 @@ the heuristic from working with large databases and getting fsync times (ext2) manageable, is the following */ - memory_size >>= 20; + memory_size >>= 22; for (order = 5; (1UL << order) < memory_size; order++); /* try to allocate something until we get it or we're asking - for something that is really too small */ + for something that is really too small */ do { nr_hash = (1UL << order) * PAGE_SIZE / @@ -1521,6 +1924,7 @@ hash_table = (struct buffer_head **) __get_free_pages(GFP_ATOMIC, order); } while (hash_table == NULL && --order > 4); + printk("buffer-cache hash table entries: %d (order: %d, %ld bytes)\n", nr_hash, order, (1UL<b_count--; next->b_count--; + wake_up(&buffer_wait); } } #ifdef DEBUG @@ -1818,9 +2223,14 @@ run_task_queue(&tq_disk); wake_up(&bdflush_done); - /* If there are still a lot of dirty buffers around, skip the sleep - and flush some more */ - if(ndirty == 0 || nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) { + /* + * If there are still a lot of dirty buffers around, + * skip the sleep and flush some more + */ + if ((ndirty == 0) || (nr_buffers_type[BUF_DIRTY] <= + nr_buffers * bdf_prm.b_un.nfract/100)) { + + atomic_set(&too_many_dirty_buffers, 0); spin_lock_irq(¤t->sigmask_lock); flush_signals(current); spin_unlock_irq(¤t->sigmask_lock); diff -u --recursive --new-file v2.3.6/linux/fs/devices.c linux/fs/devices.c --- v2.3.6/linux/fs/devices.c Sat May 15 23:43:04 1999 +++ linux/fs/devices.c Sat Jun 19 11:45:28 1999 @@ -277,11 +277,14 @@ NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* diff -u --recursive --new-file v2.3.6/linux/fs/devpts/root.c linux/fs/devpts/root.c --- v2.3.6/linux/fs/devpts/root.c Sat May 8 17:56:37 1999 +++ linux/fs/devpts/root.c Sat Jun 19 11:45:29 1999 @@ -57,7 +57,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/ext2/balloc.c linux/fs/ext2/balloc.c --- v2.3.6/linux/fs/ext2/balloc.c Wed Oct 28 21:54:56 1998 +++ linux/fs/ext2/balloc.c Wed Jun 16 19:26:27 1999 @@ -358,7 +358,7 @@ * bitmap, and then for any free bit if that fails. */ int ext2_new_block (const struct inode * inode, unsigned long goal, - u32 * prealloc_count, u32 * prealloc_block, int * err) + u32 * prealloc_count, u32 * prealloc_block, int * err) { struct buffer_head * bh; struct buffer_head * bh2; @@ -594,20 +594,12 @@ if (j >= le32_to_cpu(es->s_blocks_count)) { ext2_error (sb, "ext2_new_block", - "block >= blocks count - " - "block_group = %d, block=%d", i, j); + "block(%d) >= blocks count(%d) - " + "block_group = %d, es == %p ",j, + le32_to_cpu(es->s_blocks_count), i, es); unlock_super (sb); return 0; } - if (!(bh = getblk (sb->s_dev, j, sb->s_blocksize))) { - ext2_error (sb, "ext2_new_block", "cannot get block %d", j); - unlock_super (sb); - return 0; - } - memset(bh->b_data, 0, sb->s_blocksize); - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); - brelse (bh); ext2_debug ("allocating block %d. " "Goal hits %d of %d.\n", j, goal_hits, goal_attempts); diff -u --recursive --new-file v2.3.6/linux/fs/ext2/dir.c linux/fs/ext2/dir.c --- v2.3.6/linux/fs/ext2/dir.c Fri Apr 23 21:20:37 1999 +++ linux/fs/ext2/dir.c Sat Jun 19 11:45:28 1999 @@ -67,12 +67,14 @@ ext2_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ ext2_permission, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; int ext2_check_dir_entry (const char * function, struct inode * dir, diff -u --recursive --new-file v2.3.6/linux/fs/ext2/file.c linux/fs/ext2/file.c --- v2.3.6/linux/fs/ext2/file.c Mon Dec 21 15:22:54 1998 +++ linux/fs/ext2/file.c Sun Jun 20 01:09:22 1999 @@ -30,15 +30,15 @@ #include #include #include +#include #define NBUF 32 #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) +static int ext2_writepage (struct file * file, struct page * page); static long long ext2_file_lseek(struct file *, long long, int); -static ssize_t ext2_file_write (struct file *, const char *, size_t, loff_t *); -static int ext2_release_file (struct inode *, struct file *); #if BITS_PER_LONG < 64 static int ext2_open_file (struct inode *, struct file *); @@ -57,51 +57,6 @@ #endif -/* - * We have mostly NULL's here: the current defaults are ok for - * the ext2 filesystem. - */ -static struct file_operations ext2_file_operations = { - ext2_file_lseek, /* lseek */ - generic_file_read, /* read */ - ext2_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - ext2_ioctl, /* ioctl */ - generic_file_mmap, /* mmap */ -#if BITS_PER_LONG == 64 - NULL, /* no special open is needed */ -#else - ext2_open_file, -#endif - NULL, /* flush */ - ext2_release_file, /* release */ - ext2_sync_file, /* fsync */ - NULL, /* fasync */ - NULL, /* check_media_change */ - NULL /* revalidate */ -}; - -struct inode_operations ext2_file_inode_operations = { - &ext2_file_operations,/* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - generic_readpage, /* readpage */ - NULL, /* writepage */ - ext2_bmap, /* bmap */ - ext2_truncate, /* truncate */ - ext2_permission, /* permission */ - NULL /* smap */ -}; /* * Make sure the offset never goes beyond the 32-bit mark.. @@ -151,164 +106,30 @@ } } -static ssize_t ext2_file_write (struct file * filp, const char * buf, - size_t count, loff_t *ppos) +static int ext2_writepage (struct file * file, struct page * page) { - struct inode * inode = filp->f_dentry->d_inode; - off_t pos; - long block; - int offset; - int written, c; - struct buffer_head * bh, *bufferlist[NBUF]; - struct super_block * sb; - int err; - int i,buffercount,write_error; - - /* POSIX: mtime/ctime may not change for 0 count */ - if (!count) - return 0; - write_error = buffercount = 0; - if (!inode) { - printk("ext2_file_write: inode = NULL\n"); - return -EINVAL; - } - sb = inode->i_sb; - if (sb->s_flags & MS_RDONLY) - /* - * This fs has been automatically remounted ro because of errors - */ - return -ENOSPC; - - if (!S_ISREG(inode->i_mode)) { - ext2_warning (sb, "ext2_file_write", "mode = %07o", - inode->i_mode); - return -EINVAL; - } - remove_suid(inode); - - if (filp->f_flags & O_APPEND) - pos = inode->i_size; - else { - pos = *ppos; - if (pos != *ppos) - return -EINVAL; -#if BITS_PER_LONG >= 64 - if (pos > ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(sb)]) - return -EINVAL; -#endif - } + return block_write_full_page(file, page, ext2_getblk_block); +} - /* Check for overflow.. */ -#if BITS_PER_LONG < 64 - if (pos > (__u32) (pos + count)) { - count = ~pos; /* == 0xFFFFFFFF - pos */ - if (!count) - return -EFBIG; - } -#else - { - off_t max = ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(sb)]; +static long ext2_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +{ + return block_write_partial_page(file, page, offset, bytes, buf, ext2_getblk_block); +} - if (pos + count > max) { - count = max - pos; - if (!count) - return -EFBIG; - } - if (((pos + count) >> 32) && - !(sb->u.ext2_sb.s_es->s_feature_ro_compat & - cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE))) { - /* If this is the first large file created, add a flag - to the superblock */ - sb->u.ext2_sb.s_es->s_feature_ro_compat |= - cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE); - mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); - } +/* + * Write to a file (through the page cache). + */ +static ssize_t +ext2_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + ssize_t retval = generic_file_write(file, buf, count, ppos, ext2_write_one_page); + if (retval > 0) { + struct inode *inode = file->f_dentry->d_inode; + remove_suid(inode); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + mark_inode_dirty(inode); } -#endif - - /* - * If a file has been opened in synchronous mode, we have to ensure - * that meta-data will also be written synchronously. Thus, we - * set the i_osync field. This field is tested by the allocation - * routines. - */ - if (filp->f_flags & O_SYNC) - inode->u.ext2_i.i_osync++; - block = pos >> EXT2_BLOCK_SIZE_BITS(sb); - offset = pos & (sb->s_blocksize - 1); - c = sb->s_blocksize - offset; - written = 0; - do { - bh = ext2_getblk (inode, block, 1, &err); - if (!bh) { - if (!written) - written = err; - break; - } - if (c > count) - c = count; - if (c != sb->s_blocksize && !buffer_uptodate(bh)) { - ll_rw_block (READ, 1, &bh); - wait_on_buffer (bh); - if (!buffer_uptodate(bh)) { - brelse (bh); - if (!written) - written = -EIO; - break; - } - } - c -= copy_from_user (bh->b_data + offset, buf, c); - if (!c) { - brelse(bh); - if (!written) - written = -EFAULT; - break; - } - update_vm_cache(inode, pos, bh->b_data + offset, c); - pos += c; - written += c; - buf += c; - count -= c; - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); - - if (filp->f_flags & O_SYNC) - bufferlist[buffercount++] = bh; - else - brelse(bh); - if (buffercount == NBUF){ - ll_rw_block(WRITE, buffercount, bufferlist); - for(i=0; is_blocksize; - } while (count); - if ( buffercount ){ - ll_rw_block(WRITE, buffercount, bufferlist); - for(i=0; i inode->i_size) - inode->i_size = pos; - if (filp->f_flags & O_SYNC) - inode->u.ext2_i.i_osync--; - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - *ppos = pos; - mark_inode_dirty(inode); - return written; + return retval; } /* @@ -335,3 +156,51 @@ return 0; } #endif + +/* + * We have mostly NULL's here: the current defaults are ok for + * the ext2 filesystem. + */ +static struct file_operations ext2_file_operations = { + ext2_file_lseek, /* lseek */ + generic_file_read, /* read */ + ext2_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* poll - default */ + ext2_ioctl, /* ioctl */ + generic_file_mmap, /* mmap */ +#if BITS_PER_LONG == 64 + NULL, /* no special open is needed */ +#else + ext2_open_file, +#endif + NULL, /* flush */ + ext2_release_file, /* release */ + ext2_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ +}; + +struct inode_operations ext2_file_inode_operations = { + &ext2_file_operations,/* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + ext2_bmap, /* bmap */ + block_read_full_page, /* readpage */ + ext2_writepage, /* writepage */ + block_flushpage, /* flushpage */ + ext2_truncate, /* truncate */ + ext2_permission, /* permission */ + NULL, /* smap */ + NULL, /* revalidate */ +}; diff -u --recursive --new-file v2.3.6/linux/fs/ext2/fsync.c linux/fs/ext2/fsync.c --- v2.3.6/linux/fs/ext2/fsync.c Wed May 20 13:09:12 1998 +++ linux/fs/ext2/fsync.c Fri Jun 18 12:58:30 1999 @@ -17,6 +17,9 @@ * Removed unnecessary code duplication for little endian machines * and excessive __inline__s. * Andi Kleen, 1997 + * + * Major simplications and cleanup - we only need to do the metadata, because + * we can depend on generic_block_fdatasync() to sync the data blocks. */ #include @@ -32,221 +35,84 @@ #include -#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb)) -#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb)) +#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb)) +#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb)) -static int sync_block (struct inode * inode, u32 * block, int wait) +static int sync_indirect(struct inode * inode, u32 * block, int wait) { struct buffer_head * bh; if (!*block) return 0; - bh = get_hash_table (inode->i_dev, *block, blocksize); + bh = get_hash_table(inode->i_dev, le32_to_cpu(*block), blocksize); if (!bh) return 0; if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { - brelse (bh); + brelse(bh); return -1; } if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { - brelse (bh); + brelse(bh); return 0; } - ll_rw_block (WRITE, 1, &bh); + ll_rw_block(WRITE, 1, &bh); bh->b_count--; return 0; } -#ifndef __LITTLE_ENDIAN -static int sync_block_swab32 (struct inode * inode, u32 * block, int wait) -{ - struct buffer_head * bh; - - if (!le32_to_cpu(*block)) - return 0; - bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize); - if (!bh) - return 0; - if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { - brelse (bh); - return -1; - } - if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { - brelse (bh); - return 0; - } - ll_rw_block (WRITE, 1, &bh); - bh->b_count--; - return 0; -} -#else -#define sync_block_swab32 sync_block -#endif - - -static int sync_iblock (struct inode * inode, u32 * iblock, +static int sync_iblock(struct inode * inode, u32 * iblock, struct buffer_head ** bh, int wait) { int rc, tmp; *bh = NULL; - tmp = *iblock; - if (!tmp) - return 0; - rc = sync_block (inode, iblock, wait); - if (rc) - return rc; - *bh = bread (inode->i_dev, tmp, blocksize); - if (!*bh) - return -1; - return 0; -} - -#ifndef __LITTLE_ENDIAN -static int sync_iblock_swab32 (struct inode * inode, u32 * iblock, - struct buffer_head ** bh, int wait) -{ - int rc, tmp; - - *bh = NULL; tmp = le32_to_cpu(*iblock); if (!tmp) return 0; - rc = sync_block_swab32 (inode, iblock, wait); + rc = sync_indirect(inode, iblock, wait); if (rc) return rc; - *bh = bread (inode->i_dev, tmp, blocksize); + *bh = bread(inode->i_dev, tmp, blocksize); if (!*bh) return -1; return 0; } -#else -#define sync_iblock_swab32 sync_iblock -#endif -static int sync_direct (struct inode * inode, int wait) -{ - int i; - int rc, err = 0; - - for (i = 0; i < EXT2_NDIR_BLOCKS; i++) { - rc = sync_block (inode, inode->u.ext2_i.i_data + i, wait); - if (rc) - err = rc; - } - return err; -} - -static int sync_indirect (struct inode * inode, u32 * iblock, int wait) -{ - int i; - struct buffer_head * ind_bh; - int rc, err = 0; - - rc = sync_iblock (inode, iblock, &ind_bh, wait); - if (rc || !ind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_block_swab32 (inode, - ((u32 *) ind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (ind_bh); - return err; -} - -#ifndef __LITTLE_ENDIAN -static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock, int wait) -{ - int i; - struct buffer_head * ind_bh; - int rc, err = 0; - - rc = sync_iblock_swab32 (inode, iblock, &ind_bh, wait); - if (rc || !ind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_block_swab32 (inode, - ((u32 *) ind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (ind_bh); - return err; -} -#else -#define sync_indirect_swab32 sync_indirect -#endif - -static int sync_dindirect (struct inode * inode, u32 * diblock, int wait) -{ - int i; - struct buffer_head * dind_bh; - int rc, err = 0; - - rc = sync_iblock (inode, diblock, &dind_bh, wait); - if (rc || !dind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_indirect_swab32 (inode, - ((u32 *) dind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (dind_bh); - return err; -} - -#ifndef __LITTLE_ENDIAN -static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock, int wait) +static int sync_dindirect(struct inode * inode, u32 * diblock, int wait) { int i; struct buffer_head * dind_bh; int rc, err = 0; - rc = sync_iblock_swab32 (inode, diblock, &dind_bh, wait); + rc = sync_iblock(inode, diblock, &dind_bh, wait); if (rc || !dind_bh) return rc; for (i = 0; i < addr_per_block; i++) { - rc = sync_indirect_swab32 (inode, - ((u32 *) dind_bh->b_data) + i, - wait); + rc = sync_indirect(inode, ((u32 *) dind_bh->b_data) + i, wait); if (rc) err = rc; } - brelse (dind_bh); + brelse(dind_bh); return err; } -#else -#define sync_dindirect_swab32 sync_dindirect -#endif -static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait) +static int sync_tindirect(struct inode * inode, u32 * tiblock, int wait) { int i; struct buffer_head * tind_bh; int rc, err = 0; - rc = sync_iblock (inode, tiblock, &tind_bh, wait); + rc = sync_iblock(inode, tiblock, &tind_bh, wait); if (rc || !tind_bh) return rc; for (i = 0; i < addr_per_block; i++) { - rc = sync_dindirect_swab32 (inode, - ((u32 *) tind_bh->b_data) + i, - wait); + rc = sync_dindirect(inode, ((u32 *) tind_bh->b_data) + i, wait); if (rc) err = rc; } - brelse (tind_bh); + brelse(tind_bh); return err; } @@ -266,18 +132,19 @@ */ goto skip; + err = generic_buffer_fdatasync(inode, 0, ~0UL); + for (wait=0; wait<=1; wait++) { - err |= sync_direct (inode, wait); - err |= sync_indirect (inode, - inode->u.ext2_i.i_data+EXT2_IND_BLOCK, + err |= sync_indirect(inode, + inode->u.ext2_i.i_data+EXT2_IND_BLOCK, + wait); + err |= sync_dindirect(inode, + inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, + wait); + err |= sync_tindirect(inode, + inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, wait); - err |= sync_dindirect (inode, - inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, - wait); - err |= sync_tindirect (inode, - inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, - wait); } skip: err |= ext2_sync_inode (inode); diff -u --recursive --new-file v2.3.6/linux/fs/ext2/inode.c linux/fs/ext2/inode.c --- v2.3.6/linux/fs/ext2/inode.c Tue May 11 23:01:41 1999 +++ linux/fs/ext2/inode.c Sun Jun 20 15:58:20 1999 @@ -31,6 +31,7 @@ #include #include #include +#include static int ext2_update_inode(struct inode * inode, int do_sync); @@ -59,7 +60,7 @@ ext2_free_inode (inode); } -#define inode_bmap(inode, nr) ((inode)->u.ext2_i.i_data[(nr)]) +#define inode_bmap(inode, nr) (le32_to_cpu((inode)->u.ext2_i.i_data[(nr)])) static inline int block_bmap (struct buffer_head * bh, int nr) { @@ -92,13 +93,12 @@ #endif } -static int ext2_alloc_block (struct inode * inode, unsigned long goal, int * err) +static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err) { #ifdef EXT2FS_DEBUG static unsigned long alloc_hits = 0, alloc_attempts = 0; #endif unsigned long result; - struct buffer_head * bh; wait_on_super (inode->i_sb); @@ -112,19 +112,6 @@ ext2_debug ("preallocation hit (%lu/%lu).\n", ++alloc_hits, ++alloc_attempts); - /* It doesn't matter if we block in getblk() since - we have already atomically allocated the block, and - are only clearing it now. */ - if (!(bh = getblk (inode->i_sb->s_dev, result, - inode->i_sb->s_blocksize))) { - ext2_error (inode->i_sb, "ext2_alloc_block", - "cannot get block %lu", result); - return 0; - } - memset(bh->b_data, 0, inode->i_sb->s_blocksize); - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); - brelse (bh); } else { ext2_discard_prealloc (inode); ext2_debug ("preallocation miss (%lu/%lu).\n", @@ -139,13 +126,76 @@ #else result = ext2_new_block (inode, goal, 0, 0, err); #endif - return result; } int ext2_bmap (struct inode * inode, int block) { + int i, ret; + int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); + int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); + + ret = 0; + lock_kernel(); + if (block < 0) { + ext2_warning (inode->i_sb, "ext2_bmap", "block < 0"); + goto out; + } + if (block >= EXT2_NDIR_BLOCKS + addr_per_block + + (1 << (addr_per_block_bits * 2)) + + ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) { + ext2_warning (inode->i_sb, "ext2_bmap", "block > big"); + goto out; + } + if (block < EXT2_NDIR_BLOCKS) { + ret = inode_bmap (inode, block); + goto out; + } + block -= EXT2_NDIR_BLOCKS; + if (block < addr_per_block) { + i = inode_bmap (inode, EXT2_IND_BLOCK); + if (!i) + goto out; + ret = block_bmap (bread (inode->i_dev, i, + inode->i_sb->s_blocksize), block); + goto out; + } + block -= addr_per_block; + if (block < (1 << (addr_per_block_bits * 2))) { + i = inode_bmap (inode, EXT2_DIND_BLOCK); + if (!i) + goto out; + i = block_bmap (bread (inode->i_dev, i, + inode->i_sb->s_blocksize), + block >> addr_per_block_bits); + if (!i) + goto out; + ret = block_bmap (bread (inode->i_dev, i, + inode->i_sb->s_blocksize), + block & (addr_per_block - 1)); + } + block -= (1 << (addr_per_block_bits * 2)); + i = inode_bmap (inode, EXT2_TIND_BLOCK); + if (!i) + goto out; + i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), + block >> (addr_per_block_bits * 2)); + if (!i) + goto out; + i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), + (block >> addr_per_block_bits) & (addr_per_block - 1)); + if (!i) + goto out; + ret = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), + block & (addr_per_block - 1)); +out: + unlock_kernel(); + return ret; +} + +int ext2_bmap_create (struct inode * inode, int block) +{ int i; int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); @@ -201,7 +251,8 @@ } static struct buffer_head * inode_getblk (struct inode * inode, int nr, - int create, int new_block, int * err) + int create, int new_block, int * err, int metadata, + int *phys_block, int *created) { u32 * p; int tmp, goal = 0; @@ -210,13 +261,18 @@ p = inode->u.ext2_i.i_data + nr; repeat: - tmp = *p; + tmp = le32_to_cpu(*p); if (tmp) { - struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp == *p) - return result; - brelse (result); - goto repeat; + if (metadata) { + struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); + if (tmp == le32_to_cpu(*p)) + return result; + brelse (result); + goto repeat; + } else { + *phys_block = tmp; + return NULL; + } } *err = -EFBIG; if (!create) @@ -244,7 +300,7 @@ if (!goal) { for (tmp = nr - 1; tmp >= 0; tmp--) { if (inode->u.ext2_i.i_data[tmp]) { - goal = inode->u.ext2_i.i_data[tmp]; + goal = le32_to_cpu(inode->u.ext2_i.i_data[tmp]); break; } } @@ -259,13 +315,28 @@ tmp = ext2_alloc_block (inode, goal, err); if (!tmp) return NULL; - result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (*p) { - ext2_free_blocks (inode, tmp, 1); - brelse (result); - goto repeat; + if (metadata) { + result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); + if (*p) { + ext2_free_blocks (inode, tmp, 1); + brelse (result); + goto repeat; + } + memset(result->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + if (*p) { + ext2_free_blocks (inode, tmp, 1); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *err = 0; + *created = 1; } - *p = tmp; + *p = cpu_to_le32(tmp); + inode->u.ext2_i.i_next_alloc_block = new_block; inode->u.ext2_i.i_next_alloc_goal = tmp; inode->i_ctime = CURRENT_TIME; @@ -277,10 +348,17 @@ return result; } +/* + * metadata / data + * possibly create / access + * can fail due to: - not present + * - out of space + * + * NULL return in the data case is mandatory. + */ static struct buffer_head * block_getblk (struct inode * inode, - struct buffer_head * bh, int nr, - int create, int blocksize, - int new_block, int * err) + struct buffer_head * bh, int nr, int create, int blocksize, + int new_block, int * err, int metadata, int *phys_block, int *created) { int tmp, goal = 0; u32 * p; @@ -302,13 +380,19 @@ repeat: tmp = le32_to_cpu(*p); if (tmp) { - result = getblk (bh->b_dev, tmp, blocksize); - if (tmp == le32_to_cpu(*p)) { + if (metadata) { + result = getblk (bh->b_dev, tmp, blocksize); + if (tmp == le32_to_cpu(*p)) { + brelse (bh); + return result; + } + brelse (result); + goto repeat; + } else { + *phys_block = tmp; brelse (bh); - return result; + return NULL; } - brelse (result); - goto repeat; } *err = -EFBIG; if (!create) { @@ -343,7 +427,22 @@ brelse (bh); return NULL; } - result = getblk (bh->b_dev, tmp, blocksize); + if (metadata) { + result = getblk (bh->b_dev, tmp, blocksize); + if (*p) { + ext2_free_blocks (inode, tmp, 1); + brelse (result); + goto repeat; + } + memset(result->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + *phys_block = tmp; + result = NULL; + *err = 0; + *created = 1; + } if (le32_to_cpu(*p)) { ext2_free_blocks (inode, tmp, 1); brelse (result); @@ -364,24 +463,27 @@ return result; } -struct buffer_head * ext2_getblk (struct inode * inode, long block, - int create, int * err) +int ext2_getblk_block (struct inode * inode, long block, + int create, int * err, int * created) { - struct buffer_head * bh; + struct buffer_head * bh, *tmp; unsigned long b; unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); + int phys_block, ret; + lock_kernel(); + ret = 0; *err = -EIO; if (block < 0) { ext2_warning (inode->i_sb, "ext2_getblk", "block < 0"); - return NULL; + goto abort; } if (block > EXT2_NDIR_BLOCKS + addr_per_block + (1 << (addr_per_block_bits * 2)) + ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) { ext2_warning (inode->i_sb, "ext2_getblk", "block > big"); - return NULL; + goto abort; } /* * If this is a sequential block allocation, set the next_alloc_block @@ -398,32 +500,72 @@ inode->u.ext2_i.i_next_alloc_goal++; } - *err = -ENOSPC; + *err = 0; // -ENOSPC; b = block; - if (block < EXT2_NDIR_BLOCKS) - return inode_getblk (inode, block, create, b, err); + *created = 0; + if (block < EXT2_NDIR_BLOCKS) { + /* + * data page. + */ + tmp = inode_getblk (inode, block, create, b, + err, 0, &phys_block, created); + goto out; + } block -= EXT2_NDIR_BLOCKS; if (block < addr_per_block) { - bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err); - return block_getblk (inode, bh, block, create, - inode->i_sb->s_blocksize, b, err); + bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err, 1, NULL, NULL); + tmp = block_getblk (inode, bh, block, create, + inode->i_sb->s_blocksize, b, err, 0, &phys_block, created); + goto out; } block -= addr_per_block; if (block < (1 << (addr_per_block_bits * 2))) { - bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err); + bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err, 1, NULL, NULL); bh = block_getblk (inode, bh, block >> addr_per_block_bits, - create, inode->i_sb->s_blocksize, b, err); - return block_getblk (inode, bh, block & (addr_per_block - 1), - create, inode->i_sb->s_blocksize, b, err); + create, inode->i_sb->s_blocksize, b, err, 1, NULL, NULL); + tmp = block_getblk (inode, bh, block & (addr_per_block - 1), + create, inode->i_sb->s_blocksize, b, err, 0, &phys_block, created); + goto out; } block -= (1 << (addr_per_block_bits * 2)); - bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err); + bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err, 1, NULL,NULL); bh = block_getblk (inode, bh, block >> (addr_per_block_bits * 2), - create, inode->i_sb->s_blocksize, b, err); - bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & (addr_per_block - 1), - create, inode->i_sb->s_blocksize, b, err); - return block_getblk (inode, bh, block & (addr_per_block - 1), create, - inode->i_sb->s_blocksize, b, err); + create, inode->i_sb->s_blocksize, b, err, 1, NULL,NULL); + bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & + (addr_per_block - 1), create, inode->i_sb->s_blocksize, + b, err, 1, NULL,NULL); + tmp = block_getblk (inode, bh, block & (addr_per_block - 1), create, + inode->i_sb->s_blocksize, b, err, 0, &phys_block, created); + +out: + if (!phys_block) + goto abort; + if (*err) + goto abort; + ret = phys_block; +abort: + unlock_kernel(); + return ret; +} + +struct buffer_head * ext2_getblk (struct inode * inode, long block, + int create, int * err) +{ + struct buffer_head *tmp = NULL; + int phys_block; + int created; + + phys_block = ext2_getblk_block (inode, block, create, err, &created); + + if (phys_block) { + tmp = getblk (inode->i_dev, phys_block, inode->i_sb->s_blocksize); + if (created) { + memset(tmp->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(tmp, 1); + mark_buffer_dirty(tmp, 1); + } + } + return tmp; } struct buffer_head * ext2_bread (struct inode * inode, int block, @@ -569,11 +711,14 @@ if (inode->u.ext2_i.i_prealloc_count) ext2_error (inode->i_sb, "ext2_read_inode", "New inode has non-zero prealloc count!"); - if (S_ISLNK(inode->i_mode) && !inode->i_blocks) - for (block = 0; block < EXT2_N_BLOCKS; block++) - inode->u.ext2_i.i_data[block] = raw_inode->i_block[block]; - else for (block = 0; block < EXT2_N_BLOCKS; block++) - inode->u.ext2_i.i_data[block] = le32_to_cpu(raw_inode->i_block[block]); + + /* + * NOTE! The in-memory inode i_blocks array is in little-endian order + * even on big-endian machines: we do NOT byteswap the block numbers! + */ + for (block = 0; block < EXT2_N_BLOCKS; block++) + inode->u.ext2_i.i_data[block] = raw_inode->i_block[block]; + if (inode->i_ino == EXT2_ACL_IDX_INO || inode->i_ino == EXT2_ACL_DATA_INO) /* Nothing to do */ ; @@ -689,11 +834,8 @@ raw_inode->i_generation = cpu_to_le32(inode->i_generation); if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev)); - else if (S_ISLNK(inode->i_mode) && !inode->i_blocks) - for (block = 0; block < EXT2_N_BLOCKS; block++) - raw_inode->i_block[block] = inode->u.ext2_i.i_data[block]; else for (block = 0; block < EXT2_N_BLOCKS; block++) - raw_inode->i_block[block] = cpu_to_le32(inode->u.ext2_i.i_data[block]); + raw_inode->i_block[block] = inode->u.ext2_i.i_data[block]; mark_buffer_dirty(bh, 1); if (do_sync) { ll_rw_block (WRITE, 1, &bh); diff -u --recursive --new-file v2.3.6/linux/fs/ext2/symlink.c linux/fs/ext2/symlink.c --- v2.3.6/linux/fs/ext2/symlink.c Wed Jun 9 20:46:53 1999 +++ linux/fs/ext2/symlink.c Sat Jun 19 11:45:29 1999 @@ -43,12 +43,14 @@ NULL, /* rename */ ext2_readlink, /* readlink */ ext2_follow_link, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct dentry * ext2_follow_link(struct dentry * dentry, diff -u --recursive --new-file v2.3.6/linux/fs/ext2/truncate.c linux/fs/ext2/truncate.c --- v2.3.6/linux/fs/ext2/truncate.c Sun May 16 13:55:46 1999 +++ linux/fs/ext2/truncate.c Fri Jun 18 10:39:56 1999 @@ -131,10 +131,7 @@ if (bh->b_count == 1) { int tmp; - if (ind_bh) - tmp = le32_to_cpu(*p); - else - tmp = *p; + tmp = le32_to_cpu(*p); *p = 0; inode->i_blocks -= (inode->i_sb->s_blocksize / 512); mark_inode_dirty(inode); @@ -160,6 +157,9 @@ return retry; } +#define DATA_BUFFER_USED(bh) \ + ((bh->b_count > 1) || buffer_locked(bh)) + static int trunc_direct (struct inode * inode) { struct buffer_head * bh; @@ -170,7 +170,7 @@ for (i = direct_block ; i < EXT2_NDIR_BLOCKS ; i++) { u32 * p = inode->u.ext2_i.i_data + i; - int tmp = *p; + int tmp = le32_to_cpu(*p); if (!tmp) continue; @@ -178,7 +178,7 @@ bh = find_buffer(inode->i_dev, tmp, inode->i_sb->s_blocksize); if (bh) { bh->b_count++; - if(bh->b_count != 1 || buffer_locked(bh)) { + if (DATA_BUFFER_USED(bh)) { brelse(bh); retry = 1; continue; @@ -215,11 +215,11 @@ unsigned long block_to_free = 0, free_count = 0; int indirect_block, addr_per_block, blocks; - tmp = dind_bh ? le32_to_cpu(*p) : *p; + tmp = le32_to_cpu(*p); if (!tmp) return 0; ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != (dind_bh ? le32_to_cpu(*p) : *p)) { + if (tmp != le32_to_cpu(*p)) { brelse (ind_bh); return 1; } @@ -255,8 +255,8 @@ bh = find_buffer(inode->i_dev, tmp, inode->i_sb->s_blocksize); if (bh) { bh->b_count++; - if (bh->b_count != 1 || buffer_locked(bh)) { - brelse (bh); + if (DATA_BUFFER_USED(bh)) { + brelse(bh); retry = 1; continue; } @@ -297,11 +297,11 @@ int i, tmp, retry = 0; int dindirect_block, addr_per_block; - tmp = tind_bh ? le32_to_cpu(*p) : *p; + tmp = le32_to_cpu(*p); if (!tmp) return 0; dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != (tind_bh ? le32_to_cpu(*p) : *p)) { + if (tmp != le32_to_cpu(*p)) { brelse (dind_bh); return 1; } @@ -344,10 +344,11 @@ int i, tmp, retry = 0; int tindirect_block, addr_per_block, offset; - if (!(tmp = *p)) + tmp = le32_to_cpu(*p); + if (!tmp) return 0; tind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != *p) { + if (tmp != le32_to_cpu(*p)) { brelse (tind_bh); return 1; } @@ -384,8 +385,6 @@ void ext2_truncate (struct inode * inode) { - int err, offset; - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; @@ -410,25 +409,6 @@ run_task_queue(&tq_disk); current->policy |= SCHED_YIELD; schedule(); - } - /* - * If the file is not being truncated to a block boundary, the - * contents of the partial block following the end of the file - * must be zeroed in case it ever becomes accessible again due - * to subsequent file growth. - */ - offset = inode->i_size & (inode->i_sb->s_blocksize - 1); - if (offset) { - struct buffer_head * bh; - bh = ext2_bread (inode, - inode->i_size >> EXT2_BLOCK_SIZE_BITS(inode->i_sb), - 0, &err); - if (bh) { - memset (bh->b_data + offset, 0, - inode->i_sb->s_blocksize - offset); - mark_buffer_dirty (bh, 0); - brelse (bh); - } } inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); diff -u --recursive --new-file v2.3.6/linux/fs/fifo.c linux/fs/fifo.c --- v2.3.6/linux/fs/fifo.c Tue May 11 14:37:40 1999 +++ linux/fs/fifo.c Sat Jun 19 11:45:28 1999 @@ -2,14 +2,45 @@ * linux/fs/fifo.c * * written by Paul H. Hargrove + * + * Fixes: + * 10-06-1999, AV: fixed OOM handling in fifo_open(), moved + * initialization there, switched to external + * allocation of pipe_inode_info. */ #include +#include static int fifo_open(struct inode * inode,struct file * filp) { int retval = 0; - unsigned long page; + unsigned long page = 0; + struct pipe_inode_info *info, *tmp = NULL; + + if (inode->i_pipe) + goto got_it; + tmp = kmalloc(sizeof(struct pipe_inode_info),GFP_KERNEL); + if (inode->i_pipe) + goto got_it; + if (!tmp) + goto oom; + page = __get_free_page(GFP_KERNEL); + if (inode->i_pipe) + goto got_it; + if (!page) + goto oom; + inode->i_pipe = tmp; + PIPE_LOCK(*inode) = 0; + PIPE_START(*inode) = PIPE_LEN(*inode) = 0; + PIPE_BASE(*inode) = (char *) page; + PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; + init_waitqueue_head(&PIPE_WAIT(*inode)); + tmp = NULL; /* no need to free it */ + page = 0; + +got_it: switch( filp->f_mode ) { @@ -94,19 +125,26 @@ default: retval = -EINVAL; } - if (retval || PIPE_BASE(*inode)) - return retval; - page = __get_free_page(GFP_KERNEL); - if (PIPE_BASE(*inode)) { + if (retval) + goto cleanup; +out: + if (tmp) + kfree(tmp); + if (page) free_page(page); - return 0; + return retval; + +cleanup: + if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) { + info = inode->i_pipe; + inode->i_pipe = NULL; + free_page((unsigned long)info->base); + kfree(info); } - if (!page) - return -ENOMEM; - PIPE_LOCK(*inode) = 0; - PIPE_START(*inode) = PIPE_LEN(*inode) = 0; - PIPE_BASE(*inode) = (char *) page; - return 0; + goto out; +oom: + retval = -ENOMEM; + goto out; } /* @@ -141,20 +179,20 @@ NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; + +/* Goner. Filesystems do not use it anymore. */ + void init_fifo(struct inode * inode) { inode->i_op = &fifo_inode_operations; - PIPE_LOCK(*inode) = 0; - PIPE_BASE(*inode) = NULL; - PIPE_START(*inode) = PIPE_LEN(*inode) = 0; - PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; - init_waitqueue_head(&PIPE_WAIT(*inode)); - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0; } diff -u --recursive --new-file v2.3.6/linux/fs/hfs/dir_nat.c linux/fs/hfs/dir_nat.c --- v2.3.6/linux/fs/hfs/dir_nat.c Thu May 13 10:53:59 1999 +++ linux/fs/hfs/dir_nat.c Sat Jun 19 11:45:28 1999 @@ -99,7 +99,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; @@ -122,7 +121,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/hfs/file.c linux/fs/hfs/file.c --- v2.3.6/linux/fs/hfs/file.c Mon Nov 2 09:35:16 1998 +++ linux/fs/hfs/file.c Sat Jun 19 11:45:28 1999 @@ -69,7 +69,6 @@ hfs_file_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/hfs/file_cap.c linux/fs/hfs/file_cap.c --- v2.3.6/linux/fs/hfs/file_cap.c Mon Nov 2 09:35:16 1998 +++ linux/fs/hfs/file_cap.c Sat Jun 19 11:45:28 1999 @@ -83,7 +83,6 @@ cap_info_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidata */ }; diff -u --recursive --new-file v2.3.6/linux/fs/hfs/file_hdr.c linux/fs/hfs/file_hdr.c --- v2.3.6/linux/fs/hfs/file_hdr.c Wed May 12 13:26:20 1999 +++ linux/fs/hfs/file_hdr.c Sat Jun 19 11:45:28 1999 @@ -85,7 +85,6 @@ hdr_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/hpfs/inode.c linux/fs/hpfs/inode.c --- v2.3.6/linux/fs/hpfs/inode.c Sun May 16 10:27:40 1999 +++ linux/fs/hpfs/inode.c Sat Jun 19 11:45:28 1999 @@ -48,7 +48,6 @@ &hpfs_truncate, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; @@ -91,7 +90,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; @@ -115,7 +113,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/inode.c linux/fs/inode.c --- v2.3.6/linux/fs/inode.c Thu May 13 23:18:20 1999 +++ linux/fs/inode.c Thu Jun 17 23:11:01 1999 @@ -130,7 +130,6 @@ INIT_LIST_HEAD(&inode->i_hash); INIT_LIST_HEAD(&inode->i_dentry); sema_init(&inode->i_sem, 1); - sema_init(&inode->i_atomic_write, 1); } static inline void write_inode(struct inode *inode) @@ -337,7 +336,7 @@ * dispose_list. */ #define CAN_UNUSE(inode) \ - (((inode)->i_count | (inode)->i_state) == 0) + (((inode)->i_count | (inode)->i_state | (inode)->i_nrpages) == 0) #define INODE(entry) (list_entry(entry, struct inode, i_list)) static int free_inodes(void) @@ -527,6 +526,7 @@ inode->i_generation = 0; memset(&inode->i_dquot, 0, sizeof(inode->i_dquot)); sema_init(&inode->i_sem, 1); + inode->i_pipe = NULL; } /* @@ -765,9 +765,6 @@ kdevname(inode->i_dev), inode->i_ino, inode->i_count); if (atomic_read(&inode->i_sem.count) != 1) printk(KERN_ERR "iput: Aieee, semaphore in use inode %s/%ld, count=%d\n", -kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count)); -if (atomic_read(&inode->i_atomic_write.count) != 1) -printk(KERN_ERR "iput: Aieee, atomic write semaphore in use inode %s/%ld, count=%d\n", kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count)); #endif } diff -u --recursive --new-file v2.3.6/linux/fs/isofs/file.c linux/fs/isofs/file.c --- v2.3.6/linux/fs/isofs/file.c Sun Mar 7 15:25:23 1999 +++ linux/fs/isofs/file.c Sat Jun 19 18:20:13 1999 @@ -48,9 +48,10 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ - generic_readpage, /* readpage */ - NULL, /* writepage */ isofs_bmap, /* bmap */ + block_read_full_page, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ NULL, /* truncate */ NULL /* permission */ }; diff -u --recursive --new-file v2.3.6/linux/fs/isofs/inode.c linux/fs/isofs/inode.c --- v2.3.6/linux/fs/isofs/inode.c Tue Jun 8 10:47:58 1999 +++ linux/fs/isofs/inode.c Sat Jun 19 18:20:13 1999 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -909,7 +910,7 @@ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } -int isofs_bmap(struct inode * inode,int block) +static int do_isofs_bmap(struct inode * inode,int block) { off_t b_off, offset, size; struct inode *ino; @@ -991,6 +992,15 @@ return (b_off - offset + firstext) >> ISOFS_BUFFER_BITS(inode); } +int isofs_bmap(struct inode * inode,int block) +{ + int retval; + + lock_kernel(); + retval = do_isofs_bmap(inode, block); + unlock_kernel(); + return retval; +} static void test_and_set_uid(uid_t *p, uid_t value) { diff -u --recursive --new-file v2.3.6/linux/fs/minix/bitmap.c linux/fs/minix/bitmap.c --- v2.3.6/linux/fs/minix/bitmap.c Tue Jun 8 10:47:58 1999 +++ linux/fs/minix/bitmap.c Wed Jun 16 19:26:27 1999 @@ -112,14 +112,6 @@ if (j < sb->u.minix_sb.s_firstdatazone || j >= sb->u.minix_sb.s_nzones) return 0; - if (!(bh = getblk(sb->s_dev,j,BLOCK_SIZE))) { - printk("new_block: cannot get block"); - return 0; - } - memset(bh->b_data, 0, BLOCK_SIZE); - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); - brelse(bh); return j; } diff -u --recursive --new-file v2.3.6/linux/fs/minix/file.c linux/fs/minix/file.c --- v2.3.6/linux/fs/minix/file.c Mon Aug 24 13:02:44 1998 +++ linux/fs/minix/file.c Sat Jun 19 12:15:14 1999 @@ -27,7 +27,51 @@ #include #include -static ssize_t minix_file_write(struct file *, const char *, size_t, loff_t *); +static int minix_writepage(struct file *file, struct page *page) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + unsigned long block; + int *p, nr[PAGE_SIZE/BLOCK_SIZE]; + int i, err, created; + struct buffer_head *bh; + + i = PAGE_SIZE / BLOCK_SIZE; + block = page->offset / BLOCK_SIZE; + p = nr; + bh = page->buffers; + do { + if (bh && bh->b_blocknr) + *p = bh->b_blocknr; + else + *p = minix_getblk_block(inode, block, 1, &err, &created); + if (!*p) + return -EIO; + i--; + block++; + p++; + if (bh) + bh = bh->b_this_page; + } while(i > 0); + + /* IO start */ + brw_page(WRITE, page, inode->i_dev, nr, BLOCK_SIZE, 1); + return 0; +} + +static long minix_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char *buf) +{ + return block_write_one_page(file, page, offset, bytes, buf, minix_getblk_block); +} + +/* + * Write to a file (through the page cache). + */ +static ssize_t +minix_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + return generic_file_write(file, buf, count, ppos, minix_write_one_page); +} /* * We have mostly NULLs here: the current defaults are OK for @@ -61,74 +105,11 @@ NULL, /* readlink */ NULL, /* follow_link */ generic_readpage, /* readpage */ - NULL, /* writepage */ + minix_writepage, /* writepage */ minix_bmap, /* bmap */ minix_truncate, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* revalidate */ + block_flushpage, /* flushpage */ }; - -static ssize_t minix_file_write(struct file * filp, const char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = filp->f_dentry->d_inode; - off_t pos; - ssize_t written, c; - struct buffer_head * bh; - char * p; - - if (!inode) { - printk("minix_file_write: inode = NULL\n"); - return -EINVAL; - } - if (!S_ISREG(inode->i_mode)) { - printk("minix_file_write: mode = %07o\n",inode->i_mode); - return -EINVAL; - } - if (filp->f_flags & O_APPEND) - pos = inode->i_size; - else - pos = *ppos; - written = 0; - while (written < count) { - bh = minix_getblk(inode,pos/BLOCK_SIZE,1); - if (!bh) { - if (!written) - written = -ENOSPC; - break; - } - c = BLOCK_SIZE - (pos % BLOCK_SIZE); - if (c > count-written) - c = count-written; - if (c != BLOCK_SIZE && !buffer_uptodate(bh)) { - ll_rw_block(READ, 1, &bh); - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) { - brelse(bh); - if (!written) - written = -EIO; - break; - } - } - p = (pos % BLOCK_SIZE) + bh->b_data; - c -= copy_from_user(p,buf,c); - if (!c) { - brelse(bh); - if (!written) - written = -EFAULT; - break; - } - update_vm_cache(inode, pos, p, c); - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); - brelse(bh); - pos += c; - written += c; - buf += c; - } - if (pos > inode->i_size) - inode->i_size = pos; - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - *ppos = pos; - mark_inode_dirty(inode); - return written; -} diff -u --recursive --new-file v2.3.6/linux/fs/minix/inode.c linux/fs/minix/inode.c --- v2.3.6/linux/fs/minix/inode.c Tue Jun 8 10:47:58 1999 +++ linux/fs/minix/inode.c Wed Jun 16 19:26:27 1999 @@ -407,7 +407,7 @@ return tmp; } -static int V2_minix_bmap(struct inode * inode,int block) +static int V2_minix_bmap(struct inode * inode, int block) { int i; @@ -454,7 +454,7 @@ /* * The global minix fs bmap function. */ -int minix_bmap(struct inode * inode,int block) +int minix_bmap(struct inode * inode, int block) { if (INODE_VERSION(inode) == MINIX_V1) return V1_minix_bmap(inode, block); @@ -465,8 +465,8 @@ /* * The minix V1 fs getblk functions. */ -static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr, - int create) +static struct buffer_head * V1_inode_getblk(struct inode * inode, int nr, int create, + int metadata, int *phys_block, int *created) { int tmp; unsigned short *p; @@ -476,31 +476,51 @@ repeat: tmp = *p; if (tmp) { - result = getblk(inode->i_dev, tmp, BLOCK_SIZE); - if (tmp == *p) - return result; - brelse(result); - goto repeat; + if (metadata) { + result = getblk(inode->i_dev, tmp, BLOCK_SIZE); + if (tmp == *p) + return result; + brelse(result); + goto repeat; + } else { + *phys_block = tmp; + return NULL; + } } if (!create) return NULL; tmp = minix_new_block(inode->i_sb); if (!tmp) return NULL; - result = getblk(inode->i_dev, tmp, BLOCK_SIZE); - if (*p) { - minix_free_block(inode->i_sb,tmp); - brelse(result); - goto repeat; + if (metadata) { + result = getblk(inode->i_dev, tmp, BLOCK_SIZE); + if (*p) { + minix_free_block(inode->i_sb, tmp); + brelse(result); + goto repeat; + } + memset(result->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + if (*p) { + minix_free_block(inode->i_sb, tmp); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *created = 1; } *p = tmp; + inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); return result; } static struct buffer_head * V1_block_getblk(struct inode * inode, - struct buffer_head * bh, int nr, int create) + struct buffer_head * bh, int nr, int create, + int metadata, int *phys_block, int *created) { int tmp; unsigned short *p; @@ -520,13 +540,19 @@ repeat: tmp = *p; if (tmp) { - result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (tmp == *p) { + if (metadata) { + result = getblk(bh->b_dev, tmp, BLOCK_SIZE); + if (tmp == *p) { + brelse(bh); + return result; + } + brelse(result); + goto repeat; + } else { + *phys_block = tmp; brelse(bh); - return result; + return NULL; } - brelse(result); - goto repeat; } if (!create) { brelse(bh); @@ -537,49 +563,74 @@ brelse(bh); return NULL; } - result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (*p) { - minix_free_block(inode->i_sb,tmp); - brelse(result); - goto repeat; + if (metadata) { + result = getblk(bh->b_dev, tmp, BLOCK_SIZE); + if (*p) { + minix_free_block(inode->i_sb, tmp); + brelse(result); + goto repeat; + } + memset(result->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + if (*p) { + minix_free_block(inode->i_sb, tmp); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *created = 1; } + *p = tmp; mark_buffer_dirty(bh, 1); brelse(bh); return result; } -static struct buffer_head * V1_minix_getblk(struct inode * inode, int block, - int create) +int V1_getblk_block(struct inode * inode, long block, int create, int *err, int *created) { - struct buffer_head * bh; + struct buffer_head *bh, *tmp; + int phys_block; - if (block<0) { + *err = -EIO; + if (block < 0) { printk("minix_getblk: block<0"); - return NULL; + return 0; } if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) { printk("minix_getblk: block>big"); - return NULL; + return 0; + } + *created = 0; + if (block < 7) { + tmp = V1_inode_getblk(inode, block, create, + 0, &phys_block, created); + goto out; } - if (block < 7) - return V1_inode_getblk(inode,block,create); block -= 7; if (block < 512) { - bh = V1_inode_getblk(inode,7,create); - return V1_block_getblk(inode, bh, block, create); + bh = V1_inode_getblk(inode, 7, create, 1, NULL, NULL); + tmp = V1_block_getblk(inode, bh, block, create, + 0, &phys_block, created); + goto out; } block -= 512; - bh = V1_inode_getblk(inode,8,create); - bh = V1_block_getblk(inode, bh, (block>>9) & 511, create); - return V1_block_getblk(inode, bh, block & 511, create); + bh = V1_inode_getblk(inode, 8, create, 1, NULL, NULL); + bh = V1_block_getblk(inode, bh, (block>>9) & 511, create, 1, NULL, NULL); + tmp = V1_block_getblk(inode, bh, block & 511, create, 0, &phys_block, created); + +out: + *err = 0; + return phys_block; } /* * The minix V2 fs getblk functions. */ -static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr, - int create) +static struct buffer_head * V2_inode_getblk(struct inode * inode, int nr, int create, + int metadata, int *phys_block, int *created) { int tmp; unsigned long *p; @@ -589,31 +640,51 @@ repeat: tmp = *p; if (tmp) { - result = getblk(inode->i_dev, tmp, BLOCK_SIZE); - if (tmp == *p) - return result; - brelse(result); - goto repeat; + if (metadata) { + result = getblk(inode->i_dev, tmp, BLOCK_SIZE); + if (tmp == *p) + return result; + brelse(result); + goto repeat; + } else { + *phys_block = tmp; + return NULL; + } } if (!create) return NULL; tmp = minix_new_block(inode->i_sb); if (!tmp) return NULL; - result = getblk(inode->i_dev, tmp, BLOCK_SIZE); - if (*p) { - minix_free_block(inode->i_sb,tmp); - brelse(result); - goto repeat; + if (metadata) { + result = getblk(inode->i_dev, tmp, BLOCK_SIZE); + if (*p) { + minix_free_block(inode->i_sb, tmp); + brelse(result); + goto repeat; + } + memset(result->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + if (*p) { + minix_free_block(inode->i_sb, tmp); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *created = 1; } *p = tmp; + inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); return result; } static struct buffer_head * V2_block_getblk(struct inode * inode, - struct buffer_head * bh, int nr, int create) + struct buffer_head * bh, int nr, int create, + int metadata, int *phys_block, int *created) { int tmp; unsigned long *p; @@ -633,13 +704,19 @@ repeat: tmp = *p; if (tmp) { - result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (tmp == *p) { + if (metadata) { + result = getblk(bh->b_dev, tmp, BLOCK_SIZE); + if (tmp == *p) { + brelse(bh); + return result; + } + brelse(result); + goto repeat; + } else { + *phys_block = tmp; brelse(bh); - return result; + return NULL; } - brelse(result); - goto repeat; } if (!create) { brelse(bh); @@ -650,60 +727,107 @@ brelse(bh); return NULL; } - result = getblk(bh->b_dev, tmp, BLOCK_SIZE); - if (*p) { - minix_free_block(inode->i_sb,tmp); - brelse(result); - goto repeat; + if (metadata) { + result = getblk(bh->b_dev, tmp, BLOCK_SIZE); + if (*p) { + minix_free_block(inode->i_sb, tmp); + brelse(result); + goto repeat; + } + memset(result->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + if (*p) { + minix_free_block(inode->i_sb, tmp); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *created = 1; } + *p = tmp; mark_buffer_dirty(bh, 1); brelse(bh); return result; } -static struct buffer_head * V2_minix_getblk(struct inode * inode, int block, - int create) +int V2_getblk_block(struct inode * inode, int block, int create, int *err, int *created) { - struct buffer_head * bh; + struct buffer_head * bh, *tmp; + int phys_block; - if (block<0) { + *err = -EIO; + if (block < 0) { printk("minix_getblk: block<0"); - return NULL; + return 0; } if (block >= inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE) { printk("minix_getblk: block>big"); - return NULL; + return 0; + } + *created = 0; + if (block < 7) { + tmp = V2_inode_getblk(inode, block, create, + 0, &phys_block, created); + goto out; } - if (block < 7) - return V2_inode_getblk(inode,block,create); block -= 7; if (block < 256) { - bh = V2_inode_getblk(inode,7,create); - return V2_block_getblk(inode, bh, block, create); + bh = V2_inode_getblk(inode, 7, create, 1, NULL, NULL); + tmp = V2_block_getblk(inode, bh, block, create, + 0, &phys_block, created); + goto out; } block -= 256; if (block < 256*256) { - bh = V2_inode_getblk(inode,8,create); - bh = V2_block_getblk(inode, bh, (block>>8) & 255, create); - return V2_block_getblk(inode, bh, block & 255, create); + bh = V2_inode_getblk(inode, 8, create, 1, NULL, NULL); + bh = V2_block_getblk(inode, bh, (block>>8) & 255, create, + 1, NULL, NULL); + tmp = V2_block_getblk(inode, bh, block & 255, create, + 0, &phys_block, created); + goto out; } block -= 256*256; - bh = V2_inode_getblk(inode,9,create); - bh = V2_block_getblk(inode, bh, (block >> 16) & 255, create); - bh = V2_block_getblk(inode, bh, (block >> 8) & 255, create); - return V2_block_getblk(inode, bh, block & 255, create); + bh = V2_inode_getblk(inode, 9, create, 1, NULL, NULL); + bh = V2_block_getblk(inode, bh, (block >> 16) & 255, create, 1, NULL, NULL); + bh = V2_block_getblk(inode, bh, (block >> 8) & 255, create, 1, NULL, NULL); + tmp = V2_block_getblk(inode, bh, block & 255, create, 0, &phys_block, created); + +out: + *err = 0; + return phys_block; +} + +int minix_getblk_block (struct inode *inode, long block, + int create, int *err, int *created) +{ + if (INODE_VERSION(inode) == MINIX_V1) + return V1_getblk_block(inode, block, create, err, created); + else + return V2_getblk_block(inode, block, create, err, created); } /* * the global minix fs getblk function. */ -struct buffer_head * minix_getblk(struct inode * inode, int block, int create) +struct buffer_head *minix_getblk (struct inode *inode, int block, int create) { - if (INODE_VERSION(inode) == MINIX_V1) - return V1_minix_getblk(inode,block,create); - else - return V2_minix_getblk(inode,block,create); + struct buffer_head *tmp = NULL; + int phys_block; + int err, created; + + phys_block = minix_getblk_block(inode, block, create, &err, &created); + if (phys_block) { + tmp = getblk(inode->i_dev, phys_block, BLOCK_SIZE); + if (created) { + memset(tmp->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(tmp, 1); + mark_buffer_dirty(tmp, 1); + } + } + return tmp; } struct buffer_head * minix_bread(struct inode * inode, int block, int create) diff -u --recursive --new-file v2.3.6/linux/fs/minix/truncate.c linux/fs/minix/truncate.c --- v2.3.6/linux/fs/minix/truncate.c Thu Nov 12 11:44:09 1998 +++ linux/fs/minix/truncate.c Wed Jun 16 19:26:27 1999 @@ -32,6 +32,9 @@ * general case (size = XXX). I hope. */ +#define DATA_BUFFER_USED(bh) \ + ((bh->b_count > 1) || buffer_locked(bh)) + /* * The functions for minix V1 fs truncation. */ @@ -52,7 +55,7 @@ brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || tmp != *p) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != *p) { retry = 1; brelse(bh); continue; @@ -103,7 +106,7 @@ brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || tmp != *ind) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != *ind) { retry = 1; brelse(bh); continue; @@ -216,7 +219,7 @@ brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || tmp != *p) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != *p) { retry = 1; brelse(bh); continue; @@ -267,7 +270,7 @@ brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || tmp != *ind) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != *ind) { retry = 1; brelse(bh); continue; diff -u --recursive --new-file v2.3.6/linux/fs/msdos/namei.c linux/fs/msdos/namei.c --- v2.3.6/linux/fs/msdos/namei.c Thu May 13 23:18:20 1999 +++ linux/fs/msdos/namei.c Sat Jun 19 11:45:28 1999 @@ -633,7 +633,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c --- v2.3.6/linux/fs/ncpfs/dir.c Fri May 14 12:43:00 1999 +++ linux/fs/ncpfs/dir.c Sat Jun 19 11:45:28 1999 @@ -98,7 +98,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/nfs/dir.c linux/fs/nfs/dir.c --- v2.3.6/linux/fs/nfs/dir.c Tue Jun 8 17:58:03 1999 +++ linux/fs/nfs/dir.c Sat Jun 19 11:45:28 1999 @@ -78,13 +78,13 @@ nfs_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ nfs_revalidate, /* revalidate */ }; @@ -118,6 +118,61 @@ }; static kmem_cache_t *nfs_cookie_cachep; +/* This whole scheme relies on the fact that dirent cookies + * are monotonically increasing. + * + * Another invariant is that once we have a valid non-zero + * EOF marker cached, we also have the complete set of cookie + * table entries. + * + * We return the page offset assosciated with the page where + * cookie must be if it exists at all, however if we can not + * figure that out conclusively, we return < 0. + */ +static long __nfs_readdir_offset(struct inode *inode, __u32 cookie) +{ + struct nfs_cookie_table *p; + unsigned long ret = 0; + + for(p = NFS_COOKIES(inode); p != NULL; p = p->next) { + int i; + + for (i = 0; i < COOKIES_PER_CHUNK; i++) { + __u32 this_cookie = p->cookies[i]; + + /* End of known cookies, EOF is our only hope. */ + if (!this_cookie) + goto check_eof; + + /* Next cookie is larger, must be in previous page. */ + if (this_cookie > cookie) + return ret; + + ret += 1; + + /* Exact cookie match, it must be in this page :-) */ + if (this_cookie == cookie) + return ret; + } + } +check_eof: + if (NFS_DIREOF(inode) != 0) + return ret; + + return -1L; +} + +static __inline__ long nfs_readdir_offset(struct inode *inode, __u32 cookie) +{ + /* Cookie zero is always at page offset zero. Optimize the + * other common case since most directories fit entirely + * in one page. + */ + if (!cookie || (!NFS_COOKIES(inode) && NFS_DIREOF(inode))) + return 0; + return __nfs_readdir_offset(inode, cookie); +} + /* Since a cookie of zero is declared special by the NFS * protocol, we easily can tell if a cookie in an existing * table chunk is valid or not. @@ -148,38 +203,7 @@ return ret; } -/* Now we cache directories properly, by stuffing the dirent - * data directly in the page cache. - * - * Inode invalidation due to refresh etc. takes care of - * _everything_, no sloppy entry flushing logic, no extraneous - * copying, network direct to page cache, the way it was meant - * to be. - * - * NOTE: Dirent information verification is done always by the - * page-in of the RPC reply, nowhere else, this simplies - * things substantially. - */ #define NFS_NAMELEN_ALIGN(__len) ((((__len)+3)>>2)<<2) -static u32 find_midpoint(__u32 *p, u32 doff) -{ - u32 walk = doff & PAGE_MASK; - - while(*p++ != 0) { - __u32 skip; - - p++; /* skip fileid */ - - /* Skip len, name, and cookie. */ - skip = NFS_NAMELEN_ALIGN(*p++); - p += (skip >> 2) + 1; - walk += skip + (4 * sizeof(__u32)); - if (walk >= doff) - break; - } - return walk; -} - static int create_cookie(__u32 cookie, unsigned long off, struct inode *inode) { struct nfs_cookie_table **cpp; @@ -211,48 +235,74 @@ return 0; } -static struct page *try_to_get_dirent_page(struct file *, unsigned long, int); +static struct page *try_to_get_dirent_page(struct file *, __u32, int); /* Recover from a revalidation flush. The case here is that * the inode for the directory got invalidated somehow, and * all of our cached information is lost. In order to get * a correct cookie for the current readdir request from the * user, we must (re-)fetch older readdir page cache entries. + * + * Returns < 0 if some error occurrs, else it is the page offset + * to fetch. */ -static int refetch_to_readdir_off(struct file *file, struct inode *inode, u32 off) +static long refetch_to_readdir_cookie(struct file *file, struct inode *inode) { - u32 cur_off, goal_off = off & PAGE_MASK; + struct page *page; + u32 goal_cookie = file->f_pos; + long cur_off, ret = -1L; again: cur_off = 0; - while (cur_off < goal_off) { - struct page *page; - - page = find_page(inode, cur_off); + for (;;) { + page = find_get_page(inode, cur_off); if (page) { - if (PageLocked(page)) - __wait_on_page(page); - if (!PageUptodate(page)) - return -1; + if (!Page_Uptodate(page)) + goto out_error; } else { - page = try_to_get_dirent_page(file, cur_off, 0); + __u32 *cp = find_cookie(inode, cur_off); + + if (!cp) + goto out_error; + + page = try_to_get_dirent_page(file, *cp, 0); if (!page) { if (!cur_off) - return -1; + goto out_error; /* Someone touched the dir on us. */ goto again; } - page_cache_release(page); } + page_cache_release(page); + + if ((ret = nfs_readdir_offset(inode, goal_cookie)) >= 0) + goto out; - cur_off += PAGE_SIZE; + cur_off += 1; } +out: + return ret; - return 0; +out_error: + if (page) + page_cache_release(page); + goto out; } -static struct page *try_to_get_dirent_page(struct file *file, unsigned long offset, int refetch_ok) +/* Now we cache directories properly, by stuffing the dirent + * data directly in the page cache. + * + * Inode invalidation due to refresh etc. takes care of + * _everything_, no sloppy entry flushing logic, no extraneous + * copying, network direct to page cache, the way it was meant + * to be. + * + * NOTE: Dirent information verification is done always by the + * page-in of the RPC reply, nowhere else, this simplies + * things substantially. + */ +static struct page *try_to_get_dirent_page(struct file *file, __u32 cookie, int refetch_ok) { struct nfs_readdirargs rd_args; struct nfs_readdirres rd_res; @@ -260,6 +310,7 @@ struct inode *inode = dentry->d_inode; struct page *page, **hash; unsigned long page_cache; + long offset; __u32 *cookiep; page = NULL; @@ -267,27 +318,34 @@ if (!page_cache) goto out; - while ((cookiep = find_cookie(inode, offset)) == NULL) { + if ((offset = nfs_readdir_offset(inode, cookie)) < 0) { if (!refetch_ok || - refetch_to_readdir_off(file, inode, file->f_pos)) + (offset = refetch_to_readdir_cookie(file, inode)) < 0) { + page_cache_free(page_cache); goto out; + } + } + + cookiep = find_cookie(inode, offset); + if (!cookiep) { + /* Gross fatal error. */ + page_cache_free(page_cache); + goto out; } hash = page_hash(inode, offset); - page = __find_page(inode, offset, *hash); +repeat: + page = __find_lock_page(inode, offset, hash); if (page) { page_cache_free(page_cache); - goto out; + goto unlock_out; } page = page_cache_entry(page_cache); - atomic_inc(&page->count); - page->flags = ((page->flags & - ~((1 << PG_uptodate) | (1 << PG_error))) | - ((1 << PG_referenced) | (1 << PG_locked))); - page->offset = offset; - add_page_to_inode_queue(inode, page); - __add_page_to_hash_queue(page, hash); + if (add_to_page_cache_unique(page, inode, offset, hash)) { + page_cache_release(page); + goto repeat; + } rd_args.fh = NFS_FH(dentry); rd_res.buffer = (char *)page_cache; @@ -303,48 +361,50 @@ } while(rd_res.bufsiz > 0); if (rd_res.bufsiz < 0) - NFS_DIREOF(inode) = - (offset << PAGE_CACHE_SHIFT) + -(rd_res.bufsiz); + NFS_DIREOF(inode) = rd_res.cookie; else if (create_cookie(rd_res.cookie, offset, inode)) goto error; - set_bit(PG_uptodate, &page->flags); + SetPageUptodate(page); unlock_out: - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + UnlockPage(page); out: return page; error: - set_bit(PG_error, &page->flags); + SetPageError(page); goto unlock_out; } -static __inline__ u32 nfs_do_filldir(__u32 *p, u32 doff, +/* Seek up to dirent assosciated with the passed in cookie, + * then fill in dirents found. Return the last cookie + * actually given to the user, to update the file position. + */ +static __inline__ u32 nfs_do_filldir(__u32 *p, u32 cookie, void *dirent, filldir_t filldir) { u32 end; - if (doff & ~PAGE_CACHE_MASK) { - doff = find_midpoint(p, doff); - p += (doff & ~PAGE_CACHE_MASK) >> 2; - } while((end = *p++) != 0) { - __u32 fileid = *p++; - __u32 len = *p++; - __u32 skip = NFS_NAMELEN_ALIGN(len); - char *name = (char *) p; - - /* Skip the cookie. */ - p = ((__u32 *) (name + skip)) + 1; - if (filldir(dirent, name, len, doff, fileid) < 0) - goto out; - doff += (skip + (4 * sizeof(__u32))); + __u32 fileid, len, skip, this_cookie; + char *name; + + fileid = *p++; + len = *p++; + name = (char *) p; + skip = NFS_NAMELEN_ALIGN(len); + p += (skip >> 2); + this_cookie = *p++; + + if (this_cookie < cookie) + continue; + + cookie = this_cookie; + if (filldir(dirent, name, len, cookie, fileid) < 0) + break; } - if (!*p) - doff = PAGE_CACHE_ALIGN(doff); -out: - return doff; + + return cookie; } /* The file offset position is represented in pure bytes, to @@ -359,7 +419,7 @@ struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; struct page *page, **hash; - unsigned long offset; + long offset; int res; res = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry); @@ -369,14 +429,14 @@ if (NFS_DIREOF(inode) && filp->f_pos >= NFS_DIREOF(inode)) return 0; - offset = filp->f_pos >> PAGE_CACHE_SHIFT; + if ((offset = nfs_readdir_offset(inode, filp->f_pos)) < 0) + goto no_dirent_page; + hash = page_hash(inode, offset); - page = __find_page(inode, offset, *hash); + page = __find_get_page(inode, offset, hash); if (!page) goto no_dirent_page; - if (PageLocked(page)) - goto dirent_locked_wait; - if (!PageUptodate(page)) + if (!Page_Uptodate(page)) goto dirent_read_error; success: filp->f_pos = nfs_do_filldir((__u32 *) page_address(page), @@ -385,13 +445,11 @@ return 0; no_dirent_page: - page = try_to_get_dirent_page(filp, offset, 1); + page = try_to_get_dirent_page(filp, filp->f_pos, 1); if (!page) goto no_page; -dirent_locked_wait: - wait_on_page(page); - if (PageUptodate(page)) + if (Page_Uptodate(page)) goto success; dirent_read_error: page_cache_release(page); @@ -399,20 +457,39 @@ return -EIO; } -/* Invalidate directory cookie caches and EOF marker - * for an inode. +/* Flush directory cookie and EOF caches for an inode. + * So we don't thrash allocating/freeing cookie tables, + * we keep the cookies around until the inode is + * deleted/reused. + */ +__inline__ void nfs_flush_dircache(struct inode *inode) +{ + struct nfs_cookie_table *p = NFS_COOKIES(inode); + + while (p != NULL) { + int i; + + for(i = 0; i < COOKIES_PER_CHUNK; i++) + p->cookies[i] = 0; + + p = p->next; + } + NFS_DIREOF(inode) = 0; +} + +/* Free up directory cache state, this happens when + * nfs_delete_inode is called on an NFS directory. */ -__inline__ void nfs_invalidate_dircache(struct inode *inode) +void nfs_free_dircache(struct inode *inode) { struct nfs_cookie_table *p = NFS_COOKIES(inode); - if (p != NULL) { - NFS_COOKIES(inode) = NULL; - do { struct nfs_cookie_table *next = p->next; - kmem_cache_free(nfs_cookie_cachep, p); - p = next; - } while (p != NULL); + while (p != NULL) { + struct nfs_cookie_table *next = p->next; + kmem_cache_free(nfs_cookie_cachep, p); + p = next; } + NFS_COOKIES(inode) = NULL; NFS_DIREOF(inode) = 0; } @@ -538,11 +615,11 @@ /* Purge readdir caches. */ if (dentry->d_parent->d_inode) { invalidate_inode_pages(dentry->d_parent->d_inode); - nfs_invalidate_dircache(dentry->d_parent->d_inode); + nfs_flush_dircache(dentry->d_parent->d_inode); } if (inode && S_ISDIR(inode->i_mode)) { invalidate_inode_pages(inode); - nfs_invalidate_dircache(inode); + nfs_flush_dircache(inode); } return 0; } @@ -739,7 +816,7 @@ * Invalidate the dir cache before the operation to avoid a race. */ invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name, &sattr, &fhandle, &fattr); if (!error) @@ -769,7 +846,7 @@ sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name, &sattr, &fhandle, &fattr); if (!error) @@ -804,7 +881,7 @@ */ d_drop(dentry); invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent), dentry->d_name.name, &sattr, &fhandle, &fattr); return error; @@ -825,7 +902,7 @@ #endif invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name); @@ -953,7 +1030,7 @@ } while(sdentry->d_inode != NULL); /* need negative lookup */ invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name, NFS_FH(dentry->d_parent), silly); @@ -1023,7 +1100,7 @@ d_delete(dentry); } invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name); /* @@ -1090,7 +1167,7 @@ */ d_drop(dentry); invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent), dentry->d_name.name, symname, &sattr); if (!error) { @@ -1121,7 +1198,7 @@ */ d_drop(dentry); invalidate_inode_pages(dir); - nfs_invalidate_dircache(dir); + nfs_flush_dircache(dir); error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry), NFS_FH(dentry->d_parent), dentry->d_name.name); if (!error) { @@ -1267,9 +1344,9 @@ } invalidate_inode_pages(new_dir); - nfs_invalidate_dircache(new_dir); + nfs_flush_dircache(new_dir); invalidate_inode_pages(old_dir); - nfs_invalidate_dircache(old_dir); + nfs_flush_dircache(old_dir); error = nfs_proc_rename(NFS_DSERVER(old_dentry), NFS_FH(old_dentry->d_parent), old_dentry->d_name.name, NFS_FH(new_dentry->d_parent), new_dentry->d_name.name); diff -u --recursive --new-file v2.3.6/linux/fs/nfs/file.c linux/fs/nfs/file.c --- v2.3.6/linux/fs/nfs/file.c Wed Jun 2 13:46:59 1999 +++ linux/fs/nfs/file.c Sat Jun 19 11:45:28 1999 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -70,13 +71,13 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ nfs_readpage, /* readpage */ nfs_writepage, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ nfs_revalidate, /* revalidate */ }; @@ -172,8 +173,11 @@ bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes); status = -EFAULT; - if (bytes) + if (bytes) { + lock_kernel(); status = nfs_updatepage(file, page, offset, bytes); + unlock_kernel(); + } return status; } diff -u --recursive --new-file v2.3.6/linux/fs/nfs/inode.c linux/fs/nfs/inode.c --- v2.3.6/linux/fs/nfs/inode.c Tue Jun 8 17:58:03 1999 +++ linux/fs/nfs/inode.c Wed Jun 16 19:26:27 1999 @@ -99,23 +99,28 @@ int failed; dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino); - /* - * Flush out any pending write requests ... - */ - if (NFS_WRITEBACK(inode) != NULL) { - unsigned long timeout = jiffies + 5*HZ; + + if (S_ISDIR(inode->i_mode)) { + nfs_free_dircache(inode); + } else { + /* + * Flush out any pending write requests ... + */ + if (NFS_WRITEBACK(inode) != NULL) { + unsigned long timeout = jiffies + 5*HZ; #ifdef NFS_DEBUG_VERBOSE printk("nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino); #endif - nfs_inval(inode); - while (NFS_WRITEBACK(inode) != NULL && - time_before(jiffies, timeout)) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); + nfs_inval(inode); + while (NFS_WRITEBACK(inode) != NULL && + time_before(jiffies, timeout)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); + } + current->state = TASK_RUNNING; + if (NFS_WRITEBACK(inode) != NULL) + printk("NFS: Arghhh, stuck RPC requests!\n"); } - current->state = TASK_RUNNING; - if (NFS_WRITEBACK(inode) != NULL) - printk("NFS: Arghhh, stuck RPC requests!\n"); } failed = nfs_check_failed_request(inode); @@ -433,7 +438,7 @@ invalidate_inode_pages(inode); if (S_ISDIR(inode->i_mode)) - nfs_invalidate_dircache(inode); + nfs_flush_dircache(inode); } /* @@ -477,8 +482,6 @@ inode->i_size = fattr->size; inode->i_mtime = fattr->mtime.seconds; NFS_OLDMTIME(inode) = fattr->mtime.seconds; - NFS_COOKIES(inode) = NULL; - NFS_WRITEBACK(inode) = NULL; } nfs_refresh_inode(inode, fattr); } diff -u --recursive --new-file v2.3.6/linux/fs/nfs/read.c linux/fs/nfs/read.c --- v2.3.6/linux/fs/nfs/read.c Mon Jun 7 13:25:49 1999 +++ linux/fs/nfs/read.c Sat Jun 19 11:38:49 1999 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -77,7 +78,6 @@ int flags = IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0; dprintk("NFS: nfs_readpage_sync(%p)\n", page); - clear_bit(PG_error, &page->flags); do { if (count < rsize) @@ -111,16 +111,14 @@ } while (count); memset(buffer, 0, count); - set_bit(PG_uptodate, &page->flags); + SetPageUptodate(page); result = 0; io_error: + UnlockPage(page); /* Note: we don't refresh if the call returned error */ if (refresh && result >= 0) nfs_refresh_inode(inode, &rqst.ra_fattr); - /* N.B. Use nfs_unlock_page here? */ - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); return result; } @@ -146,17 +144,15 @@ memset((char *) address + result, 0, PAGE_SIZE - result); } nfs_refresh_inode(req->ra_inode, &req->ra_fattr); - set_bit(PG_uptodate, &page->flags); + SetPageUptodate(page); succ++; } else { - set_bit(PG_error, &page->flags); + SetPageError(page); fail++; dprintk("NFS: %d successful reads, %d failures\n", succ, fail); } - /* N.B. Use nfs_unlock_page here? */ - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); - + page->owner = (int)current; // HACK, FIXME, will go away. + UnlockPage(page); free_page(address); rpc_release_task(task); @@ -227,10 +223,10 @@ struct inode *inode = dentry->d_inode; int error; + lock_kernel(); dprintk("NFS: nfs_readpage (%p %ld@%ld)\n", page, PAGE_SIZE, page->offset); - atomic_inc(&page->count); - set_bit(PG_locked, &page->flags); + get_page(page); /* * Try to flush any pending writes to the file.. @@ -256,10 +252,10 @@ goto out_free; out_error: - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + UnlockPage(page); out_free: free_page(page_address(page)); out: + unlock_kernel(); return error; } diff -u --recursive --new-file v2.3.6/linux/fs/nfs/symlink.c linux/fs/nfs/symlink.c --- v2.3.6/linux/fs/nfs/symlink.c Tue Jun 8 22:11:58 1999 +++ linux/fs/nfs/symlink.c Sat Jun 19 11:45:29 1999 @@ -43,11 +43,14 @@ NULL, /* rename */ nfs_readlink, /* readlink */ nfs_follow_link, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* Symlink caching in the page cache is even more simplistic @@ -65,20 +68,18 @@ goto out; hash = page_hash(inode, 0); - page = __find_page(inode, 0, *hash); +repeat: + page = __find_lock_page(inode, 0, hash); if (page) { page_cache_free(page_cache); - goto out; + goto unlock_out; } page = page_cache_entry(page_cache); - atomic_inc(&page->count); - page->flags = ((page->flags & - ~((1 << PG_uptodate) | (1 << PG_error))) | - ((1 << PG_referenced) | (1 << PG_locked))); - page->offset = 0; - add_page_to_inode_queue(inode, page); - __add_page_to_hash_queue(page, hash); + if (add_to_page_cache_unique(page, inode, 0, hash)) { + page_cache_release(page); + goto repeat; + } /* We place the length at the beginning of the page, * in host byte order, followed by the string. The @@ -89,32 +90,28 @@ if (rpc_call(NFS_CLIENT(inode), NFSPROC_READLINK, &rl_args, NULL, 0) < 0) goto error; - set_bit(PG_uptodate, &page->flags); + SetPageUptodate(page); unlock_out: - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + UnlockPage(page); out: return page; error: - set_bit(PG_error, &page->flags); + SetPageError(page); goto unlock_out; } static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen) { struct inode *inode = dentry->d_inode; - struct page *page, **hash; + struct page *page; u32 *p, len; /* Caller revalidated the directory inode already. */ - hash = page_hash(inode, 0); - page = __find_page(inode, 0, *hash); + page = find_get_page(inode, 0); if (!page) goto no_readlink_page; - if (PageLocked(page)) - goto readlink_locked_wait; - if (!PageUptodate(page)) + if (!Page_Uptodate(page)) goto readlink_read_error; success: p = (u32 *) page_address(page); @@ -129,9 +126,7 @@ page = try_to_get_symlink_page(dentry, inode); if (!page) goto no_page; -readlink_locked_wait: - wait_on_page(page); - if (PageUptodate(page)) + if (Page_Uptodate(page)) goto success; readlink_read_error: page_cache_release(page); @@ -144,17 +139,14 @@ { struct dentry *result; struct inode *inode = dentry->d_inode; - struct page *page, **hash; + struct page *page; u32 *p; /* Caller revalidated the directory inode already. */ - hash = page_hash(inode, 0); - page = __find_page(inode, 0, *hash); + page = find_get_page(inode, 0); if (!page) goto no_followlink_page; - if (PageLocked(page)) - goto followlink_locked_wait; - if (!PageUptodate(page)) + if (!Page_Uptodate(page)) goto followlink_read_error; success: p = (u32 *) page_address(page); @@ -166,9 +158,7 @@ page = try_to_get_symlink_page(dentry, inode); if (!page) goto no_page; -followlink_locked_wait: - wait_on_page(page); - if (PageUptodate(page)) + if (Page_Uptodate(page)) goto success; followlink_read_error: page_cache_release(page); diff -u --recursive --new-file v2.3.6/linux/fs/nfs/write.c linux/fs/nfs/write.c --- v2.3.6/linux/fs/nfs/write.c Tue Jun 8 17:58:03 1999 +++ linux/fs/nfs/write.c Sat Jun 19 11:38:49 1999 @@ -55,6 +55,7 @@ #include #include #include +#include #define NFS_PARANOIA 1 #define NFSDBG_FACILITY NFSDBG_PAGECACHE @@ -93,6 +94,7 @@ u8 *buffer; struct nfs_fattr fattr; + lock_kernel(); dprintk("NFS: nfs_writepage_sync(%s/%s %d@%ld)\n", dentry->d_parent->d_name.name, dentry->d_name.name, count, page->offset + offset); @@ -110,7 +112,7 @@ if (result < 0) { /* Must mark the page invalid after I/O error */ - clear_bit(PG_uptodate, &page->flags); + ClearPageUptodate(page); goto io_error; } if (result != wsize) @@ -153,6 +155,7 @@ inode->i_ino, fattr.fileid); } + unlock_kernel(); return written? written : result; } @@ -463,7 +466,7 @@ * Ok, there's another user of this page with the new request.. * The IO completion will then free the page and the dentry. */ - atomic_inc(&page->count); + get_page(page); file->f_count++; /* Schedule request */ @@ -471,7 +474,7 @@ updated: if (req->wb_bytes == PAGE_SIZE) - set_bit(PG_uptodate, &page->flags); + SetPageUptodate(page); retval = count; if (synchronous) { @@ -486,7 +489,7 @@ } if (retval < 0) - clear_bit(PG_uptodate, &page->flags); + ClearPageUptodate(page); } free_write_request(req); @@ -682,7 +685,7 @@ rpc_release_task(task); if (WB_INVALIDATE(req)) - clear_bit(PG_uptodate, &page->flags); + ClearPageUptodate(page); __free_page(page); remove_write_request(&NFS_WRITEBACK(inode), req); diff -u --recursive --new-file v2.3.6/linux/fs/ntfs/fs.c linux/fs/ntfs/fs.c --- v2.3.6/linux/fs/ntfs/fs.c Tue Jun 8 10:47:58 1999 +++ linux/fs/ntfs/fs.c Sat Jun 19 11:45:28 1999 @@ -445,7 +445,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; @@ -628,7 +627,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; @@ -677,7 +675,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/pipe.c linux/fs/pipe.c --- v2.3.6/linux/fs/pipe.c Tue Jun 8 10:47:58 1999 +++ linux/fs/pipe.c Sat Jun 19 11:45:28 1999 @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -101,9 +102,7 @@ free = count; else free = 1; /* can't do it atomically, wait for any free space */ - up(&inode->i_sem); - if (down_interruptible(&inode->i_atomic_write)) { - down(&inode->i_sem); + if (down_interruptible(&inode->i_sem)) { return -ERESTARTSYS; } while (count>0) { @@ -144,8 +143,7 @@ inode->i_ctime = inode->i_mtime = CURRENT_TIME; mark_inode_dirty(inode); errout: - up(&inode->i_atomic_write); - down(&inode->i_sem); + up(&inode->i_sem); return written ? written : err; } @@ -249,8 +247,11 @@ static int pipe_release(struct inode * inode) { if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) { - free_page((unsigned long) PIPE_BASE(*inode)); - PIPE_BASE(*inode) = NULL; + struct pipe_inode_info *info = inode->i_pipe; + inode->i_pipe = NULL; + free_page((unsigned long) info->base); + kfree(info); + return 0; } wake_up_interruptible(&PIPE_WAIT(*inode)); return 0; @@ -404,36 +405,48 @@ { extern struct inode_operations pipe_inode_operations; struct inode *inode = get_empty_inode(); + unsigned long page; - if (inode) { - unsigned long page = __get_free_page(GFP_USER); + if (!inode) + goto fail_inode; - if (!page) { - iput(inode); - inode = NULL; - } else { - PIPE_BASE(*inode) = (char *) page; - inode->i_op = &pipe_inode_operations; - init_waitqueue_head(&PIPE_WAIT(*inode)); - PIPE_START(*inode) = PIPE_LEN(*inode) = 0; - PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; - PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; - PIPE_LOCK(*inode) = 0; - /* - * Mark the inode dirty from the very beginning, - * that way it will never be moved to the dirty - * list because "mark_inode_dirty()" will think - * that it already _is_ on the dirty list. - */ - inode->i_state = I_DIRTY; - inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR; - inode->i_uid = current->fsuid; - inode->i_gid = current->fsgid; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_blksize = PAGE_SIZE; - } - } + page = __get_free_page(GFP_USER); + + if (!page) + goto fail_iput; + + /* XXX */ + inode->i_pipe = kmalloc(sizeof(struct pipe_inode_info), GFP_KERNEL); + if (!inode->i_pipe) + goto fail_page; + + PIPE_BASE(*inode) = (char *) page; + inode->i_op = &pipe_inode_operations; + init_waitqueue_head(&PIPE_WAIT(*inode)); + PIPE_START(*inode) = PIPE_LEN(*inode) = 0; + PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0; + PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1; + PIPE_LOCK(*inode) = 0; + /* + * Mark the inode dirty from the very beginning, + * that way it will never be moved to the dirty + * list because "mark_inode_dirty()" will think + * that it already _is_ on the dirty list. + */ + inode->i_state = I_DIRTY; + inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_blksize = PAGE_SIZE; return inode; + +fail_page: + free_page(page); +fail_iput: + iput(inode); +fail_inode: + return NULL; } struct inode_operations pipe_inode_operations = { @@ -448,11 +461,14 @@ NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; int do_pipe(int *fd) @@ -513,6 +529,8 @@ put_unused_fd(i); close_f12_inode: free_page((unsigned long) PIPE_BASE(*inode)); + kfree(inode->i_pipe); + inode->i_pipe = NULL; iput(inode); close_f12: put_filp(f2); diff -u --recursive --new-file v2.3.6/linux/fs/proc/array.c linux/fs/proc/array.c --- v2.3.6/linux/fs/proc/array.c Tue May 11 16:30:45 1999 +++ linux/fs/proc/array.c Sat Jun 19 11:45:28 1999 @@ -348,7 +348,7 @@ len = sprintf(buffer, " total: used: free: shared: buffers: cached:\n" "Mem: %8lu %8lu %8lu %8lu %8lu %8lu\n" "Swap: %8lu %8lu %8lu\n", - i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, page_cache_size*PAGE_SIZE, + i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, atomic_read(&page_cache_size)*PAGE_SIZE, i.totalswap, i.totalswap-i.freeswap, i.freeswap); /* * Tagged format, for easy grepping and expansion. The above will go away @@ -359,14 +359,14 @@ "MemFree: %8lu kB\n" "MemShared: %8lu kB\n" "Buffers: %8lu kB\n" - "Cached: %8lu kB\n" + "Cached: %8u kB\n" "SwapTotal: %8lu kB\n" "SwapFree: %8lu kB\n", i.totalram >> 10, i.freeram >> 10, i.sharedram >> 10, i.bufferram >> 10, - page_cache_size << (PAGE_SHIFT - 10), + atomic_read(&page_cache_size) << (PAGE_SHIFT - 10), i.totalswap >> 10, i.freeswap >> 10); } @@ -975,7 +975,7 @@ ++*dirty; if (MAP_NR(pte_page(page)) >= max_mapnr) continue; - if (atomic_read(&mem_map[MAP_NR(pte_page(page))].count) > 1) + if (page_count(mem_map + MAP_NR(pte_page(page))) > 1) ++*shared; } while (address < end); } @@ -1326,6 +1326,9 @@ case PROC_IOPORTS: return get_ioport_list(page); + + case PROC_MEMORY: + return get_mem_list(page); #ifdef CONFIG_BLK_DEV_MD case PROC_MD: return get_md_status(page); @@ -1516,11 +1519,14 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static ssize_t arraylong_read(struct file * file, char * buf, @@ -1564,9 +1570,12 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/proc/base.c linux/fs/proc/base.c --- v2.3.6/linux/fs/proc/base.c Mon Aug 24 13:02:43 1998 +++ linux/fs/proc/base.c Sat Jun 19 11:45:28 1999 @@ -45,11 +45,14 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* diff -u --recursive --new-file v2.3.6/linux/fs/proc/fd.c linux/fs/proc/fd.c --- v2.3.6/linux/fs/proc/fd.c Fri Apr 23 21:20:38 1999 +++ linux/fs/proc/fd.c Sat Jun 19 11:45:28 1999 @@ -51,11 +51,14 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - proc_permission /* permission */ + proc_permission, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* diff -u --recursive --new-file v2.3.6/linux/fs/proc/generic.c linux/fs/proc/generic.c --- v2.3.6/linux/fs/proc/generic.c Mon Aug 24 13:02:44 1998 +++ linux/fs/proc/generic.c Sat Jun 19 11:45:28 1999 @@ -51,20 +51,23 @@ &proc_file_operations, /* default proc 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, /* readpage */ - NULL, /* writepage */ - NULL, /* bmap */ - NULL, /* truncate */ - NULL /* permission */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* @@ -83,11 +86,14 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/proc/kmsg.c linux/fs/proc/kmsg.c --- v2.3.6/linux/fs/proc/kmsg.c Tue May 11 14:37:40 1999 +++ linux/fs/proc/kmsg.c Sat Jun 19 11:45:28 1999 @@ -72,9 +72,12 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/proc/link.c linux/fs/proc/link.c --- v2.3.6/linux/fs/proc/link.c Tue Jun 8 10:47:58 1999 +++ linux/fs/proc/link.c Sat Jun 19 11:45:28 1999 @@ -49,11 +49,14 @@ NULL, /* rename */ proc_readlink, /* readlink */ proc_follow_link, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - proc_permission /* permission */ + proc_permission, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct dentry * proc_follow_link(struct dentry *dentry, diff -u --recursive --new-file v2.3.6/linux/fs/proc/mem.c linux/fs/proc/mem.c --- v2.3.6/linux/fs/proc/mem.c Wed Sep 23 15:24:37 1998 +++ linux/fs/proc/mem.c Sat Jun 19 11:45:28 1999 @@ -298,7 +298,7 @@ set_pte(dest_table, *src_table); mapnr = MAP_NR(pte_page(*src_table)); if (mapnr < max_mapnr) - atomic_inc(&mem_map[MAP_NR(pte_page(*src_table))].count); + get_page(mem_map + MAP_NR(pte_page(*src_table))); stmp += PAGE_SIZE; dtmp += PAGE_SIZE; @@ -336,9 +336,12 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - proc_permission /* permission */ + proc_permission, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/proc/net.c linux/fs/proc/net.c --- v2.3.6/linux/fs/proc/net.c Mon Aug 24 13:14:09 1998 +++ linux/fs/proc/net.c Sat Jun 19 11:45:28 1999 @@ -113,9 +113,12 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/proc/omirr.c linux/fs/proc/omirr.c --- v2.3.6/linux/fs/proc/omirr.c Tue May 11 23:07:08 1999 +++ linux/fs/proc/omirr.c Sat Jun 19 11:45:28 1999 @@ -277,22 +277,24 @@ }; struct inode_operations proc_omirr_inode_operations = { - &omirr_operations, - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* bmap */ - NULL, /* truncate */ - NULL, /* permission */ - NULL /* smap */ + &omirr_operations, + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/proc/proc_devtree.c linux/fs/proc/proc_devtree.c --- v2.3.6/linux/fs/proc/proc_devtree.c Sat Sep 19 13:43:36 1998 +++ linux/fs/proc/proc_devtree.c Sat Jun 19 11:45:29 1999 @@ -57,12 +57,14 @@ NULL, /* rename */ devtree_readlink, /* readlink */ devtree_follow_link, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct dentry *devtree_follow_link(struct dentry *dentry, diff -u --recursive --new-file v2.3.6/linux/fs/proc/root.c linux/fs/proc/root.c --- v2.3.6/linux/fs/proc/root.c Mon Jun 7 12:20:50 1999 +++ linux/fs/proc/root.c Sat Jun 19 11:45:29 1999 @@ -71,11 +71,14 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* @@ -94,11 +97,14 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* @@ -136,11 +142,14 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; /* @@ -293,11 +302,14 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; struct proc_dir_entry proc_openprom = { @@ -478,11 +490,14 @@ NULL, /* rename */ proc_self_readlink, /* readlink */ proc_self_follow_link, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct inode_operations proc_link_inode_operations = { @@ -498,11 +513,14 @@ NULL, /* rename */ proc_readlink, /* readlink */ proc_follow_link, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct proc_dir_entry proc_root_loadavg = { @@ -621,6 +639,11 @@ S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_array_inode_operations }; +static struct proc_dir_entry proc_root_memory = { + PROC_MEMORY, 6, "memory", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_array_inode_operations +}; static struct proc_dir_entry proc_root_cmdline = { PROC_CMDLINE, 7, "cmdline", S_IFREG | S_IRUGO, 1, 0, 0, @@ -709,6 +732,7 @@ proc_register(&proc_root, &proc_root_fs); proc_register(&proc_root, &proc_root_dma); proc_register(&proc_root, &proc_root_ioports); + proc_register(&proc_root, &proc_root_memory); proc_register(&proc_root, &proc_root_cmdline); #ifdef CONFIG_RTC proc_register(&proc_root, &proc_root_rtc); diff -u --recursive --new-file v2.3.6/linux/fs/proc/scsi.c linux/fs/proc/scsi.c --- v2.3.6/linux/fs/proc/scsi.c Mon Aug 24 13:14:10 1998 +++ linux/fs/proc/scsi.c Sat Jun 19 11:45:29 1999 @@ -59,23 +59,26 @@ * proc directories can do almost nothing.. */ struct inode_operations proc_scsi_inode_operations = { - &proc_scsi_operations, /* default scsi directory file-ops */ - NULL, /* create */ - proc_lookup, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* bmap */ - NULL, /* truncate */ - NULL /* permission */ +&proc_scsi_operations, /* default scsi directory file-ops */ + NULL, /* create */ + proc_lookup, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; int get_not_present_info(char *buffer, char **start, off_t offset, int length) diff -u --recursive --new-file v2.3.6/linux/fs/proc/sysvipc.c linux/fs/proc/sysvipc.c --- v2.3.6/linux/fs/proc/sysvipc.c Mon Jun 7 12:20:50 1999 +++ linux/fs/proc/sysvipc.c Sat Jun 19 11:45:29 1999 @@ -118,21 +118,24 @@ * proc directories can do almost nothing.. */ struct inode_operations proc_sysvipc_inode_operations = { - &proc_sysvipc_operations, /* default net 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, /* readpage */ - NULL, /* writepage */ - NULL, /* bmap */ - NULL, /* truncate */ - NULL /* permission */ + &proc_sysvipc_operations, /* default net 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, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/read_write.c linux/fs/read_write.c --- v2.3.6/linux/fs/read_write.c Sun Dec 27 10:52:09 1998 +++ linux/fs/read_write.c Wed Jun 16 19:26:27 1999 @@ -166,9 +166,7 @@ if (!file->f_op || !(write = file->f_op->write)) goto out; - down(&inode->i_sem); ret = write(file, buf, count, &file->f_pos); - up(&inode->i_sem); out: fput(file); bad_file: @@ -304,9 +302,7 @@ if (!file) goto bad_file; if (file->f_op && file->f_op->write && (file->f_mode & FMODE_WRITE)) { - down(&file->f_dentry->d_inode->i_sem); ret = do_readv_writev(VERIFY_READ, file, vector, count); - up(&file->f_dentry->d_inode->i_sem); } fput(file); @@ -376,10 +372,7 @@ if (pos < 0) goto out; - down(&file->f_dentry->d_inode->i_sem); ret = write(file, buf, count, &pos); - up(&file->f_dentry->d_inode->i_sem); - out: fput(file); bad_file: diff -u --recursive --new-file v2.3.6/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c --- v2.3.6/linux/fs/smbfs/dir.c Tue Jun 8 10:47:58 1999 +++ linux/fs/smbfs/dir.c Sat Jun 19 11:45:28 1999 @@ -65,7 +65,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ smb_revalidate_inode, /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/smbfs/file.c linux/fs/smbfs/file.c --- v2.3.6/linux/fs/smbfs/file.c Wed Jun 2 13:46:59 1999 +++ linux/fs/smbfs/file.c Sat Jun 19 11:45:28 1999 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -271,8 +272,11 @@ bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes); status = -EFAULT; - if (bytes) + if (bytes) { + lock_kernel(); status = smb_updatepage(file, page, offset, bytes); + unlock_kernel(); + } return status; } @@ -406,6 +410,5 @@ NULL, /* truncate */ smb_file_permission, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ smb_revalidate_inode, /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c --- v2.3.6/linux/fs/smbfs/inode.c Tue Jun 8 10:47:58 1999 +++ linux/fs/smbfs/inode.c Wed Jun 16 19:26:27 1999 @@ -376,9 +376,6 @@ *mnt = *((struct smb_mount_data *) raw_data); /* ** temp ** pass config flags in file mode */ mnt->version = (mnt->file_mode >> 9); -#ifdef CONFIG_SMB_WIN95 - mnt->version |= SMB_FIX_WIN95; -#endif mnt->file_mode &= (S_IRWXU | S_IRWXG | S_IRWXO); mnt->file_mode |= S_IFREG; mnt->dir_mode &= (S_IRWXU | S_IRWXG | S_IRWXO); @@ -387,8 +384,6 @@ /* * Display the enabled options */ - if (mnt->version & SMB_FIX_WIN95) - printk("SMBFS: Win 95 bug fixes enabled\n"); if (mnt->version & SMB_FIX_OLDATTR) printk("SMBFS: Using core getattr (Win 95 speedup)\n"); else if (mnt->version & SMB_FIX_DIRATTR) diff -u --recursive --new-file v2.3.6/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c --- v2.3.6/linux/fs/smbfs/proc.c Tue Nov 3 21:56:58 1998 +++ linux/fs/smbfs/proc.c Wed Jun 16 19:26:27 1999 @@ -39,6 +39,9 @@ #define SMB_DIRINFO_SIZE 43 #define SMB_STATUS_SIZE 21 +/* yes, this deliberately has two parts */ +#define DENTRY_PATH(dentry) (dentry)->d_parent->d_name.name,(dentry)->d_name.name + static int smb_proc_setattr_ext(struct smb_sb_info *, struct inode *, struct smb_fattr *); static inline int @@ -174,24 +177,22 @@ /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ -extern struct timezone sys_tz; - static time_t -utc2local(time_t time) +utc2local(struct smb_sb_info *server, time_t time) { - return time - sys_tz.tz_minuteswest * 60 - (sys_tz.tz_dsttime ? 3600 :0); + return time - server->opt.serverzone*60; } static time_t -local2utc(time_t time) +local2utc(struct smb_sb_info *server, time_t time) { - return time + sys_tz.tz_minuteswest * 60 + (sys_tz.tz_dsttime ? 3600 : 0); + return time + server->opt.serverzone*60; } /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ static time_t -date_dos2unix(__u16 date, __u16 time) +date_dos2unix(struct smb_sb_info *server, __u16 date, __u16 time) { int month, year; time_t secs; @@ -202,18 +203,19 @@ ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653); /* days since 1.1.70 plus 80's leap day */ - return local2utc(secs); + return local2utc(server, secs); } /* Convert linear UNIX date to a MS-DOS time/date pair. */ static void -date_unix2dos(int unix_date, __u16 *date, __u16 *time) +date_unix2dos(struct smb_sb_info *server, + int unix_date, __u16 *date, __u16 *time) { int day, year, nl_day, month; - unix_date = utc2local(unix_date); + unix_date = utc2local(server, unix_date); *time = (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) + (((unix_date / 3600) % 24) << 11); @@ -355,6 +357,11 @@ int error = server->err; char *class = "Unknown"; +#ifdef SMBFS_DEBUG_VERBOSE + printk("smb_errno: errcls %d code %d from command 0x%x\n", + errcls, error, SMB_CMD(server->packet)); +#endif + if (errcls == ERRDOS) switch (error) { @@ -456,7 +463,7 @@ class = "ERRCMD"; err_unknown: - printk("smb_errno: class %s, code %d from command %x\n", + printk("smb_errno: class %s, code %d from command 0x%x\n", class, error, SMB_CMD(server->packet)); return EIO; } @@ -646,9 +653,27 @@ server->generation += 1; server->state = CONN_VALID; error = 0; + + /* check if we have an old smbmount that uses seconds for the + serverzone */ + if (server->opt.serverzone > 12*60 || server->opt.serverzone < -12*60) + server->opt.serverzone /= 60; + + /* now that we have an established connection we can detect the server + type and enable bug workarounds */ + if (server->opt.protocol == SMB_PROTOCOL_NT1 && + (server->opt.max_xmit < 0x1000) && + !(server->opt.capabilities & SMB_CAP_NT_SMBS)) { + server->mnt->version |= SMB_FIX_WIN95; #ifdef SMBFS_DEBUG_VERBOSE -printk("smb_newconn: protocol=%d, max_xmit=%d, pid=%d\n", -server->opt.protocol, server->opt.max_xmit, server->conn_pid); + printk("smb_newconn: detected WIN95 server\n"); +#endif + } + +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_newconn: protocol=%d, max_xmit=%d, pid=%d capabilities=0x%x\n", + server->opt.protocol, server->opt.max_xmit, server->conn_pid, + server->opt.capabilities); #endif out: @@ -755,7 +780,7 @@ { #ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_open: %s/%s R/W failed, error=%d, retrying R/O\n", -dentry->d_parent->d_name.name, dentry->d_name.name, error); + DENTRY_PATH(dentry), error); #endif mode = read_only; goto retry; @@ -789,7 +814,7 @@ if (!inode) { printk("smb_open: no inode for dentry %s/%s\n", - dentry->d_parent->d_name.name, dentry->d_name.name); + DENTRY_PATH(dentry)); goto out; } @@ -810,7 +835,7 @@ { #ifdef SMBFS_PARANOIA printk("smb_open: %s/%s open failed, result=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, result); + DENTRY_PATH(dentry), result); #endif goto out; } @@ -829,8 +854,7 @@ { #ifdef SMBFS_PARANOIA printk("smb_open: %s/%s access denied, access=%x, wish=%x\n", -dentry->d_parent->d_name.name, dentry->d_name.name, -inode->u.smbfs_i.access, wish); + DENTRY_PATH(dentry), inode->u.smbfs_i.access, wish); #endif result = -EACCES; } @@ -845,7 +869,7 @@ { smb_setup_header(server, SMBclose, 3, 0); WSET(server->packet, smb_vwv0, fileid); - DSET(server->packet, smb_vwv1, utc2local(mtime)); + DSET(server->packet, smb_vwv1, utc2local(server, mtime)); return smb_request_ok(server, SMBclose, 0, 0); } @@ -946,7 +970,7 @@ { #ifdef SMBFS_DEBUG_VERBOSE printk("smb_close_dentry: closing %s/%s, count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); + DENTRY_PATH(dentry), dentry->d_count); #endif smb_proc_close_inode(server, ino); } @@ -954,7 +978,7 @@ } #ifdef SMBFS_DEBUG_VERBOSE printk("smb_close_dentry: closed %s/%s, count=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); + DENTRY_PATH(dentry), dentry->d_count); #endif } } @@ -1014,7 +1038,7 @@ out: #ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_read: file %s/%s, count=%d, result=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, count, result); + DENTRY_PATH(dentry), count, result); #endif smb_unlock_server(server); return result; @@ -1029,8 +1053,7 @@ #if SMBFS_DEBUG_VERBOSE printk("smb_proc_write: file %s/%s, count=%d@%ld, packet_size=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, -count, offset, server->packet_size); + DENTRY_PATH(dentry), count, offset, server->packet_size); #endif smb_lock_server(server); p = smb_setup_header(server, SMBwrite, 5, count + 3); @@ -1063,7 +1086,7 @@ retry: p = smb_setup_header(server, SMBcreate, 3, 0); WSET(server->packet, smb_vwv0, attr); - DSET(server->packet, smb_vwv1, utc2local(ctime)); + DSET(server->packet, smb_vwv1, utc2local(server, ctime)); *p++ = 4; p = smb_encode_path(server, p, dentry, NULL); smb_setup_bcc(server, p); @@ -1323,7 +1346,7 @@ #ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_readdir_short: %s/%s, pos=%d\n", -dir->d_parent->d_name.name, dir->d_name.name, fpos); + DENTRY_PATH(dir), fpos); #endif smb_lock_server(server); @@ -1804,15 +1827,15 @@ */ date = WVAL(resp_data, 0); time = WVAL(resp_data, 2); - fattr->f_ctime = date_dos2unix(date, time); + fattr->f_ctime = date_dos2unix(server, date, time); date = WVAL(resp_data, 4); time = WVAL(resp_data, 6); - fattr->f_atime = date_dos2unix(date, time); + fattr->f_atime = date_dos2unix(server, date, time); date = WVAL(resp_data, 8); time = WVAL(resp_data, 10); - fattr->f_mtime = date_dos2unix(date, time); + fattr->f_mtime = date_dos2unix(server, date, time); #ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_getattr_ff: name=%s, date=%x, time=%x, mtime=%ld\n", mask, date, time, fattr->f_mtime); @@ -1831,7 +1854,7 @@ */ static int smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, - struct smb_fattr *fattr) + struct smb_fattr *fattr) { int result; char *p; @@ -1849,13 +1872,13 @@ goto out; } fattr->attr = WVAL(server->packet, smb_vwv0); - fattr->f_mtime = local2utc(DVAL(server->packet, smb_vwv1)); + fattr->f_mtime = local2utc(server, DVAL(server->packet, smb_vwv1)); fattr->f_size = DVAL(server->packet, smb_vwv3); fattr->f_ctime = fattr->f_mtime; fattr->f_atime = fattr->f_mtime; #ifdef SMBFS_DEBUG_TIMESTAMP printk("getattr_core: %s/%s, mtime=%ld\n", -dir->d_name.name, name->name, fattr->f_mtime); + DENTRY_PATH(dir), fattr->f_mtime); #endif result = 0; @@ -1926,18 +1949,18 @@ } date = WVAL(resp_data, off_date); time = WVAL(resp_data, off_time); - attr->f_ctime = date_dos2unix(date, time); + attr->f_ctime = date_dos2unix(server, date, time); date = WVAL(resp_data, 4 + off_date); time = WVAL(resp_data, 4 + off_time); - attr->f_atime = date_dos2unix(date, time); + attr->f_atime = date_dos2unix(server, date, time); date = WVAL(resp_data, 8 + off_date); time = WVAL(resp_data, 8 + off_time); - attr->f_mtime = date_dos2unix(date, time); + attr->f_mtime = date_dos2unix(server, date, time); #ifdef SMBFS_DEBUG_TIMESTAMP printk("getattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", -dir->d_name.name, name->name, date, time, attr->f_mtime); + DENTRY_PATH(dir), date, time, attr->f_mtime); #endif attr->f_size = DVAL(resp_data, 12); attr->attr = WVAL(resp_data, 20); @@ -1980,6 +2003,7 @@ return result; } + /* * Called with the server locked. Because of bugs in the * core protocol, we use this only to set attributes. See @@ -1994,7 +2018,7 @@ */ static int smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, - __u16 attr) + __u16 attr) { char *p; int result; @@ -2009,11 +2033,6 @@ WSET(server->packet, smb_vwv6, 0); WSET(server->packet, smb_vwv7, 0); *p++ = 4; - /* - * Samba uses three leading '\', so we'll do it too. - */ - *p++ = '\\'; - *p++ = '\\'; p = smb_encode_path(server, p, dentry, NULL); *p++ = 4; *p++ = 0; @@ -2044,7 +2063,7 @@ #ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_setattr: setting %s/%s, open=%d\n", -dir->d_parent->d_name.name, dir->d_name.name, smb_is_open(dir->d_inode)); + DENTRY_PATH(dir), smb_is_open(dir->d_inode)); #endif smb_lock_server(server); result = smb_proc_setattr_core(server, dir, fattr->attr); @@ -2069,10 +2088,10 @@ /* We don't change the creation time */ WSET(server->packet, smb_vwv1, 0); WSET(server->packet, smb_vwv2, 0); - date_unix2dos(fattr->f_atime, &date, &time); + date_unix2dos(server, fattr->f_atime, &date, &time); WSET(server->packet, smb_vwv3, date); WSET(server->packet, smb_vwv4, time); - date_unix2dos(fattr->f_mtime, &date, &time); + date_unix2dos(server, fattr->f_mtime, &date, &time); WSET(server->packet, smb_vwv5, date); WSET(server->packet, smb_vwv6, time); #ifdef SMBFS_DEBUG_TIMESTAMP @@ -2119,15 +2138,15 @@ WSET(data, 0, 0); /* creation time */ WSET(data, 2, 0); - date_unix2dos(fattr->f_atime, &date, &time); + date_unix2dos(server, fattr->f_atime, &date, &time); WSET(data, 4, date); WSET(data, 6, time); - date_unix2dos(fattr->f_mtime, &date, &time); + date_unix2dos(server, fattr->f_mtime, &date, &time); WSET(data, 8, date); WSET(data, 10, time); #ifdef SMBFS_DEBUG_TIMESTAMP printk("setattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", -dir->d_parent->d_name.name, dir->d_name.name, date, time, fattr->f_mtime); + DENTRY_PATH(dir), date, time, fattr->f_mtime); #endif DSET(data, 12, 0); /* size */ DSET(data, 16, 0); /* blksize */ @@ -2174,10 +2193,12 @@ #ifdef SMBFS_DEBUG_VERBOSE printk("smb_proc_settime: setting %s/%s, open=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode)); + DENTRY_PATH(dentry), smb_is_open(inode)); #endif smb_lock_server(server); - if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) + /* setting the time on a Win95 server fails (tridge) */ + if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 && + !(server->mnt->version & SMB_FIX_WIN95)) { if (smb_is_open(inode) && inode->u.smbfs_i.access != SMB_O_RDONLY) @@ -2194,12 +2215,13 @@ { /* * Set the mtime by opening and closing the file. + * Note that the file is opened read-only, but this + * still allows us to set the date (tridge) */ result = -EACCES; if (!smb_is_open(inode)) - smb_proc_open(server, dentry, SMB_O_WRONLY); - if (smb_is_open(inode) && - inode->u.smbfs_i.access != SMB_O_RDONLY) + smb_proc_open(server, dentry, SMB_O_RDONLY); + if (smb_is_open(inode)) { inode->i_mtime = fattr->f_mtime; result = smb_proc_close_inode(server, inode); diff -u --recursive --new-file v2.3.6/linux/fs/super.c linux/fs/super.c --- v2.3.6/linux/fs/super.c Tue Jun 8 10:47:58 1999 +++ linux/fs/super.c Thu Jun 17 23:18:29 1999 @@ -918,13 +918,6 @@ int retval; struct vfsmount *vfsmnt; - /* - * Invalidate the inodes, as some mount options may be changed. - * N.B. If we are changing media, we should check the return - * from invalidate_inodes ... can't allow _any_ open files. - */ - invalidate_inodes(sb); - if (!(flags & MS_RDONLY) && sb->s_dev && is_read_only(sb->s_dev)) return -EACCES; /*flags |= MS_RDONLY;*/ @@ -941,6 +934,14 @@ vfsmnt = lookup_vfsmnt(sb->s_dev); if (vfsmnt) vfsmnt->mnt_flags = sb->s_flags; + + /* + * Invalidate the inodes, as some mount options may be changed. + * N.B. If we are changing media, we should check the return + * from invalidate_inodes ... can't allow _any_ open files. + */ + invalidate_inodes(sb); + return 0; } diff -u --recursive --new-file v2.3.6/linux/fs/sysv/file.c linux/fs/sysv/file.c --- v2.3.6/linux/fs/sysv/file.c Mon Aug 24 13:02:44 1998 +++ linux/fs/sysv/file.c Sat Jun 19 12:15:14 1999 @@ -33,7 +33,51 @@ #include #include -static ssize_t sysv_file_write(struct file *, const char *, size_t, loff_t *); +static int sysv_writepage (struct file * file, struct page * page) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + unsigned long block; + int *p, nr[PAGE_SIZE/512]; + int i, err, created; + struct buffer_head *bh; + + i = PAGE_SIZE >> inode->i_sb->sv_block_size_bits; + block = page->offset >> inode->i_sb->sv_block_size_bits; + p = nr; + bh = page->buffers; + do { + if (bh && bh->b_blocknr) + *p = bh->b_blocknr; + else + *p = sysv_getblk_block (inode, block, 1, &err, &created); + if (!*p) + return -EIO; + i--; + block++; + p++; + if (bh) + bh = bh->b_this_page; + } while (i > 0); + + /* IO start */ + brw_page(WRITE, page, inode->i_dev, nr, inode->i_sb->sv_block_size, 1); + return 0; +} + +static long sysv_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +{ + return block_write_one_page(file, page, offset, bytes, buf, sysv_getblk_block); +} + +/* + * Write to a file (through the page cache). + */ +static ssize_t +sysv_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + return generic_file_write(file, buf, count, ppos, sysv_write_one_page); +} /* * We have mostly NULLs here: the current defaults are OK for @@ -41,7 +85,7 @@ */ static struct file_operations sysv_file_operations = { NULL, /* lseek - default */ - sysv_file_read, /* read */ + generic_file_read, /* read */ sysv_file_write, /* write */ NULL, /* readdir - bad */ NULL, /* poll - default */ @@ -50,7 +94,10 @@ NULL, /* no special open is needed */ NULL, /* flush */ NULL, /* release */ - sysv_sync_file /* fsync */ + sysv_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ }; struct inode_operations sysv_file_inode_operations = { @@ -67,208 +114,11 @@ NULL, /* readlink */ NULL, /* follow_link */ generic_readpage, /* readpage */ - NULL, /* writepage */ + sysv_writepage, /* writepage */ sysv_bmap, /* bmap */ sysv_truncate, /* truncate */ - NULL /* permission */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* revalidate */ + block_flushpage, /* flushpage */ }; - -ssize_t sysv_file_read(struct file * filp, char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = filp->f_dentry->d_inode; - struct super_block * sb = inode->i_sb; - ssize_t read,left,chars; - size_t block; - ssize_t blocks, offset; - int bhrequest, uptodate; - struct buffer_head ** bhb, ** bhe; - struct buffer_head * bhreq[NBUF]; - struct buffer_head * buflist[NBUF]; - size_t size; - - if (!inode) { - printk("sysv_file_read: inode = NULL\n"); - return -EINVAL; - } - if (!S_ISREG(inode->i_mode)) { - printk("sysv_file_read: mode = %07o\n",inode->i_mode); - return -EINVAL; - } - offset = *ppos; - size = inode->i_size; - if (offset > size) - left = 0; - else - left = size - offset; - if (left > count) - left = count; - if (left <= 0) - return 0; - read = 0; - block = offset >> sb->sv_block_size_bits; - offset &= sb->sv_block_size_1; - size = (size + sb->sv_block_size_1) >> sb->sv_block_size_bits; - blocks = (left + offset + sb->sv_block_size_1) >> sb->sv_block_size_bits; - bhb = bhe = buflist; - if (filp->f_reada) { - blocks += read_ahead[MAJOR(inode->i_dev)] >> (sb->sv_block_size_bits - 9); - if (block + blocks > size) - blocks = size - block; - } - - /* We do this in a two stage process. We first try to request - as many blocks as we can, then we wait for the first one to - complete, and then we try to wrap up as many as are actually - done. This routine is rather generic, in that it can be used - in a filesystem by substituting the appropriate function in - for getblk. - - This routine is optimized to make maximum use of the various - buffers and caches. - */ - - do { - bhrequest = 0; - uptodate = 1; - while (blocks) { - --blocks; - *bhb = sysv_getblk(inode, block++, 0); - if (*bhb && !buffer_uptodate(*bhb)) { - uptodate = 0; - bhreq[bhrequest++] = *bhb; - } - - if (++bhb == &buflist[NBUF]) - bhb = buflist; - - /* If the block we have on hand is uptodate, go ahead - and complete processing. */ - if (uptodate) - break; - if (bhb == bhe) - break; - } - - /* Now request them all */ - if (bhrequest) - ll_rw_block(READ, bhrequest, bhreq); - - do { /* Finish off all I/O that has actually completed */ - if (*bhe) { - wait_on_buffer(*bhe); - if (!buffer_uptodate(*bhe)) { /* read error? */ - brelse(*bhe); - if (++bhe == &buflist[NBUF]) - bhe = buflist; - left = 0; - break; - } - } - if (left < sb->sv_block_size - offset) - chars = left; - else - chars = sb->sv_block_size - offset; - *ppos += chars; - left -= chars; - read += chars; - if (*bhe) { - copy_to_user(buf,offset+(*bhe)->b_data,chars); - brelse(*bhe); - buf += chars; - } else { - while (chars-- > 0) - put_user(0,buf++); - } - offset = 0; - if (++bhe == &buflist[NBUF]) - bhe = buflist; - } while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe))); - } while (left > 0); - -/* Release the read-ahead blocks */ - while (bhe != bhb) { - brelse(*bhe); - if (++bhe == &buflist[NBUF]) - bhe = buflist; - }; - if (!read) - return -EIO; - filp->f_reada = 1; - if (!IS_RDONLY(inode)) { - inode->i_atime = CURRENT_TIME; - mark_inode_dirty(inode); - } - return read; -} - -static ssize_t sysv_file_write(struct file * filp, const char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = filp->f_dentry->d_inode; - struct super_block * sb = inode->i_sb; - off_t pos; - ssize_t written, c; - struct buffer_head * bh; - char * p; - - if (!inode) { - printk("sysv_file_write: inode = NULL\n"); - return -EINVAL; - } - if (!S_ISREG(inode->i_mode)) { - printk("sysv_file_write: mode = %07o\n",inode->i_mode); - return -EINVAL; - } -/* - * OK, append may not work when many processes are writing at the same time - * but so what. That way leads to madness anyway. - * But we need to protect against simultaneous truncate as we may end up - * writing our data into blocks that have meanwhile been incorporated into - * the freelist, thereby trashing the freelist. - */ - if (filp->f_flags & O_APPEND) - pos = inode->i_size; - else - pos = *ppos; - written = 0; - while (written> sb->sv_block_size_bits, 1); - if (!bh) { - if (!written) - written = -ENOSPC; - break; - } - c = sb->sv_block_size - (pos & sb->sv_block_size_1); - if (c > count-written) - c = count-written; - if (c != sb->sv_block_size && !buffer_uptodate(bh)) { - ll_rw_block(READ, 1, &bh); - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) { - brelse(bh); - if (!written) - written = -EIO; - break; - } - } - /* now either c==sb->sv_block_size or buffer_uptodate(bh) */ - p = (pos & sb->sv_block_size_1) + bh->b_data; - copy_from_user(p, buf, c); - update_vm_cache(inode, pos, p, c); - pos += c; - if (pos > inode->i_size) { - inode->i_size = pos; - mark_inode_dirty(inode); - } - written += c; - buf += c; - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); - brelse(bh); - } - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - *ppos = pos; - mark_inode_dirty(inode); - return written; -} diff -u --recursive --new-file v2.3.6/linux/fs/sysv/inode.c linux/fs/sysv/inode.c --- v2.3.6/linux/fs/sysv/inode.c Tue Jun 8 10:47:58 1999 +++ linux/fs/sysv/inode.c Wed Jun 16 19:26:27 1999 @@ -657,7 +657,8 @@ /* Access selected blocks of regular files (or directories) */ -static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create) +static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create, + int metadata, int *phys_block, int *created) { struct super_block *sb; u32 tmp; @@ -669,31 +670,48 @@ repeat: tmp = *p; if (tmp) { - result = sv_getblk(sb, inode->i_dev, tmp); - if (tmp == *p) - return result; - brelse(result); - goto repeat; + if (metadata) { + result = sv_getblk(sb, inode->i_dev, tmp); + if (tmp == *p) + return result; + brelse(result); + goto repeat; + } else { + *phys_block = tmp; + return NULL; + } } if (!create) return NULL; tmp = sysv_new_block(sb); if (!tmp) return NULL; - result = sv_getblk(sb, inode->i_dev, tmp); - if (*p) { - sysv_free_block(sb,tmp); - brelse(result); - goto repeat; + if (metadata) { + result = sv_getblk(sb, inode->i_dev, tmp); + if (*p) { + sysv_free_block(sb, tmp); + brelse(result); + goto repeat; + } + } else { + if (*p) { + sysv_free_block(sb, tmp); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *created = 1; } *p = tmp; + inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); return result; } static struct buffer_head * block_getblk(struct inode * inode, - struct buffer_head * bh, int nr, int create) + struct buffer_head * bh, int nr, int create, + int metadata, int *phys_block, int *created) { struct super_block *sb; u32 tmp, block; @@ -717,13 +735,19 @@ if (sb->sv_convert) block = from_coh_ulong(block); if (tmp) { - result = sv_getblk(sb, bh->b_dev, block); - if (tmp == *p) { + if (metadata) { + result = sv_getblk(sb, bh->b_dev, block); + if (tmp == *p) { + brelse(bh); + return result; + } + brelse(result); + goto repeat; + } else { + *phys_block = tmp; brelse(bh); - return result; + return NULL; } - brelse(result); - goto repeat; } if (!create) { brelse(bh); @@ -734,11 +758,17 @@ brelse(bh); return NULL; } - result = sv_getblk(sb, bh->b_dev, block); - if (*p) { - sysv_free_block(sb,block); - brelse(result); - goto repeat; + if (metadata) { + result = sv_getblk(sb, bh->b_dev, block); + if (*p) { + sysv_free_block(sb,block); + brelse(result); + goto repeat; + } + } else { + *phys_block = tmp; + result = NULL; + *created = 1; } *p = (sb->sv_convert ? to_coh_ulong(block) : block); mark_buffer_dirty(bh, 1); @@ -746,37 +776,74 @@ return result; } -struct buffer_head * sysv_getblk(struct inode * inode, unsigned int block, int create) +int sysv_getblk_block(struct inode *inode, long block, int create, + int *err, int *created) { - struct super_block * sb = inode->i_sb; - struct buffer_head * bh; + struct super_block *sb = inode->i_sb; + struct buffer_head *bh, *tmp; + int phys_block; - if (block < 10) - return inode_getblk(inode,block,create); + *err = -EIO; + if (block < 0) { + printk("sysv_getblk: block<0"); + return 0; + } + if (block > sb->sv_ind_per_block_3) { + printk("sysv_getblk: block>big"); + return 0; + } + if (block < 10) { + tmp = inode_getblk(inode, block, create, + 0, &phys_block, created); + goto out; + } block -= 10; if (block < sb->sv_ind_per_block) { - bh = inode_getblk(inode,10,create); - return block_getblk(inode, bh, block, create); + bh = inode_getblk(inode, 10, create, 1, NULL, NULL); + tmp = block_getblk(inode, bh, block, create, + 0, &phys_block, created); + goto out; } block -= sb->sv_ind_per_block; if (block < sb->sv_ind_per_block_2) { - bh = inode_getblk(inode,11,create); - bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_bits, create); - return block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create); + bh = inode_getblk(inode, 11, create, 1, NULL, NULL); + bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_bits, create, + 1, NULL, NULL); + tmp = block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create, + 0, &phys_block, created); + goto out; } block -= sb->sv_ind_per_block_2; - if (block < sb->sv_ind_per_block_3) { - bh = inode_getblk(inode,12,create); - bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_2_bits, create); - bh = block_getblk(inode, bh, (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1, create); - return block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create); - } - if ((int)block<0) { - printk("sysv_getblk: block<0"); - return NULL; + bh = inode_getblk(inode, 12, create, 1, NULL, NULL); + bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_2_bits, create, + 1, NULL, NULL); + bh = block_getblk(inode, bh, + (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1, + create, 1, NULL, NULL); + tmp = block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create, + 0, &phys_block, created); + +out: + *err = 0; + return phys_block; +} + +struct buffer_head *sysv_getblk (struct inode *inode, unsigned int block, int create) +{ + struct buffer_head *tmp = NULL; + int phys_block; + int err, created; + + phys_block = sysv_getblk_block(inode, block, create, &err, &created); + if (phys_block) { + tmp = getblk(inode->i_dev, phys_block, BLOCK_SIZE); + if (created) { + memset(tmp->b_data, 0, BLOCK_SIZE); + mark_buffer_uptodate(tmp, 1); + mark_buffer_dirty(tmp, 1); + } } - printk("sysv_getblk: block>big"); - return NULL; + return tmp; } struct buffer_head * sysv_file_bread(struct inode * inode, int block, int create) diff -u --recursive --new-file v2.3.6/linux/fs/sysv/truncate.c linux/fs/sysv/truncate.c --- v2.3.6/linux/fs/sysv/truncate.c Sun Sep 13 10:27:07 1998 +++ linux/fs/sysv/truncate.c Wed Jun 16 19:26:27 1999 @@ -35,6 +35,9 @@ * general case (size = XXX). I hope. */ +#define DATA_BUFFER_USED(bh) \ + ((bh->b_count > 1) || buffer_locked(bh)) + /* We throw away any data beyond inode->i_size. */ static int trunc_direct(struct inode * inode) @@ -58,7 +61,7 @@ brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || (block != *p)) { + if ((bh && DATA_BUFFER_USED(bh)) || (block != *p)) { retry = 1; brelse(bh); continue; @@ -115,7 +118,7 @@ brelse(bh); goto repeat; } - if ((bh && bh->b_count != 1) || (tmp != *ind)) { + if ((bh && DATA_BUFFER_USED(bh)) || (tmp != *ind)) { retry = 1; brelse(bh); continue; @@ -128,7 +131,7 @@ for (i = 0; i < sb->sv_ind_per_block; i++) if (((sysv_zone_t *) indbh->b_data)[i]) goto done; - if ((indbh->b_count != 1) || (indtmp != *p)) { + if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) { brelse(indbh); return 1; } @@ -185,7 +188,7 @@ for (i = 0; i < sb->sv_ind_per_block; i++) if (((sysv_zone_t *) indbh->b_data)[i]) goto done; - if ((indbh->b_count != 1) || (indtmp != *p)) { + if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) { brelse(indbh); return 1; } @@ -242,7 +245,7 @@ for (i = 0; i < sb->sv_ind_per_block; i++) if (((sysv_zone_t *) indbh->b_data)[i]) goto done; - if ((indbh->b_count != 1) || (indtmp != *p)) { + if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) { brelse(indbh); return 1; } diff -u --recursive --new-file v2.3.6/linux/fs/ufs/file.c linux/fs/ufs/file.c --- v2.3.6/linux/fs/ufs/file.c Thu Jan 14 10:31:41 1999 +++ linux/fs/ufs/file.c Sat Jun 19 12:15:14 1999 @@ -41,52 +41,6 @@ #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) -static long long ufs_file_lseek(struct file *, long long, int); -static ssize_t ufs_file_write (struct file *, const char *, size_t, loff_t *); -static int ufs_release_file (struct inode *, struct file *); - -/* - * We have mostly NULL's here: the current defaults are ok for - * the ufs filesystem. - */ -static struct file_operations ufs_file_operations = { - ufs_file_lseek, /* lseek */ - generic_file_read, /* read */ - ufs_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - NULL, /* ioctl */ - generic_file_mmap, /* mmap */ - NULL, /* no special open is needed */ - NULL, /* flush */ - ufs_release_file, /* release */ - NULL, /* fsync */ - NULL, /* fasync */ - NULL, /* check_media_change */ - NULL /* revalidate */ -}; - -struct inode_operations ufs_file_inode_operations = { - &ufs_file_operations,/* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - generic_readpage, /* readpage */ - NULL, /* writepage */ - ufs_bmap, /* bmap */ - ufs_truncate, /* truncate */ - NULL, /* permission */ - NULL /* smap */ -}; - /* * Make sure the offset never goes beyond the 32-bit mark.. */ @@ -133,139 +87,49 @@ } } -static ssize_t ufs_file_write ( - struct file * filp, - const char * buf, - size_t count, - loff_t *ppos ) -{ - struct inode * inode = filp->f_dentry->d_inode; - __u32 pos; - long block; - int offset; - int written, c; - struct buffer_head * bh, *bufferlist[NBUF]; - struct super_block * sb; - int err; - int i,buffercount,write_error; - - /* POSIX: mtime/ctime may not change for 0 count */ - if (!count) - return 0; - write_error = buffercount = 0; - if (!inode) - return -EINVAL; - sb = inode->i_sb; - if (sb->s_flags & MS_RDONLY) - /* - * This fs has been automatically remounted ro because of errors - */ - return -ENOSPC; - - if (!S_ISREG(inode->i_mode)) { - ufs_warning (sb, "ufs_file_write", "mode = %07o", - inode->i_mode); - return -EINVAL; - } - remove_suid(inode); - - if (filp->f_flags & O_APPEND) - pos = inode->i_size; - else { - pos = *ppos; - if (pos != *ppos) - return -EINVAL; - } - - /* Check for overflow.. */ - if (pos > (__u32) (pos + count)) { - count = ~pos; /* == 0xFFFFFFFF - pos */ - if (!count) - return -EFBIG; - } - - /* - * If a file has been opened in synchronous mode, we have to ensure - * that meta-data will also be written synchronously. Thus, we - * set the i_osync field. This field is tested by the allocation - * routines. - */ - if (filp->f_flags & O_SYNC) - inode->u.ufs_i.i_osync++; - block = pos >> sb->s_blocksize_bits; - offset = pos & (sb->s_blocksize - 1); - c = sb->s_blocksize - offset; - written = 0; +static int ufs_writepage (struct file *file, struct page *page) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + unsigned long block; + int *p, nr[PAGE_SIZE/512]; + int i, err, created; + struct buffer_head *bh; + + i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; + block = page->offset >> inode->i_sb->s_blocksize_bits; + p = nr; + bh = page->buffers; do { - bh = ufs_getfrag (inode, block, 1, &err); - if (!bh) { - if (!written) - written = err; - break; - } - if (c > count) - c = count; - if (c != sb->s_blocksize && !buffer_uptodate(bh)) { - ll_rw_block (READ, 1, &bh); - wait_on_buffer (bh); - if (!buffer_uptodate(bh)) { - brelse (bh); - if (!written) - written = -EIO; - break; - } - } - c -= copy_from_user (bh->b_data + offset, buf, c); - if (!c) { - brelse(bh); - if (!written) - written = -EFAULT; - break; - } - update_vm_cache(inode, pos, bh->b_data + offset, c); - pos += c; - written += c; - buf += c; - count -= c; - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); - if (filp->f_flags & O_SYNC) - bufferlist[buffercount++] = bh; + if (bh && bh->b_blocknr) + *p = bh->b_blocknr; else - brelse(bh); - if (buffercount == NBUF){ - ll_rw_block(WRITE, buffercount, bufferlist); - for(i=0; is_blocksize; - } while (count); - if (buffercount){ - ll_rw_block(WRITE, buffercount, bufferlist); - for (i=0; i inode->i_size) - inode->i_size = pos; - if (filp->f_flags & O_SYNC) - inode->u.ufs_i.i_osync--; - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - *ppos = pos; - mark_inode_dirty(inode); - return written; + p++; + if (bh) + bh = bh->b_this_page; + } while (i > 0); + + brw_page(WRITE, page, inode->i_dev, nr, inode->i_sb->s_blocksize, 1); + return 0; +} + +static long ufs_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char *buf) +{ + return block_write_one_page(file, page, offset, bytes, buf, ufs_getfrag_block); +} + +/* + * Write to a file (through the page cache). + */ +static ssize_t +ufs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + return generic_file_write(file, buf, count, ppos, ufs_write_one_page); } /* @@ -277,3 +141,47 @@ { return 0; } + +/* + * We have mostly NULL's here: the current defaults are ok for + * the ufs filesystem. + */ +static struct file_operations ufs_file_operations = { + ufs_file_lseek, /* lseek */ + generic_file_read, /* read */ + ufs_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* poll - default */ + NULL, /* ioctl */ + generic_file_mmap, /* mmap */ + NULL, /* no special open is needed */ + NULL, /* flush */ + ufs_release_file, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ +}; + +struct inode_operations ufs_file_inode_operations = { + &ufs_file_operations,/* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + generic_readpage, /* readpage */ + ufs_writepage, /* writepage */ + ufs_bmap, /* bmap */ + ufs_truncate, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* revalidate */ + block_flushpage, /* flushpage */ +}; diff -u --recursive --new-file v2.3.6/linux/fs/ufs/inode.c linux/fs/ufs/inode.c --- v2.3.6/linux/fs/ufs/inode.c Tue May 11 23:01:41 1999 +++ linux/fs/ufs/inode.c Wed Jun 16 19:26:27 1999 @@ -175,7 +175,7 @@ static struct buffer_head * ufs_inode_getfrag (struct inode * inode, unsigned fragment, unsigned new_fragment, int create, - unsigned required, int * err ) + unsigned required, int *err, int metadata, int *phys_block, int *created) { struct super_block * sb; struct ufs_sb_private_info * uspi; @@ -201,13 +201,19 @@ tmp = SWAB32(*p); lastfrag = inode->u.ufs_i.i_lastfrag; if (tmp && fragment < lastfrag) { - result = getblk (sb->s_dev, uspi->s_sbbase + tmp + blockoff, sb->s_blocksize); - if (tmp == SWAB32(*p)) { - UFSD(("EXIT, result %u\n", tmp + blockoff)) - return result; + if (metadata) { + result = getblk (sb->s_dev, uspi->s_sbbase + tmp + blockoff, + sb->s_blocksize); + if (tmp == SWAB32(*p)) { + UFSD(("EXIT, result %u\n", tmp + blockoff)) + return result; + } + brelse (result); + goto repeat; + } else { + *phys_block = tmp; + return NULL; } - brelse (result); - goto repeat; } *err = -EFBIG; if (!create) @@ -269,7 +275,20 @@ else return NULL; } - result = getblk (inode->i_dev, tmp + blockoff, sb->s_blocksize); + + /* The nullification of framgents done in ufs/balloc.c is + * something I don't have the stomache to move into here right + * now. -DaveM + */ + if (metadata) { + result = getblk (inode->i_dev, tmp + blockoff, sb->s_blocksize); + } else { + *phys_block = tmp; + result = NULL; + *err = 0; + *created = 1; + } + inode->i_ctime = CURRENT_TIME; if (IS_SYNC(inode)) ufs_sync_inode (inode); @@ -280,7 +299,7 @@ static struct buffer_head * ufs_block_getfrag (struct inode * inode, struct buffer_head * bh, unsigned fragment, unsigned new_fragment, - int create, unsigned blocksize, int * err) + int create, unsigned blocksize, int * err, int metadata, int *phys_block, int *created) { struct super_block * sb; struct ufs_sb_private_info * uspi; @@ -312,19 +331,36 @@ repeat: tmp = SWAB32(*p); if (tmp) { - result = getblk (bh->b_dev, uspi->s_sbbase + tmp + blockoff, sb->s_blocksize); - if (tmp == SWAB32(*p)) { + if (metadata) { + result = getblk (bh->b_dev, uspi->s_sbbase + tmp + blockoff, + sb->s_blocksize); + if (tmp == SWAB32(*p)) { + brelse (bh); + UFSD(("EXIT, result %u\n", tmp + blockoff)) + return result; + } + brelse (result); + goto repeat; + } else { + *phys_block = tmp; brelse (bh); - UFSD(("EXIT, result %u\n", tmp + blockoff)) - return result; + return NULL; } - brelse (result); - goto repeat; } - if (!create || new_fragment >= (current->rlim[RLIMIT_FSIZE].rlim_cur >> sb->s_blocksize)) { + *err = -EFBIG; + if (!create) { brelse (bh); - *err = -EFBIG; return NULL; + } else { + unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur; + if (limit < RLIM_INFINITY) { + limit >>= sb->s_blocksize_bits; + if (new_fragment >= limit) { + brelse (bh); + send_sig(SIGXFSZ, current, 0); + return NULL; + } + } } if (block && (tmp = SWAB32(((u32*)bh->b_data)[block-1]) + uspi->s_fpb)) goal = tmp + uspi->s_fpb; @@ -334,12 +370,25 @@ if (!tmp) { if (SWAB32(*p)) { goto repeat; - } - else { + } else { + brelse (bh); return NULL; } } - result = getblk (bh->b_dev, tmp + blockoff, sb->s_blocksize); + + /* The nullification of framgents done in ufs/balloc.c is + * something I don't have the stomache to move into here right + * now. -DaveM + */ + if (metadata) { + result = getblk (bh->b_dev, tmp + blockoff, sb->s_blocksize); + } else { + *phys_block = tmp; + result = NULL; + *err = 0; + *created = 1; + } + mark_buffer_dirty(bh, 1); if (IS_SYNC(inode)) { ll_rw_block (WRITE, 1, &bh); @@ -352,14 +401,15 @@ return result; } -struct buffer_head * ufs_getfrag (struct inode * inode, unsigned fragment, - int create, int * err) +int ufs_getfrag_block (struct inode * inode, long fragment, + int create, int * err, int *created) { struct super_block * sb; struct ufs_sb_private_info * uspi; - struct buffer_head * bh; + struct buffer_head * bh, * tmp; unsigned f; unsigned swab; + int phys_block; sb = inode->i_sb; uspi = sb->u.ufs_sb.s_uspi; @@ -367,19 +417,27 @@ *err = -EIO; UFSD(("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment)) + if (fragment < 0) { + ufs_warning (sb, "ufs_getblk", "block < 0"); + return 0; + } if (fragment > ((UFS_NDADDR + uspi->s_apb + uspi->s_2apb + uspi->s_3apb) << uspi->s_fpbshift)) { ufs_warning (sb, "ufs_getblk", "block > big"); - return NULL; + return 0; } *err = -ENOSPC; f = fragment; + *created = 0; /* * Direct fragment */ - if (fragment < UFS_NDIR_FRAGMENT) - return ufs_inode_getfrag (inode, fragment, fragment, create, 1, err); + if (fragment < UFS_NDIR_FRAGMENT) { + tmp = ufs_inode_getfrag (inode, fragment, fragment, create, 1, + err, 0, &phys_block, created); + goto out; + } /* * Indirect fragment */ @@ -387,10 +445,12 @@ if (fragment < (1 << (uspi->s_apbshift + uspi->s_fpbshift))) { bh = ufs_inode_getfrag (inode, UFS_IND_FRAGMENT + (fragment >> uspi->s_apbshift), - f, create, uspi->s_fpb, err); - return ufs_block_getfrag (inode, bh, - fragment & uspi->s_apbmask, - f, create, sb->s_blocksize, err); + f, create, uspi->s_fpb, err, 1, NULL, NULL); + tmp = ufs_block_getfrag (inode, bh, + fragment & uspi->s_apbmask, + f, create, sb->s_blocksize, + err, 0, &phys_block, created); + goto out; } /* * Dindirect fragment @@ -398,14 +458,18 @@ fragment -= 1 << (uspi->s_apbshift + uspi->s_fpbshift); if ( fragment < (1 << (uspi->s_2apbshift + uspi->s_fpbshift))) { bh = ufs_inode_getfrag (inode, - UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift), - f, create, uspi->s_fpb, err); + UFS_DIND_FRAGMENT + (fragment >> uspi->s_2apbshift), + f, create, uspi->s_fpb, err, + 1, NULL, NULL); bh = ufs_block_getfrag (inode, bh, (fragment >> uspi->s_apbshift) & uspi->s_apbmask, - f, create, sb->s_blocksize, err); - return ufs_block_getfrag (inode, bh, + f, create, sb->s_blocksize, err, + 1, NULL, NULL); + tmp = ufs_block_getfrag (inode, bh, fragment & uspi->s_apbmask, - f, create, sb->s_blocksize, err); + f, create, sb->s_blocksize, err, + 0, &phys_block, created); + goto out; } /* * Tindirect fragment @@ -413,19 +477,42 @@ fragment -= 1 << (uspi->s_2apbshift + uspi->s_fpbshift); bh = ufs_inode_getfrag (inode, UFS_TIND_FRAGMENT + (fragment >> uspi->s_3apbshift), - f, create, uspi->s_fpb, err); + f, create, uspi->s_fpb, err, 1, NULL, NULL); bh = ufs_block_getfrag (inode, bh, (fragment >> uspi->s_2apbshift) & uspi->s_apbmask, - f, create, sb->s_blocksize, err); + f, create, sb->s_blocksize, err, 1, NULL, NULL); bh = ufs_block_getfrag (inode, bh, (fragment >> uspi->s_apbshift) & uspi->s_apbmask, - f, create, sb->s_blocksize, err); - return ufs_block_getfrag (inode, bh, + f, create, sb->s_blocksize, err, 1, NULL, NULL); + tmp = ufs_block_getfrag (inode, bh, fragment & uspi->s_apbmask, - f, create, sb->s_blocksize, err); + f, create, sb->s_blocksize, err, 0, &phys_block, created); + +out: + if (!phys_block) + return 0; + if (*err) + return 0; + return phys_block; } +struct buffer_head *ufs_getfrag(struct inode *inode, unsigned int fragment, + int create, int *err) +{ + struct buffer_head *tmp = NULL; + int phys_block, created; + phys_block = ufs_getfrag_block(inode, fragment, create, err, &created); + if (phys_block) { + tmp = getblk(inode->i_dev, phys_block, inode->i_sb->s_blocksize); + if (created) { + memset(tmp->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(tmp, 1); + mark_buffer_dirty(tmp, 1); + } + } + return tmp; +} struct buffer_head * ufs_bread (struct inode * inode, unsigned fragment, int create, int * err) diff -u --recursive --new-file v2.3.6/linux/fs/ufs/truncate.c linux/fs/ufs/truncate.c --- v2.3.6/linux/fs/ufs/truncate.c Tue Sep 1 10:50:11 1998 +++ linux/fs/ufs/truncate.c Wed Jun 16 19:26:27 1999 @@ -62,6 +62,9 @@ #define DIRECT_BLOCK howmany (inode->i_size, uspi->s_bsize) #define DIRECT_FRAGMENT howmany (inode->i_size, uspi->s_fsize) +#define DATA_BUFFER_USED(bh) \ + ((bh->b_count > 1) || buffer_locked(bh)) + static int ufs_trunc_direct (struct inode * inode) { struct super_block * sb; @@ -114,7 +117,7 @@ frag2 = ufs_fragnum (frag2); for (j = frag1; j < frag2; j++) { bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); - if ((bh && bh->b_count != 1) || tmp != SWAB32(*p)) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != SWAB32(*p)) { retry = 1; brelse (bh); goto next1; @@ -137,7 +140,7 @@ continue; for (j = 0; j < uspi->s_fpb; j++) { bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); - if ((bh && bh->b_count != 1) || tmp != SWAB32(*p)) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != SWAB32(*p)) { retry = 1; brelse (bh); goto next2; @@ -176,7 +179,7 @@ frag4 = ufs_fragnum (frag4); for (j = 0; j < frag4; j++) { bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); - if ((bh && bh->b_count != 1) || tmp != SWAB32(*p)) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != SWAB32(*p)) { retry = 1; brelse (bh); goto next1; @@ -237,7 +240,7 @@ continue; for (j = 0; j < uspi->s_fpb; j++) { bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize); - if ((bh && bh->b_count != 1) || tmp != SWAB32(*ind)) { + if ((bh && DATA_BUFFER_USED(bh)) || tmp != SWAB32(*ind)) { retry = 1; brelse (bh); goto next; diff -u --recursive --new-file v2.3.6/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c --- v2.3.6/linux/fs/umsdos/dir.c Thu May 13 23:18:21 1999 +++ linux/fs/umsdos/dir.c Sat Jun 19 11:45:28 1999 @@ -838,6 +838,5 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c --- v2.3.6/linux/fs/umsdos/rdir.c Fri Apr 23 21:20:38 1999 +++ linux/fs/umsdos/rdir.c Sat Jun 19 11:45:29 1999 @@ -253,6 +253,5 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL, /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/fs/umsdos/symlink.c linux/fs/umsdos/symlink.c --- v2.3.6/linux/fs/umsdos/symlink.c Sat Sep 19 13:46:28 1998 +++ linux/fs/umsdos/symlink.c Sat Jun 19 11:45:29 1999 @@ -141,7 +141,6 @@ NULL, /* truncate */ NULL, /* permission */ NULL, /* smap */ - NULL, /* updatepage */ NULL /* revalidate */ }; diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-arc/ide.h linux/include/asm-arm/arch-arc/ide.h --- v2.3.6/linux/include/asm-arm/arch-arc/ide.h Thu May 13 11:00:08 1999 +++ linux/include/asm-arm/arch-arc/ide.h Thu Jun 17 01:11:35 1999 @@ -19,7 +19,8 @@ * Set up a hw structure for a specified data port, control port and IRQ. * This should follow whatever the default interface uses. */ -static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq) +static __inline__ void +ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq) { ide_ioreg_t reg = (ide_ioreg_t) data_port; int i; diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-ebsa285/ide.h linux/include/asm-arm/arch-ebsa285/ide.h --- v2.3.6/linux/include/asm-arm/arch-ebsa285/ide.h Thu May 13 11:00:08 1999 +++ linux/include/asm-arm/arch-ebsa285/ide.h Thu Jun 17 01:11:35 1999 @@ -12,7 +12,8 @@ * Set up a hw structure for a specified data port, control port and IRQ. * This should follow whatever the default interface uses. */ -static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq) +static __inline__ void +ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) { ide_ioreg_t reg = (ide_ioreg_t) data_port; int i; @@ -22,7 +23,7 @@ reg += 1; } hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port; - hw->irq = *irq; + hw->irq = irq; } /* diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-ebsa285/irq.h linux/include/asm-arm/arch-ebsa285/irq.h --- v2.3.6/linux/include/asm-arm/arch-ebsa285/irq.h Wed May 12 13:16:27 1999 +++ linux/include/asm-arm/arch-ebsa285/irq.h Thu Jun 17 01:11:35 1999 @@ -10,7 +10,6 @@ * 26-Jan-1999 PJB Don't use IACK on CATS * 16-Mar-1999 RMK Added autodetect of ISA PICs */ -#include #include #include #include diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-ebsa285/memory.h linux/include/asm-arm/arch-ebsa285/memory.h --- v2.3.6/linux/include/asm-arm/arch-ebsa285/memory.h Wed May 12 13:16:27 1999 +++ linux/include/asm-arm/arch-ebsa285/memory.h Thu Jun 17 01:11:35 1999 @@ -15,8 +15,6 @@ #ifndef __ASM_ARCH_MMU_H #define __ASM_ARCH_MMU_H -#include - #if defined(CONFIG_HOST_FOOTBRIDGE) /* diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-ebsa285/system.h linux/include/asm-arm/arch-ebsa285/system.h --- v2.3.6/linux/include/asm-arm/arch-ebsa285/system.h Sat May 8 11:06:57 1999 +++ linux/include/asm-arm/arch-ebsa285/system.h Thu Jun 17 01:11:35 1999 @@ -20,16 +20,7 @@ mcr p15, 0, ip, c7, c7 @ flush caches mov pc, lr" : : : "cc"); } else { - if (machine_is_ebsa285() || machine_is_co285()) { - /* To reboot, we set up the 21285 watchdog and - * enable it. We then wait for it to timeout. - */ - *CSR_TIMER4_LOAD = 0x8000; - *CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE | - TIMER_CNTL_AUTORELOAD | - TIMER_CNTL_DIV16; - *CSR_SA110_CNTL |= 1 << 13; - } else if (machine_is_netwinder()) { + if (machine_is_netwinder()) { /* open up the SuperIO chip */ outb(0x87, 0x370); @@ -48,6 +39,15 @@ /* set a RED LED and toggle WD_TIMER for rebooting */ outb(0xc4, 0x338); + } else { + /* To reboot, we set up the 21285 watchdog and + * enable it. We then wait for it to timeout. + */ + *CSR_TIMER4_LOAD = 0x8000; + *CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE | + TIMER_CNTL_AUTORELOAD | + TIMER_CNTL_DIV16; + *CSR_SA110_CNTL |= 1 << 13; } } } diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-ebsa285/time.h linux/include/asm-arm/arch-ebsa285/time.h --- v2.3.6/linux/include/asm-arm/arch-ebsa285/time.h Sat May 8 11:06:57 1999 +++ linux/include/asm-arm/arch-ebsa285/time.h Thu Jun 17 01:11:35 1999 @@ -333,7 +333,7 @@ set_rtc_mmss = set_dummy_time; } - if (machine_is_ebsa285()) { + if (machine_is_ebsa285() || machine_is_co285()) { gettimeoffset = timer1_gettimeoffset; *CSR_TIMER1_CLR = 0; diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/arch-rpc/ide.h linux/include/asm-arm/arch-rpc/ide.h --- v2.3.6/linux/include/asm-arm/arch-rpc/ide.h Thu May 13 11:00:08 1999 +++ linux/include/asm-arm/arch-rpc/ide.h Thu Jun 17 01:11:35 1999 @@ -12,30 +12,31 @@ * Set up a hw structure for a specified data port, control port and IRQ. * This should follow whatever the default interface uses. */ -static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int *irq) +static __inline__ void +ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq) { ide_ioreg_t reg = (ide_ioreg_t) data_port; int i; + memset(hw, 0, sizeof(*hw)); + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { hw->io_ports[i] = reg; reg += 1; } hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port; - hw->irq = *irq; + hw->irq = irq; } /* * This registers the standard ports for this architecture with the IDE * driver. */ -static __inline__ void ide_init_default_hwifs(void) +static __inline__ void +ide_init_default_hwifs(void) { hw_regs_t hw; - memset(hw, 0, sizeof(*hw)); - - ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, NULL); - hw.irq = IRQ_HARDDISK; + ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, IRQ_HARDDISK); ide_register_hw(&hw, NULL); } diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/current.h linux/include/asm-arm/current.h --- v2.3.6/linux/include/asm-arm/current.h Tue Jan 20 16:39:42 1998 +++ linux/include/asm-arm/current.h Thu Jun 17 01:11:35 1999 @@ -4,17 +4,19 @@ static inline unsigned long get_sp(void) { unsigned long sp; - __asm__ ("mov %0,sp" : "=r" (sp)); + __asm__ ("mov %0,sp" : "=r" (sp)); return sp; } +//static inline struct task_struct *get_current(void) __attribute__ (( __const__ )); + static inline struct task_struct *get_current(void) { struct task_struct *ts; - __asm__ __volatile__(" - bic %0, sp, #0x1f00 - bic %0, %0, #0x00ff - " : "=r" (ts)); + __asm__ __volatile__ ( + "bic %0, sp, #0x1f00 @ get_current + bic %0, %0, #0x00ff" + : "=r" (ts)); return ts; } diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/dma.h linux/include/asm-arm/dma.h --- v2.3.6/linux/include/asm-arm/dma.h Sat May 8 11:07:16 1999 +++ linux/include/asm-arm/dma.h Thu Jun 17 01:11:35 1999 @@ -119,6 +119,10 @@ */ extern void set_dma_mode(dmach_t channel, dmamode_t mode); +/* Set the transfer speed for this channel + */ +extern void set_dma_speed(dmach_t channel, int cycle_ns); + /* Get DMA residue count. After a DMA transfer, this * should return zero. Reading this while a DMA transfer is * still in progress will return unpredictable results. diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/ide.h linux/include/asm-arm/ide.h --- v2.3.6/linux/include/asm-arm/ide.h Thu May 13 11:00:08 1999 +++ linux/include/asm-arm/ide.h Thu Jun 17 01:11:35 1999 @@ -46,6 +46,13 @@ #define ide_release_lock(lock) do {} while (0) #define ide_get_lock(lock, hdlr, data) do {} while (0) +/* + * We always use the new IDE port registering, + * so these are fixed here. + */ +#define ide_default_io_base(i) ((ide_ioreg_t)0) +#define ide_default_irq(b) (0) + #endif /* __KERNEL__ */ #endif /* __ASMARM_IDE_H */ diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/io.h linux/include/asm-arm/io.h --- v2.3.6/linux/include/asm-arm/io.h Thu May 13 11:00:08 1999 +++ linux/include/asm-arm/io.h Thu Jun 17 01:11:35 1999 @@ -189,21 +189,10 @@ #define inl_p(port) __inl_p((port)) #endif -/* Nothing to do */ - -#ifndef dma_cache_inv -#define dma_cache_inv(_start,_size) do { } while (0) -#endif -#ifndef dma_cache_wback -#define dma_cache_wback(_start,_size) do { } while (0) -#ifndef ARCH_READWRITE -#ifndef dma_cache_wback_inv -#define dma_cache_wback_inv(_start,_size) do { } while (0) #endif -#endif /* __KERNEL__ */ +#ifndef ARCH_READWRITE -#endif /* __ASM_ARM_IO_H */ /* for panic */ #include diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/irq.h linux/include/asm-arm/irq.h --- v2.3.6/linux/include/asm-arm/irq.h Sat May 8 11:07:16 1999 +++ linux/include/asm-arm/irq.h Thu Jun 17 01:11:35 1999 @@ -24,8 +24,5 @@ extern void disable_irq(unsigned int); extern void enable_irq(unsigned int); -#define __STR(x) #x -#define STR(x) __STR(x) - #endif diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/proc-armo/ptrace.h linux/include/asm-arm/proc-armo/ptrace.h --- v2.3.6/linux/include/asm-arm/proc-armo/ptrace.h Sat May 8 11:06:57 1999 +++ linux/include/asm-arm/proc-armo/ptrace.h Thu Jun 17 01:11:35 1999 @@ -44,6 +44,8 @@ #define CC_Z_BIT (1 << 30) #define CC_N_BIT (1 << 31) +#ifdef __KERNEL__ + #define processor_mode(regs) \ ((regs)->ARM_pc & MODE_MASK) @@ -70,11 +72,19 @@ */ static inline int valid_user_regs(struct pt_regs *regs) { - if (!user_mode(regs) || regs->ARM_pc & (F_BIT | I_BIT)) + if (user_mode(regs) && + (regs->ARM_pc & (F_BIT | I_BIT)) == 0) return 1; + /* + * force it to be something sensible + */ + regs->ARM_pc &= ~(MODE_MASK | F_BIT | I_BIT); + return 0; } + +#endif /* __KERNEL__ */ #endif diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/proc-armo/semaphore.h linux/include/asm-arm/proc-armo/semaphore.h --- v2.3.6/linux/include/asm-arm/proc-armo/semaphore.h Sat May 8 11:06:57 1999 +++ linux/include/asm-arm/proc-armo/semaphore.h Thu Jun 17 01:11:35 1999 @@ -14,13 +14,13 @@ @ atomic down operation mov r0, pc orr lr, r0, #0x08000000 - and r0, r0, #0x0c000003 teqp lr, #0 ldr lr, [%0] + and r0, r0, #0x0c000003 subs lr, lr, #1 str lr, [%0] - mov lr, pc, lsr #28 - teqp r0, lr, lsl #28 + orrmi r0, r0, #0x80000000 @ set N + teqp r0, #0 movmi r0, %0 blmi " SYMBOL_NAME_STR(__down_failed) : @@ -39,14 +39,13 @@ @ atomic down operation mov r0, pc orr lr, r0, #0x08000000 - and r0, r0, #0x0c000003 teqp lr, #0 ldr lr, [%1] + and r0, r0, #0x0c000003 subs lr, lr, #1 str lr, [%1] - mov lr, pc, lsr #28 orrmi r0, r0, #0x80000000 @ set N - teqp r0, lr, lsl #28 + teqp r0, #0 movmi r0, %1 movpl r0, #0 blmi " SYMBOL_NAME_STR(__down_interruptible_failed) " @@ -64,14 +63,13 @@ @ atomic down operation mov r0, pc orr lr, r0, #0x08000000 - and r0, r0, #0x0c000003 teqp lr, #0 ldr lr, [%1] + and r0, r0, #0x0c000003 subs lr, lr, #1 str lr, [%1] - mov lr, pc, lsr #28 orrmi r0, r0, #0x80000000 @ set N - teqp r0, lr, lsl #28 + teqp r0, #0 movmi r0, %1 movpl r0, #0 blmi " SYMBOL_NAME_STR(__down_trylock_failed) " @@ -94,14 +92,13 @@ @ atomic up operation mov r0, pc orr lr, r0, #0x08000000 - and r0, r0, #0x0c000003 teqp lr, #0 ldr lr, [%0] + and r0, r0, #0x0c000003 adds lr, lr, #1 str lr, [%0] - mov lr, pc, lsr #28 - orrls r0, r0, #0x80000000 @ set N - teqp r0, lr, lsl #28 + orrle r0, r0, #0x80000000 @ set N + teqp r0, #0 movmi r0, %0 blmi " SYMBOL_NAME_STR(__up_wakeup) : diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/proc-armo/system.h linux/include/asm-arm/proc-armo/system.h --- v2.3.6/linux/include/asm-arm/proc-armo/system.h Thu Jan 7 15:51:33 1999 +++ linux/include/asm-arm/proc-armo/system.h Thu Jun 17 01:11:35 1999 @@ -110,6 +110,12 @@ : "memory"); \ } while (0) +/* For spinlocks etc */ +#define local_irq_save(x) __save_flags_cli(x) +#define local_irq_restore(x) __restore_flags(x) +#define local_irq_disable() __cli() +#define local_irq_enable() __sti() + #ifdef __SMP__ #error SMP not supported #else diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/proc-armv/ptrace.h linux/include/asm-arm/proc-armv/ptrace.h --- v2.3.6/linux/include/asm-arm/proc-armv/ptrace.h Sat May 8 11:06:58 1999 +++ linux/include/asm-arm/proc-armv/ptrace.h Thu Jun 17 01:11:35 1999 @@ -52,6 +52,8 @@ #define CC_Z_BIT (1 << 30) #define CC_N_BIT (1 << 31) +#ifdef __KERNEL__ + #if 0 /* GCC/egcs should be able to optimise this, IMHO */ #define user_mode(regs) \ ((((regs)->ARM_cpsr & MODE_MASK) == USR_MODE) || \ @@ -81,8 +83,8 @@ */ static inline int valid_user_regs(struct pt_regs *regs) { - if ((regs->ARM_cpsr & 0xf) == 0 || - (regs->ARM_cpsr & (F_BIT|I_BIT))) + if ((regs->ARM_cpsr & 0xf) == 0 && + (regs->ARM_cpsr & (F_BIT|I_BIT)) == 0) return 1; /* @@ -92,6 +94,8 @@ return 0; } + +#endif /* __KERNEL__ */ #endif diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/proc-armv/semaphore.h linux/include/asm-arm/proc-armv/semaphore.h --- v2.3.6/linux/include/asm-arm/proc-armv/semaphore.h Sat May 8 11:06:58 1999 +++ linux/include/asm-arm/proc-armv/semaphore.h Thu Jun 17 01:11:35 1999 @@ -16,12 +16,12 @@ @ atomic down operation mrs %0, cpsr orr %1, %0, #128 @ disable IRQs - bic %0, %0, #0x80000000 @ clear N msr cpsr, %1 ldr %1, [%2] + bic %0, %0, #0x80000000 @ clear N subs %1, %1, #1 - orrmi %0, %0, #0x80000000 @ set N str %1, [%2] + orrmi %0, %0, #0x80000000 @ set N msr cpsr, %0 movmi r0, %2 blmi " SYMBOL_NAME_STR(__down_failed) @@ -42,12 +42,12 @@ @ atomic down interruptible operation mrs %0, cpsr orr %1, %0, #128 @ disable IRQs - bic %0, %0, #0x80000000 @ clear N msr cpsr, %1 ldr %1, [%2] + bic %0, %0, #0x80000000 @ clear N subs %1, %1, #1 - orrmi %0, %0, #0x80000000 @ set N str %1, [%2] + orrmi %0, %0, #0x80000000 @ set N msr cpsr, %0 movmi r0, %2 movpl r0, #0 @@ -68,12 +68,12 @@ @ atomic down try lock operation mrs %0, cpsr orr %1, %0, #128 @ disable IRQs - bic %0, %0, #0x80000000 @ clear N msr cpsr, %1 ldr %1, [%2] + bic %0, %0, #0x80000000 @ clear N subs %1, %1, #1 - orrmi %0, %0, #0x80000000 @ set N str %1, [%2] + orrmi %0, %0, #0x80000000 @ set N msr cpsr, %0 movmi r0, %2 movpl r0, #0 @@ -100,12 +100,12 @@ @ atomic up operation mrs %0, cpsr orr %1, %0, #128 @ disable IRQs - bic %0, %0, #0x80000000 @ clear N msr cpsr, %1 ldr %1, [%2] + bic %0, %0, #0x80000000 @ clear N adds %1, %1, #1 - orrls %0, %0, #0x80000000 @ set N str %1, [%2] + orrle %0, %0, #0x80000000 @ set N msr cpsr, %0 movmi r0, %2 blmi " SYMBOL_NAME_STR(__up_wakeup) diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/proc-armv/system.h linux/include/asm-arm/proc-armv/system.h --- v2.3.6/linux/include/asm-arm/proc-armv/system.h Thu Jan 7 15:51:33 1999 +++ linux/include/asm-arm/proc-armv/system.h Thu Jun 17 01:11:35 1999 @@ -121,6 +121,12 @@ : "memory"); \ } while (0) +/* For spinlocks etc */ +#define local_irq_save(x) __save_flags_cli(x) +#define local_irq_restore(x) __restore_flags(x) +#define local_irq_disable() __cli() +#define local_irq_enable() __sti() + #ifdef __SMP__ #error SMP not supported #else diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/processor.h linux/include/asm-arm/processor.h --- v2.3.6/linux/include/asm-arm/processor.h Tue May 25 14:55:05 1999 +++ linux/include/asm-arm/processor.h Thu Jun 17 01:11:35 1999 @@ -36,6 +36,7 @@ #define NR_DEBUGS 5 +#include #include #include @@ -86,6 +87,7 @@ } /* Forward declaration, a strange C thing */ +struct task_struct; struct mm_struct; /* Free all resources held by a thread. */ diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/semaphore.h linux/include/asm-arm/semaphore.h --- v2.3.6/linux/include/asm-arm/semaphore.h Sat May 15 15:05:37 1999 +++ linux/include/asm-arm/semaphore.h Thu Jun 17 01:11:35 1999 @@ -6,6 +6,7 @@ #include #include +#include struct semaphore { atomic_t count; @@ -13,8 +14,35 @@ wait_queue_head_t wait; }; -#define MUTEX ((struct semaphore) { ATOMIC_INIT(1), 0, NULL }) -#define MUTEX_LOCKED ((struct semaphore) { ATOMIC_INIT(0), 0, NULL }) +#define __SEMAPHORE_INIT(name,count) \ + { ATOMIC_INIT(count), 0, \ + __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) } + +#define __MUTEX_INITIALIZER(name) \ + __SEMAPHORE_INIT(name,1) + +#define __DECLARE_SEMAPHORE_GENERIC(name,count) \ + struct semaphore name = __SEMAPHORE_INIT(name,count) + +#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1) +#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0) + +#define sema_init(sem, val) \ +do { \ + atomic_set(&((sem)->count), (val)); \ + (sem)->waking = 0; \ + init_waitqueue_head(&(sem)->wait); \ +} while (0) + +static inline void init_MUTEX(struct semaphore *sem) +{ + sema_init(sem, 1); +} + +static inline void init_MUTEX_LOCKED(struct semaphore *sem) +{ + sema_init(sem, 0); +} asmlinkage void __down_failed (void /* special register calling convention */); asmlinkage int __down_interruptible_failed (void /* special register calling convention */); @@ -26,7 +54,7 @@ extern int __down_trylock(struct semaphore * sem); extern void __up(struct semaphore * sem); -#define sema_init(sem, val) atomic_set(&((sem)->count), (val)) +extern spinlock_t semaphore_wake_lock; #include diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/softirq.h linux/include/asm-arm/softirq.h --- v2.3.6/linux/include/asm-arm/softirq.h Thu Jan 7 15:51:33 1999 +++ linux/include/asm-arm/softirq.h Thu Jun 17 01:11:35 1999 @@ -5,10 +5,18 @@ #include extern unsigned int local_bh_count[NR_CPUS]; -#define in_bh() (local_bh_count[smp_processor_id()] != 0) + +#define cpu_bh_disable(cpu) do { local_bh_count[(cpu)]++; barrier(); } while (0) +#define cpu_bh_enable(cpu) do { barrier(); local_bh_count[(cpu)]--; } while (0) + +#define cpu_bh_trylock(cpu) (local_bh_count[(cpu)] ? 0 : (local_bh_count[(cpu)] = 1)) +#define cpu_bh_endlock(cpu) (local_bh_count[(cpu)] = 0) + +#define local_bh_disable() cpu_bh_disable(smp_processor_id()) +#define local_bh_enable() cpu_bh_enable(smp_processor_id()) #define get_active_bhs() (bh_mask & bh_active) -#define clear_active_bhs(x) atomic_clear_mask((int)(x),&bh_active) +#define clear_active_bhs(x) atomic_clear_mask((x),&bh_active) extern inline void init_bh(int nr, void (*routine)(void)) { @@ -19,8 +27,9 @@ extern inline void remove_bh(int nr) { - bh_base[nr] = NULL; bh_mask &= ~(1 << nr); + mb(); + bh_base[nr] = NULL; } extern inline void mark_bh(int nr) @@ -34,20 +43,20 @@ extern inline void start_bh_atomic(void) { - local_bh_count[smp_processor_id()]++; + local_bh_disable(); barrier(); } extern inline void end_bh_atomic(void) { barrier(); - local_bh_count[smp_processor_id()]--; + local_bh_enable(); } /* These are for the irq's testing the lock */ -#define softirq_trylock(cpu) (in_bh() ? 0 : (local_bh_count[smp_processor_id()]=1)) -#define softirq_endlock(cpu) (local_bh_count[smp_processor_id()] = 0) -#define synchronize_bh() do { } while (0) +#define softirq_trylock(cpu) (cpu_bh_trylock(cpu)) +#define softirq_endlock(cpu) (cpu_bh_endlock(cpu)) +#define synchronize_bh() barrier() #endif /* SMP */ diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/spinlock.h linux/include/asm-arm/spinlock.h --- v2.3.6/linux/include/asm-arm/spinlock.h Thu Jan 7 15:51:33 1999 +++ linux/include/asm-arm/spinlock.h Thu Jun 17 01:11:35 1999 @@ -1,39 +1,96 @@ #ifndef __ASM_SPINLOCK_H #define __ASM_SPINLOCK_H -#ifndef __SMP__ - /* * To be safe, we assume the only compiler that can cope with * empty initialisers is EGCS. */ #if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 90)) -#define EMPTY_INIT_OK +#define EMPTY_STRUCT struct { } +#define EMPTY_STRUCT_INIT(t) (t) { } +#else +#define EMPTY_STRUCT unsigned char +#define EMPTY_STRUCT_INIT(t) (t) 0 #endif /* + * These are the generic versions of the spinlocks + * and read-write locks.. We should actually do a + * with all of this. Oh, well. + */ +#define spin_lock_irqsave(lock, flags) do { local_irq_save(flags); spin_lock(lock); } while (0) +#define spin_lock_irq(lock) do { local_irq_disable(); spin_lock(lock); } while (0) +#define spin_lock_bh(lock) do { local_bh_disable(); spin_lock(lock); } while (0) + +#define read_lock_irqsave(lock, flags) do { local_irq_save(flags); read_lock(lock); } while (0) +#define read_lock_irq(lock) do { local_irq_disable(); read_lock(lock); } while (0) +#define read_lock_bh(lock) do { local_bh_disable(); read_lock(lock); } while (0) + +#define write_lock_irqsave(lock, flags) do { local_irq_save(flags); write_lock(lock); } while (0) +#define write_lock_irq(lock) do { local_irq_disable(); write_lock(lock); } while (0) +#define write_lock_bh(lock) do { local_bh_disable(); write_lock(lock); } while (0) + +#define spin_unlock_irqrestore(lock, flags) do { spin_unlock(lock); local_irq_restore(flags); } while (0) +#define spin_unlock_irq(lock) do { spin_unlock(lock); local_irq_enable(); } while (0) +#define spin_unlock_bh(lock) do { spin_unlock(lock); local_bh_enable(); } while (0) + +#define read_unlock_irqrestore(lock, flags) do { read_unlock(lock); local_irq_restore(flags); } while (0) +#define read_unlock_irq(lock) do { read_unlock(lock); local_irq_enable(); } while (0) +#define read_unlock_bh(lock) do { read_unlock(lock); local_bh_enable(); } while (0) + +#define write_unlock_irqrestore(lock, flags) do { write_unlock(lock); local_irq_restore(flags); } while (0) +#define write_unlock_irq(lock) do { write_unlock(lock); local_irq_enable(); } while (0) +#define write_unlock_bh(lock) do { write_unlock(lock); local_bh_enable(); } while (0) + +#ifndef __SMP__ + +#define DEBUG_SPINLOCKS 0 /* 0 == no debugging, 1 == maintain lock state, 2 == full debugging */ + +#if (DEBUG_SPINLOCKS < 1) +/* * Your basic spinlocks, allowing only a single CPU anywhere */ -#ifdef EMPTY_INIT_OK - typedef struct { } spinlock_t; -# define SPIN_LOCK_UNLOCKED (spinlock_t) { } -#else - typedef unsigned char spinlock_t; -# define SPIN_LOCK_UNLOCKED 0 -#endif +typedef EMPTY_STRUCT spinlock_t; +#define SPIN_LOCK_UNLOCKED EMPTY_STRUCT_INIT(spinlock_t) #define spin_lock_init(lock) do { } while(0) #define spin_lock(lock) do { } while(0) -#define spin_trylock(lock) do { } while(0) +#define spin_trylock(lock) (1) #define spin_unlock_wait(lock) do { } while(0) #define spin_unlock(lock) do { } while(0) -#define spin_lock_irq(lock) cli() -#define spin_unlock_irq(lock) sti() -#define spin_lock_irqsave(lock, flags) \ - do { __save_flags_cli(flags); } while (0) -#define spin_unlock_irqrestore(lock, flags) \ - restore_flags(flags) +#elif (DEBUG_SPINLOCKS < 2) + +typedef struct { + volatile unsigned int lock; +} spinlock_t; +#define SPIN_LOCK_UNLOCKED (pinlock_t) { 0 } + +#define spin_lock_init(x) do { (x)->lock = 0; } while (0) +#define spin_lock(x) do { (x)->lock = 1; } while (0) +#define spin_trylock(lock) (!test_and_set_bit(0,(lock))) +#define spin_unlock_wait(x) do { } while (0) +#define spin_unlock(x) do { (x)->lock = 0; } while (0) + +#else /* (DEBUG_SPINLOCKS >= 2) */ + +typedef struct { + volatule unsigned int lock; + volatile unsigned int babble; + const char *module; +} spinlock_t; +#define SPIN_LOCK_UNLOCKED (spinlock_t) { 0, 25, __BASE_FILE__ } + +#include + +#define spin_lock_init(x) do { (x)->lock = 0; } while (0) +#define spin_trylock(lock) (!test_and_set_bit(0,(lock))) + +#define spin_lock(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_lock(%s:%p) already locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 1; restore_flags(__spinflags);} while (0) +#define spin_unlock_wait(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock_wait(%s:%p) deadlock\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} restore_flags(__spinflags);} while (0) +#define spin_unlock(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; restore_flags(__spinflags);} while (0) + +#endif /* * Read-write spinlocks, allowing multiple readers @@ -45,31 +102,13 @@ * irq-safe write-lock, but readers can get non-irqsafe * read-locks. */ -#ifdef EMPTY_INIT_OK - typedef struct { } rwlock_t; -# define RW_LOCK_UNLOCKED (rwlock_t) { } -#else - typedef unsigned char rwlock_t; -# define RW_LOCK_UNLOCKED 0 -#endif +typedef EMPTY_STRUCT rwlock_t; +#define RW_LOCK_UNLOCKED EMPTY_STRUCT_INIT(rwlock_t) #define read_lock(lock) do { } while(0) #define read_unlock(lock) do { } while(0) #define write_lock(lock) do { } while(0) #define write_unlock(lock) do { } while(0) -#define read_lock_irq(lock) cli() -#define read_unlock_irq(lock) sti() -#define write_lock_irq(lock) cli() -#define write_unlock_irq(lock) sti() - -#define read_lock_irqsave(lock, flags) \ - do { __save_flags_cli(flags); } while (0) -#define read_unlock_irqrestore(lock, flags) \ - restore_flags(flags) -#define write_lock_irqsave(lock, flags) \ - do { __save_flags_cli(flags); } while (0) -#define write_unlock_irqrestore(lock, flags) \ - restore_flags(flags) #else #error ARM architecture does not support spin locks diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/system.h linux/include/asm-arm/system.h --- v2.3.6/linux/include/asm-arm/system.h Sat May 8 11:07:16 1999 +++ linux/include/asm-arm/system.h Thu Jun 17 01:11:35 1999 @@ -35,7 +35,7 @@ /* * Sort out a definition for machine_arch_type - * The rules basically are: + * The rules are: * 1. If one architecture is selected, then all machine_is_xxx() * are constant. * 2. If two or more architectures are selected, then the selected @@ -118,28 +118,16 @@ #define machine_arch_type __machine_arch_type #endif -/* - * task_struct isn't always declared - forward-declare it here. - */ -struct task_struct; - #include -extern void arm_malalignedptr(const char *, void *, volatile void *); -extern void arm_invalidptr(const char *, int); - #define xchg(ptr,x) \ ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) #define tas(ptr) (xchg((ptr),1)) -/* - * switch_to(prev, next) should switch from task `prev' to `next' - * `prev' will never be the same as `next'. - * - * `next' and `prev' should be struct task_struct, but it isn't always defined - */ -#define switch_to(prev,next,last) do { last = processor._switch_to(prev,next); } while (0) +extern void arm_malalignedptr(const char *, void *, volatile void *); +extern void arm_invalidptr(const char *, int); +extern asmlinkage void __backtrace(void); /* * Include processor dependent parts @@ -152,7 +140,16 @@ #define wmb() mb() #define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t"); -extern asmlinkage void __backtrace(void); +/* + * switch_to(prev, next) should switch from task `prev' to `next' + * `prev' will never be the same as `next'. + * The `mb' is to tell GCC not to cache `current' across this call. + */ +#define switch_to(prev,next,last) \ + do { \ + last = processor._switch_to(prev,next); \ + mb(); \ + } while (0) #endif diff -u --recursive --new-file v2.3.6/linux/include/asm-arm/unistd.h linux/include/asm-arm/unistd.h --- v2.3.6/linux/include/asm-arm/unistd.h Sat May 8 11:06:58 1999 +++ linux/include/asm-arm/unistd.h Thu Jun 17 01:11:35 1999 @@ -59,7 +59,7 @@ #define __NR_geteuid (__NR_SYSCALL_BASE+ 49) #define __NR_getegid (__NR_SYSCALL_BASE+ 50) #define __NR_acct (__NR_SYSCALL_BASE+ 51) -#define __NR_phys (__NR_SYSCALL_BASE+ 52) +#define __NR_umount2 (__NR_SYSCALL_BASE+ 52) #define __NR_lock (__NR_SYSCALL_BASE+ 53) #define __NR_ioctl (__NR_SYSCALL_BASE+ 54) #define __NR_fcntl (__NR_SYSCALL_BASE+ 55) diff -u --recursive --new-file v2.3.6/linux/include/asm-i386/page.h linux/include/asm-i386/page.h --- v2.3.6/linux/include/asm-i386/page.h Tue Jun 8 23:03:39 1999 +++ linux/include/asm-i386/page.h Sun Jun 20 17:46:13 1999 @@ -84,6 +84,19 @@ #define __PAGE_OFFSET (PAGE_OFFSET_RAW) +#ifndef __ASSEMBLY__ + +#define BUG() do { \ + printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ + __asm__ __volatile__(".byte 0x0f,0x0b"); \ +} while (0) + +#define PAGE_BUG(page) do { \ + BUG(); \ +} while (0) + +#endif /* __ASSEMBLY__ */ + #define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET) #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) diff -u --recursive --new-file v2.3.6/linux/include/asm-i386/smplock.h linux/include/asm-i386/smplock.h --- v2.3.6/linux/include/asm-i386/smplock.h Tue Jun 8 23:03:41 1999 +++ linux/include/asm-i386/smplock.h Sun Jun 20 17:46:15 1999 @@ -49,6 +49,8 @@ extern __inline__ void unlock_kernel(void) { + if (current->lock_depth < 0) + BUG(); __asm__ __volatile__( "decl %1\n\t" "jns 9f\n\t" diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc/namei.h linux/include/asm-sparc/namei.h --- v2.3.6/linux/include/asm-sparc/namei.h Wed Jun 9 16:24:15 1999 +++ linux/include/asm-sparc/namei.h Thu Jun 17 01:08:50 1999 @@ -1,4 +1,4 @@ -/* $Id: namei.h,v 1.13 1999/04/06 06:54:36 jj Exp $ +/* $Id: namei.h,v 1.14 1999/06/10 05:23:12 davem Exp $ * linux/include/asm-sparc/namei.h * * Routines to handle famous /usr/gnemul/s*. diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc/page.h linux/include/asm-sparc/page.h --- v2.3.6/linux/include/asm-sparc/page.h Wed Mar 10 16:53:37 1999 +++ linux/include/asm-sparc/page.h Thu Jun 17 01:08:50 1999 @@ -28,6 +28,10 @@ #ifndef __ASSEMBLY__ +#define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0=0; } while (0) +#define PAGE_BUG(page) do { \ + BUG(); } while (0) + #define clear_page(page) memset((void *)(page), 0, PAGE_SIZE) #define copy_page(to,from) memcpy((void *)(to), (void *)(from), PAGE_SIZE) diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc/spinlock.h linux/include/asm-sparc/spinlock.h --- v2.3.6/linux/include/asm-sparc/spinlock.h Thu May 27 09:55:22 1999 +++ linux/include/asm-sparc/spinlock.h Thu Jun 17 01:08:50 1999 @@ -17,7 +17,7 @@ #define spin_lock_init(lock) do { } while(0) #define spin_lock(lock) do { } while(0) -#define spin_trylock(lock) do { } while(0) +#define spin_trylock(lock) (1) #define spin_unlock_wait(lock) do { } while(0) #define spin_unlock(lock) do { } while(0) #define spin_lock_irq(lock) cli() diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc64/elf.h linux/include/asm-sparc64/elf.h --- v2.3.6/linux/include/asm-sparc64/elf.h Sun Oct 4 10:22:44 1998 +++ linux/include/asm-sparc64/elf.h Thu Jun 17 01:08:50 1999 @@ -1,4 +1,4 @@ -/* $Id: elf.h,v 1.18 1998/09/09 05:36:08 davem Exp $ */ +/* $Id: elf.h,v 1.19 1999/06/11 13:26:04 jj Exp $ */ #ifndef __ASM_SPARC64_ELF_H #define __ASM_SPARC64_ELF_H @@ -7,7 +7,9 @@ */ #include +#ifdef __KERNEL__ #include +#endif /* * These are used to set parameters in the core dumps. diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc64/namei.h linux/include/asm-sparc64/namei.h --- v2.3.6/linux/include/asm-sparc64/namei.h Wed Jun 9 16:24:15 1999 +++ linux/include/asm-sparc64/namei.h Thu Jun 17 01:08:50 1999 @@ -1,4 +1,4 @@ -/* $Id: namei.h,v 1.14 1999/04/06 06:54:39 jj Exp $ +/* $Id: namei.h,v 1.15 1999/06/10 05:23:17 davem Exp $ * linux/include/asm-sparc64/namei.h * * Routines to handle famous /usr/gnemul/s*. diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc64/page.h linux/include/asm-sparc64/page.h --- v2.3.6/linux/include/asm-sparc64/page.h Tue Oct 27 09:52:21 1998 +++ linux/include/asm-sparc64/page.h Thu Jun 17 01:08:50 1999 @@ -18,6 +18,10 @@ #ifndef __ASSEMBLY__ +#define BUG() do { printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); *(int *)0=0; } while (0) +#define PAGE_BUG(page) do { \ + BUG(); } while (0) + extern void clear_page(unsigned long page); extern void copy_page(unsigned long to, unsigned long from); diff -u --recursive --new-file v2.3.6/linux/include/asm-sparc64/spinlock.h linux/include/asm-sparc64/spinlock.h --- v2.3.6/linux/include/asm-sparc64/spinlock.h Tue May 25 13:06:34 1999 +++ linux/include/asm-sparc64/spinlock.h Thu Jun 17 01:08:50 1999 @@ -15,7 +15,7 @@ #define spin_lock_init(lock) do { } while(0) #define spin_lock(lock) do { } while(0) -#define spin_trylock(lock) do { } while(0) +#define spin_trylock(lock) (1) #define spin_unlock_wait(lock) do { } while(0) #define spin_unlock(lock) do { } while(0) #define spin_lock_irq(lock) cli() diff -u --recursive --new-file v2.3.6/linux/include/linux/blk.h linux/include/linux/blk.h --- v2.3.6/linux/include/linux/blk.h Tue Jun 8 23:04:28 1999 +++ linux/include/linux/blk.h Sun Jun 20 17:47:10 1999 @@ -342,6 +342,15 @@ #define DEVICE_ON(device) #define DEVICE_OFF(device) +#elif (MAJOR_NR == MFM_ACORN_MAJOR) + +#define DEVICE_NAME "mfm disk" +#define DEVICE_INTR do_mfm +#define DEVICE_REQUEST do_mfm_request +#define DEVICE_NR(device) (MINOR(device) >> 6) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + #elif (MAJOR_NR == NBD_MAJOR) #define DEVICE_NAME "nbd" diff -u --recursive --new-file v2.3.6/linux/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h --- v2.3.6/linux/include/linux/ext2_fs.h Tue May 11 14:37:47 1999 +++ linux/include/linux/ext2_fs.h Wed Jun 16 19:26:27 1999 @@ -556,6 +556,7 @@ extern int ext2_bmap (struct inode *, int); extern struct buffer_head * ext2_getblk (struct inode *, long, int, int *); +extern int ext2_getblk_block (struct inode *, long, int, int *, int *); extern struct buffer_head * ext2_bread (struct inode *, int, int, int *); extern int ext2_getcluster (struct inode * inode, long block); diff -u --recursive --new-file v2.3.6/linux/include/linux/fd1772.h linux/include/linux/fd1772.h --- v2.3.6/linux/include/linux/fd1772.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/fd1772.h Thu Jun 17 01:11:35 1999 @@ -0,0 +1,80 @@ +#ifndef _LINUX_FD1772REG_H +#define _LINUX_FD1772REG_H + +/* +** WD1772 stuff - originally from the M68K Linux + * Modified for Archimedes by Dave Gilbert (gilbertd@cs.man.ac.uk) + */ + +/* register codes */ + +#define FDC1772SELREG_STP (0x80) /* command/status register */ +#define FDC1772SELREG_TRA (0x82) /* track register */ +#define FDC1772SELREG_SEC (0x84) /* sector register */ +#define FDC1772SELREG_DTA (0x86) /* data register */ + +/* register names for FDC1772_READ/WRITE macros */ + +#define FDC1772REG_CMD 0 +#define FDC1772REG_STATUS 0 +#define FDC1772REG_TRACK 2 +#define FDC1772REG_SECTOR 4 +#define FDC1772REG_DATA 6 + +/* command opcodes */ + +#define FDC1772CMD_RESTORE (0x00) /* - */ +#define FDC1772CMD_SEEK (0x10) /* | */ +#define FDC1772CMD_STEP (0x20) /* | TYP 1 Commands */ +#define FDC1772CMD_STIN (0x40) /* | */ +#define FDC1772CMD_STOT (0x60) /* - */ +#define FDC1772CMD_RDSEC (0x80) /* - TYP 2 Commands */ +#define FDC1772CMD_WRSEC (0xa0) /* - " */ +#define FDC1772CMD_RDADR (0xc0) /* - */ +#define FDC1772CMD_RDTRA (0xe0) /* | TYP 3 Commands */ +#define FDC1772CMD_WRTRA (0xf0) /* - */ +#define FDC1772CMD_FORCI (0xd0) /* - TYP 4 Command */ + +/* command modifier bits */ + +#define FDC1772CMDADD_SR6 (0x00) /* step rate settings */ +#define FDC1772CMDADD_SR12 (0x01) +#define FDC1772CMDADD_SR2 (0x02) +#define FDC1772CMDADD_SR3 (0x03) +#define FDC1772CMDADD_V (0x04) /* verify */ +#define FDC1772CMDADD_H (0x08) /* wait for spin-up */ +#define FDC1772CMDADD_U (0x10) /* update track register */ +#define FDC1772CMDADD_M (0x10) /* multiple sector access */ +#define FDC1772CMDADD_E (0x04) /* head settling flag */ +#define FDC1772CMDADD_P (0x02) /* precompensation */ +#define FDC1772CMDADD_A0 (0x01) /* DAM flag */ + +/* status register bits */ + +#define FDC1772STAT_MOTORON (0x80) /* motor on */ +#define FDC1772STAT_WPROT (0x40) /* write protected (FDC1772CMD_WR*) */ +#define FDC1772STAT_SPINUP (0x20) /* motor speed stable (Type I) */ +#define FDC1772STAT_DELDAM (0x20) /* sector has deleted DAM (Type II+III) */ +#define FDC1772STAT_RECNF (0x10) /* record not found */ +#define FDC1772STAT_CRC (0x08) /* CRC error */ +#define FDC1772STAT_TR00 (0x04) /* Track 00 flag (Type I) */ +#define FDC1772STAT_LOST (0x04) /* Lost Data (Type II+III) */ +#define FDC1772STAT_IDX (0x02) /* Index status (Type I) */ +#define FDC1772STAT_DRQ (0x02) /* DRQ status (Type II+III) */ +#define FDC1772STAT_BUSY (0x01) /* FDC1772 is busy */ + + +/* PSG Port A Bit Nr 0 .. Side Sel .. 0 -> Side 1 1 -> Side 2 */ +#define DSKSIDE (0x01) + +#define DSKDRVNONE (0x06) +#define DSKDRV0 (0x02) +#define DSKDRV1 (0x04) + +/* step rates */ +#define FDC1772STEP_6 0x00 +#define FDC1772STEP_12 0x01 +#define FDC1772STEP_2 0x02 +#define FDC1772STEP_3 0x03 + +#endif diff -u --recursive --new-file v2.3.6/linux/include/linux/fs.h linux/include/linux/fs.h --- v2.3.6/linux/include/linux/fs.h Tue Jun 8 23:03:41 1999 +++ linux/include/linux/fs.h Sun Jun 20 17:46:16 1999 @@ -74,11 +74,11 @@ /* public flags for file_system_type */ #define FS_REQUIRES_DEV 1 -#define FS_NO_DCACHE 2 /* Only dcache the necessary things. */ -#define FS_NO_PRELIM 4 /* prevent preloading of dentries, even if +#define FS_NO_DCACHE 2 /* Only dcache the necessary things. */ +#define FS_NO_PRELIM 4 /* prevent preloading of dentries, even if * FS_NO_DCACHE is not set. */ -#define FS_IBASKET 8 /* FS does callback to free_ibasket() if space gets low. */ +#define FS_IBASKET 8 /* FS does callback to free_ibasket() if space gets low. */ /* * These are the fs-independent mount-flags: up to 16 flags are supported @@ -94,9 +94,9 @@ #define S_APPEND 256 /* Append-only file */ #define S_IMMUTABLE 512 /* Immutable file */ #define MS_NOATIME 1024 /* Do not update access times. */ -#define MS_NODIRATIME 2048 /* Do not update directory access times */ +#define MS_NODIRATIME 2048 /* Do not update directory access times */ -#define MS_ODD_RENAME 32768 /* Temporary stuff; will go away as soon +#define MS_ODD_RENAME 32768 /* Temporary stuff; will go away as soon * as nfs_rename() will be cleaned up */ @@ -189,7 +189,6 @@ #define BH_Lock 2 /* 1 if the buffer is locked */ #define BH_Req 3 /* 0 if the buffer has been invalidated */ #define BH_Protected 6 /* 1 if the buffer is protected */ - /* * Try to keep the most commonly used fields in single cache lines (16 * bytes) to improve performance. This ordering should be @@ -218,7 +217,7 @@ /* Non-performance-critical data follows. */ char * b_data; /* pointer to data block (1024 bytes) */ unsigned int b_list; /* List that this buffer appears */ - unsigned long b_flushtime; /* Time when this (dirty) buffer + unsigned long b_flushtime; /* Time when this (dirty) buffer * should be written */ wait_queue_head_t b_wait; struct buffer_head ** b_pprev; /* doubly linked list of hash-queue */ @@ -235,30 +234,13 @@ typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate); void init_buffer(struct buffer_head *, kdev_t, int, bh_end_io_t *, void *); -static inline int buffer_uptodate(struct buffer_head * bh) -{ - return test_bit(BH_Uptodate, &bh->b_state); -} - -static inline int buffer_dirty(struct buffer_head * bh) -{ - return test_bit(BH_Dirty, &bh->b_state); -} +#define __buffer_state(bh, state) (((bh)->b_state & (1UL << BH_##state)) != 0) -static inline int buffer_locked(struct buffer_head * bh) -{ - return test_bit(BH_Lock, &bh->b_state); -} - -static inline int buffer_req(struct buffer_head * bh) -{ - return test_bit(BH_Req, &bh->b_state); -} - -static inline int buffer_protected(struct buffer_head * bh) -{ - return test_bit(BH_Protected, &bh->b_state); -} +#define buffer_uptodate(bh) __buffer_state(bh,Uptodate) +#define buffer_dirty(bh) __buffer_state(bh,Dirty) +#define buffer_locked(bh) __buffer_state(bh,Lock) +#define buffer_req(bh) __buffer_state(bh,Req) +#define buffer_protected(bh) __buffer_state(bh,Protected) #define buffer_page(bh) (mem_map + MAP_NR((bh)->b_data)) #define touch_buffer(bh) set_bit(PG_referenced, &buffer_page(bh)->flags) @@ -357,7 +339,6 @@ unsigned long i_version; unsigned long i_nrpages; struct semaphore i_sem; - struct semaphore i_atomic_write; struct inode_operations *i_op; struct super_block *i_sb; wait_queue_head_t i_wait; @@ -365,22 +346,21 @@ struct vm_area_struct *i_mmap; struct page *i_pages; struct dquot *i_dquot[MAXQUOTAS]; + struct pipe_inode_info *i_pipe; unsigned long i_state; unsigned int i_flags; - unsigned char i_pipe; unsigned char i_sock; int i_writecount; unsigned int i_attr_flags; __u32 i_generation; union { - struct pipe_inode_info pipe_i; struct minix_inode_info minix_i; struct ext2_inode_info ext2_i; struct hpfs_inode_info hpfs_i; - struct ntfs_inode_info ntfs_i; + struct ntfs_inode_info ntfs_i; struct msdos_inode_info msdos_i; struct umsdos_inode_info umsdos_i; struct iso_inode_info isofs_i; @@ -388,13 +368,13 @@ struct sysv_inode_info sysv_i; struct affs_inode_info affs_i; struct ufs_inode_info ufs_i; - struct efs_inode_info efs_i; + struct efs_inode_info efs_i; struct romfs_inode_info romfs_i; struct coda_inode_info coda_i; struct smb_inode_info smbfs_i; struct hfs_inode_info hfs_i; struct adfs_inode_info adfs_i; - struct qnx4_inode_info qnx4_i; + struct qnx4_inode_info qnx4_i; struct socket socket_i; void *generic_ip; } u; @@ -491,10 +471,10 @@ extern void posix_unblock_lock(struct file_lock *); struct fasync_struct { - int magic; - int fa_fd; - struct fasync_struct *fa_next; /* singly linked list */ - struct file *fa_file; + int magic; + int fa_fd; + struct fasync_struct *fa_next; /* singly linked list */ + struct file *fa_file; }; #define FASYNC_MAGIC 0x4601 @@ -547,19 +527,19 @@ struct minix_sb_info minix_sb; struct ext2_sb_info ext2_sb; struct hpfs_sb_info hpfs_sb; - struct ntfs_sb_info ntfs_sb; + struct ntfs_sb_info ntfs_sb; struct msdos_sb_info msdos_sb; struct isofs_sb_info isofs_sb; struct nfs_sb_info nfs_sb; struct sysv_sb_info sysv_sb; struct affs_sb_info affs_sb; struct ufs_sb_info ufs_sb; - struct efs_sb_info efs_sb; + struct efs_sb_info efs_sb; struct romfs_sb_info romfs_sb; struct smb_sb_info smbfs_sb; struct hfs_sb_info hfs_sb; struct adfs_sb_info adfs_sb; - struct qnx4_sb_info qnx4_sb; + struct qnx4_sb_info qnx4_sb; void *generic_sbp; } u; /* @@ -616,13 +596,22 @@ struct inode *, struct dentry *); int (*readlink) (struct dentry *, char *,int); struct dentry * (*follow_link) (struct dentry *, struct dentry *, unsigned int); + /* + * the order of these functions within the VFS template has been + * changed because SMP locking has changed: from now on all bmap, + * readpage, writepage and flushpage functions are supposed to do + * whatever locking they need to get proper SMP operation - for + * now in most cases this means a lock/unlock_kernel at entry/exit. + * [The new order is also slightly more logical :)] + */ + int (*bmap) (struct inode *,int); int (*readpage) (struct file *, struct page *); int (*writepage) (struct file *, struct page *); - int (*bmap) (struct inode *,int); + int (*flushpage) (struct inode *, struct page *, unsigned long); + void (*truncate) (struct inode *); int (*permission) (struct inode *, int); int (*smap) (struct inode *,int); - int (*updatepage) (struct file *, struct page *, unsigned long, unsigned int); int (*revalidate) (struct dentry *); }; @@ -749,13 +738,11 @@ extern struct file *inuse_filps; -extern void refile_buffer(struct buffer_head *); extern void set_writetime(struct buffer_head *, int); extern int try_to_free_buffers(struct page *); +extern void refile_buffer(struct buffer_head * buf); -extern int nr_buffers; extern int buffermem; -extern int nr_buffer_heads; #define BUF_CLEAN 0 #define BUF_LOCKED 1 /* Buffers scheduled for write */ @@ -766,21 +753,36 @@ extern inline void mark_buffer_clean(struct buffer_head * bh) { - if (test_and_clear_bit(BH_Dirty, &bh->b_state)) { - if (bh->b_list == BUF_DIRTY) - refile_buffer(bh); - } + if (test_and_clear_bit(BH_Dirty, &bh->b_state)) + refile_buffer(bh); } +extern void FASTCALL(__mark_buffer_dirty(struct buffer_head *bh, int flag)); +extern void FASTCALL(__atomic_mark_buffer_dirty(struct buffer_head *bh, int flag)); + +#define atomic_set_buffer_dirty(bh) test_and_set_bit(BH_Dirty, &(bh)->b_state) + extern inline void mark_buffer_dirty(struct buffer_head * bh, int flag) { - if (!test_and_set_bit(BH_Dirty, &bh->b_state)) { - set_writetime(bh, flag); - if (bh->b_list != BUF_DIRTY) - refile_buffer(bh); - } + if (!atomic_set_buffer_dirty(bh)) + __mark_buffer_dirty(bh, flag); +} + +/* + * SMP-safe version of the above - does synchronization with + * other users of buffer-cache data structures. + * + * since we test-set the dirty bit in a CPU-atomic way we also + * have optimized the common 'redirtying' case away completely. + */ +extern inline void atomic_mark_buffer_dirty(struct buffer_head * bh, int flag) +{ + if (!atomic_set_buffer_dirty(bh)) + __atomic_mark_buffer_dirty(bh, flag); } + +extern void balance_dirty(kdev_t); extern int check_disk_change(kdev_t); extern int invalidate_inodes(struct super_block *); extern void invalidate_inode_pages(struct inode *); @@ -869,12 +871,19 @@ extern int brw_page(int, struct page *, kdev_t, int [], int, int); typedef long (*writepage_t)(struct file *, struct page *, unsigned long, unsigned long, const char *); +typedef int (*fs_getblock_t)(struct inode *, long, int, int *, int *); + +/* Generic buffer handling for block filesystems.. */ +extern int block_read_full_page(struct file *, struct page *); +extern int block_write_full_page (struct file *, struct page *, fs_getblock_t); +extern int block_write_partial_page (struct file *, struct page *, unsigned long, unsigned long, const char *, fs_getblock_t); +extern int block_flushpage(struct inode *, struct page *, unsigned long); -extern int generic_readpage(struct file *, struct page *); extern int generic_file_mmap(struct file *, struct vm_area_struct *); extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *); extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *, writepage_t); + extern struct super_block *get_super(kdev_t); extern void put_super(kdev_t); unsigned long generate_cluster(kdev_t, int b[], int); @@ -898,6 +907,7 @@ extern int block_fsync(struct file *, struct dentry *); extern int file_fsync(struct file *, struct dentry *); +extern int generic_buffer_fdatasync(struct inode *inode, unsigned long start, unsigned long end); extern int inode_change_ok(struct inode *, struct iattr *); extern void inode_setattr(struct inode *, struct iattr *); diff -u --recursive --new-file v2.3.6/linux/include/linux/hpfs_fs_i.h linux/include/linux/hpfs_fs_i.h --- v2.3.6/linux/include/linux/hpfs_fs_i.h Fri May 14 18:30:46 1999 +++ linux/include/linux/hpfs_fs_i.h Wed Jun 16 19:26:27 1999 @@ -1,17 +1,7 @@ #ifndef _HPFS_FS_I #define _HPFS_FS_I -#if ANALWARNINGS -#warning Fix the FIFO stuff! -#warning Fix the FIFO stuff! -#warning Fix the FIFO stuff! -#endif - struct hpfs_inode_info { - union { /* Linux sometimes destroys this structure */ - struct pipe_inode_info bla; /* due to a bug. Linus doesn't want to fix */ - struct socket ble; /* it so I had to write this workaround :-) */ - } dummy; ino_t i_parent_dir; /* (directories) gives fnode of parent dir */ unsigned i_dno; /* (directories) root dnode */ unsigned i_dpos; /* (directories) temp for readdir */ diff -u --recursive --new-file v2.3.6/linux/include/linux/ioport.h linux/include/linux/ioport.h --- v2.3.6/linux/include/linux/ioport.h Wed Oct 22 08:31:55 1997 +++ linux/include/linux/ioport.h Fri Jun 18 12:43:41 1999 @@ -1,14 +1,38 @@ /* - * portio.h Definitions of routines for detecting, reserving and + * ioport.h Definitions of routines for detecting, reserving and * allocating system resources. * - * Version: 0.01 8/30/93 - * - * Author: Donald Becker (becker@super.org) + * Authors: Donald Becker (becker@cesdis.gsfc.nasa.gov) + * David Hinds (dhinds@zen.stanford.edu) */ -#ifndef _LINUX_PORTIO_H -#define _LINUX_PORTIO_H +#ifndef _LINUX_IOPORT_H +#define _LINUX_IOPORT_H + +#define RES_IO 0 +#define RES_MEM 1 + +extern void reserve_setup(char *str, int *ints); + +extern struct resource_entry *iolist, *memlist; + +extern int get_resource_list(int class, char *buf); +extern int check_resource(int class, + unsigned long from, unsigned long extent); +extern void request_resource(int class, + unsigned long from, unsigned long extent, + const char *name); +extern void release_resource(int class, + unsigned long from, unsigned long extent); +extern unsigned long occupy_resource(int class, + unsigned long base, unsigned long end, + unsigned long num, unsigned long align, + const char *name); +extern void vacate_resource(int class, + unsigned long from, unsigned long extent); + +#define get_ioport_list(buf) get_resource_list(RES_IO, buf) +#define get_mem_list(buf) get_resource_list(RES_MEM, buf) #define HAVE_PORTRESERVE /* @@ -16,20 +40,21 @@ * Once you have found you hardware, register it with request_region(). * If you unload the driver, use release_region to free ports. */ -extern void reserve_setup(char *str, int *ints); -extern int check_region(unsigned long from, unsigned long extent); -extern void request_region(unsigned long from, unsigned long extent,const char *name); -extern void release_region(unsigned long from, unsigned long extent); -extern int get_ioport_list(char *); - -#ifdef __sparc__ -extern unsigned long occupy_region(unsigned long base, unsigned long end, - unsigned long num, unsigned int align, - const char *name); -#endif +#define check_region(f,e) check_resource(RES_IO,f,e) +#define request_region(f,e,n) request_resource(RES_IO,f,e,n) +#define release_region(f,e) release_resource(RES_IO,f,e) +#define occupy_region(b,e,n,a,s) occupy_resource(RES_IO,b,e,n,a,s) +#define vacate_region(f,e) vacate_resource(RES_IO,f,e) + +#define HAVE_MEMRESERVE +#define check_mem_region(f,e) check_resource(RES_MEM,f,e) +#define request_mem_region(f,e,n) request_resource(RES_MEM,f,e,n) +#define release_mem_region(f,e) release_resource(RES_MEM,f,e) +#define occupy_mem_region(b,e,n,a,s) occupy_resource(RES_MEM,b,e,n,a,s) +#define vacate_mem_region(f,e) vacate_resource(RES_MEM,f,e) #define HAVE_AUTOIRQ extern void autoirq_setup(int waittime); extern int autoirq_report(int waittime); -#endif /* _LINUX_PORTIO_H */ +#endif /* _LINUX_IOPORT_H */ diff -u --recursive --new-file v2.3.6/linux/include/linux/minix_fs.h linux/include/linux/minix_fs.h --- v2.3.6/linux/include/linux/minix_fs.h Fri Apr 23 21:20:38 1999 +++ linux/include/linux/minix_fs.h Wed Jun 16 19:26:27 1999 @@ -110,6 +110,7 @@ extern int minix_bmap(struct inode *,int); extern struct buffer_head * minix_getblk(struct inode *, int, int); +extern int minix_getblk_block (struct inode *, long, int, int *, int *); extern struct buffer_head * minix_bread(struct inode *, int, int); extern void minix_truncate(struct inode *); diff -u --recursive --new-file v2.3.6/linux/include/linux/mm.h linux/include/linux/mm.h --- v2.3.6/linux/include/linux/mm.h Tue Jun 8 23:03:47 1999 +++ linux/include/linux/mm.h Sun Jun 20 17:46:21 1999 @@ -129,29 +129,57 @@ wait_queue_head_t wait; struct page **pprev_hash; struct buffer_head * buffers; + int owner; /* temporary debugging check */ } mem_map_t; +#define get_page(p) do { atomic_inc(&(p)->count); \ + } while (0) +#define put_page(p) __free_page(p) +#define put_page_testzero(p) ({ int __ret = atomic_dec_and_test(&(p)->count);\ + __ret; }) +#define page_count(p) atomic_read(&(p)->count) +#define set_page_count(p,v) do { atomic_set(&(p)->count, v); \ + } while (0) + /* Page flag bit values */ #define PG_locked 0 #define PG_error 1 #define PG_referenced 2 -#define PG_dirty 3 -#define PG_uptodate 4 -#define PG_free_after 5 -#define PG_decr_after 6 -#define PG_swap_unlock_after 7 -#define PG_DMA 8 -#define PG_Slab 9 -#define PG_swap_cache 10 -#define PG_skip 11 +#define PG_uptodate 3 +#define PG_free_after 4 +#define PG_decr_after 5 +#define PG_swap_unlock_after 6 +#define PG_DMA 7 +#define PG_Slab 8 +#define PG_swap_cache 9 +#define PG_skip 10 + /* bits 21-30 unused */ #define PG_reserved 31 + /* Make it prettier to test the above... */ +#define Page_Uptodate(page) (test_bit(PG_uptodate, &(page)->flags)) +#define SetPageUptodate(page) do { set_bit(PG_uptodate, &(page)->flags); \ + } while (0) +#define ClearPageUptodate(page) do { clear_bit(PG_uptodate, &(page)->flags); \ + } while (0) #define PageLocked(page) (test_bit(PG_locked, &(page)->flags)) +#define LockPage(page) \ + do { int _ret = test_and_set_bit(PG_locked, &(page)->flags); \ + if (_ret) PAGE_BUG(page); \ + if (page->owner) PAGE_BUG(page); \ + page->owner = (int)current; } while (0) +#define TryLockPage(page) ({ int _ret = test_and_set_bit(PG_locked, &(page)->flags); \ + if (!_ret) page->owner = (int)current; _ret; }) +#define UnlockPage(page) do { \ + if (page->owner != (int)current) { \ +BUG(); } page->owner = 0; \ +if (!test_and_clear_bit(PG_locked, &(page)->flags)) { \ + PAGE_BUG(page); } wake_up(&page->wait); } while (0) #define PageError(page) (test_bit(PG_error, &(page)->flags)) +#define SetPageError(page) ({ int _ret = test_and_set_bit(PG_error, &(page)->flags); _ret; }) +#define ClearPageError(page) do { if (!test_and_clear_bit(PG_error, &(page)->flags)) BUG(); } while (0) #define PageReferenced(page) (test_bit(PG_referenced, &(page)->flags)) -#define PageDirty(page) (test_bit(PG_dirty, &(page)->flags)) -#define PageUptodate(page) (test_bit(PG_uptodate, &(page)->flags)) #define PageFreeAfter(page) (test_bit(PG_free_after, &(page)->flags)) #define PageDecrAfter(page) (test_bit(PG_decr_after, &(page)->flags)) #define PageSwapUnlockAfter(page) (test_bit(PG_swap_unlock_after, &(page)->flags)) @@ -163,16 +191,12 @@ #define PageSetSlab(page) (set_bit(PG_Slab, &(page)->flags)) #define PageSetSwapCache(page) (set_bit(PG_swap_cache, &(page)->flags)) -#define PageTestandSetDirty(page) \ - (test_and_set_bit(PG_dirty, &(page)->flags)) #define PageTestandSetSwapCache(page) \ (test_and_set_bit(PG_swap_cache, &(page)->flags)) #define PageClearSlab(page) (clear_bit(PG_Slab, &(page)->flags)) #define PageClearSwapCache(page)(clear_bit(PG_swap_cache, &(page)->flags)) -#define PageTestandClearDirty(page) \ - (test_and_clear_bit(PG_dirty, &(page)->flags)) #define PageTestandClearSwapCache(page) \ (test_and_clear_bit(PG_swap_cache, &(page)->flags)) @@ -274,8 +298,8 @@ /* memory.c & swap.c*/ #define free_page(addr) free_pages((addr),0) -extern void FASTCALL(free_pages(unsigned long addr, unsigned long order)); -extern void FASTCALL(__free_page(struct page *)); +extern int FASTCALL(free_pages(unsigned long addr, unsigned long order)); +extern int FASTCALL(__free_page(struct page *)); extern void show_free_areas(void); extern unsigned long put_dirty_page(struct task_struct * tsk,unsigned long page, @@ -387,7 +411,7 @@ #define buffer_under_min() ((buffermem >> PAGE_SHIFT) * 100 < \ buffer_mem.min_percent * num_physpages) -#define pgcache_under_min() (page_cache_size * 100 < \ +#define pgcache_under_min() (atomic_read(&page_cache_size) * 100 < \ page_cache.min_percent * num_physpages) #endif /* __KERNEL__ */ diff -u --recursive --new-file v2.3.6/linux/include/linux/msdos_fs_i.h linux/include/linux/msdos_fs_i.h --- v2.3.6/linux/include/linux/msdos_fs_i.h Thu May 13 23:18:21 1999 +++ linux/include/linux/msdos_fs_i.h Wed Jun 16 19:26:27 1999 @@ -1,30 +1,11 @@ #ifndef _MSDOS_FS_I #define _MSDOS_FS_I -#ifndef _LINUX_PIPE_FS_I_H -#include -#endif - /* * MS-DOS file system inode data in memory */ struct msdos_inode_info { - /* - UMSDOS manage special file and fifo as normal empty - msdos file. fifo inode processing conflict with msdos - processing. So I insert the pipe_inode_info so the - information does not overlap. This increases the size of - the msdos_inode_info, but the clear winner here is - the ext2_inode_info. So it does not change anything to - the total size of a struct inode. - - I have not put it conditional. With the advent of loadable - file system drivers, it would be very easy to compile - a MS-DOS FS driver unaware of UMSDOS and then later to - load a (then incompatible) UMSDOS FS driver. - */ - struct pipe_inode_info reserved; int i_start; /* first cluster or 0 */ int i_logstart; /* logical first cluster */ int i_attrs; /* unused attribute bits */ diff -u --recursive --new-file v2.3.6/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h --- v2.3.6/linux/include/linux/nfs_fs.h Tue Jun 8 23:03:44 1999 +++ linux/include/linux/nfs_fs.h Sun Jun 20 17:46:45 1999 @@ -192,7 +192,8 @@ */ extern struct inode_operations nfs_dir_inode_operations; extern struct dentry_operations nfs_dentry_operations; -extern void nfs_invalidate_dircache(struct inode *); +extern void nfs_flush_dircache(struct inode *); +extern void nfs_free_dircache(struct inode *); /* * linux/fs/nfs/symlink.c diff -u --recursive --new-file v2.3.6/linux/include/linux/nfs_fs_i.h linux/include/linux/nfs_fs_i.h --- v2.3.6/linux/include/linux/nfs_fs_i.h Tue Jun 8 22:42:23 1999 +++ linux/include/linux/nfs_fs_i.h Wed Jun 16 19:26:27 1999 @@ -9,13 +9,6 @@ */ struct nfs_inode_info { /* - * This is a place holder so named pipes on NFS filesystems - * work (more or less correctly). This must be first in the - * struct because the data is really accessed via inode->u.pipe_i. - */ - struct pipe_inode_info pipeinfo; - - /* * Various flags */ unsigned short flags; diff -u --recursive --new-file v2.3.6/linux/include/linux/pagemap.h linux/include/linux/pagemap.h --- v2.3.6/linux/include/linux/pagemap.h Tue Jun 8 23:03:47 1999 +++ linux/include/linux/pagemap.h Sun Jun 20 17:46:24 1999 @@ -39,10 +39,10 @@ */ #define page_cache_entry(x) (mem_map + MAP_NR(x)) -#define PAGE_HASH_BITS 12 +#define PAGE_HASH_BITS 16 #define PAGE_HASH_SIZE (1 << PAGE_HASH_BITS) -extern unsigned long page_cache_size; /* # of pages currently in the hash table */ +extern atomic_t page_cache_size; /* # of pages currently in the hash table */ extern struct page * page_hash_table[PAGE_HASH_SIZE]; /* @@ -64,72 +64,25 @@ #define page_hash(inode,offset) (page_hash_table+_page_hashfn(inode,offset)) -static inline struct page * __find_page(struct inode * inode, unsigned long offset, struct page *page) -{ - goto inside; - for (;;) { - page = page->next_hash; -inside: - if (!page) - goto not_found; - if (page->inode != inode) - continue; - if (page->offset == offset) - break; - } - /* Found the page. */ - atomic_inc(&page->count); - set_bit(PG_referenced, &page->flags); -not_found: - return page; -} +extern struct page * __find_get_page (struct inode * inode, + unsigned long offset, struct page **hash); +#define find_get_page(inode, offset) \ + __find_get_page(inode, offset, page_hash(inode, offset)) +extern struct page * __find_lock_page (struct inode * inode, + unsigned long offset, struct page **hash); +extern void lock_page(struct page *page); +#define find_lock_page(inode, offset) \ + __find_lock_page(inode, offset, page_hash(inode, offset)) -static inline struct page *find_page(struct inode * inode, unsigned long offset) -{ - return __find_page(inode, offset, *page_hash(inode, offset)); -} +extern void __add_page_to_hash_queue(struct page * page, struct page **p); -static inline void remove_page_from_hash_queue(struct page * page) -{ - if(page->pprev_hash) { - if(page->next_hash) - page->next_hash->pprev_hash = page->pprev_hash; - *page->pprev_hash = page->next_hash; - page->pprev_hash = NULL; - } - page_cache_size--; -} - -static inline void __add_page_to_hash_queue(struct page * page, struct page **p) -{ - page_cache_size++; - if((page->next_hash = *p) != NULL) - (*p)->pprev_hash = &page->next_hash; - *p = page; - page->pprev_hash = p; -} +extern int add_to_page_cache_unique(struct page * page, struct inode * inode, unsigned long offset, struct page **hash); static inline void add_page_to_hash_queue(struct page * page, struct inode * inode, unsigned long offset) { __add_page_to_hash_queue(page, page_hash(inode,offset)); } -static inline void remove_page_from_inode_queue(struct page * page) -{ - struct inode * inode = page->inode; - - page->inode = NULL; - inode->i_nrpages--; - if (inode->i_pages == page) - inode->i_pages = page->next; - if (page->next) - page->next->prev = page->prev; - if (page->prev) - page->prev->next = page->next; - page->next = NULL; - page->prev = NULL; -} - static inline void add_page_to_inode_queue(struct inode * inode, struct page * page) { struct page **p = &inode->i_pages; @@ -142,11 +95,13 @@ *p = page; } -extern void __wait_on_page(struct page *); +extern void ___wait_on_page(struct page *); + static inline void wait_on_page(struct page * page) { + if (PageLocked(page)) - __wait_on_page(page); + ___wait_on_page(page); } extern void update_vm_cache(struct inode *, unsigned long, const char *, int); diff -u --recursive --new-file v2.3.6/linux/include/linux/pci.h linux/include/linux/pci.h --- v2.3.6/linux/include/linux/pci.h Wed Jun 9 16:59:16 1999 +++ linux/include/linux/pci.h Sun Jun 20 17:46:14 1999 @@ -280,7 +280,7 @@ /* * Vendor and card ID's: sort these numerically according to vendor * (and according to card ID within vendor). Send all updates to - * . + * . */ #define PCI_VENDOR_ID_COMPAQ 0x0e11 #define PCI_DEVICE_ID_COMPAQ_1280 0x3033 @@ -1253,6 +1253,8 @@ struct pci_dev *pci_find_device (unsigned int vendor, unsigned int device, struct pci_dev *from); struct pci_dev *pci_find_class (unsigned int class, struct pci_dev *from); struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn); + +#define PCI_ANY_ID (~0) #define pci_present pcibios_present int pci_read_config_byte(struct pci_dev *dev, u8 where, u8 *val); diff -u --recursive --new-file v2.3.6/linux/include/linux/pipe_fs_i.h linux/include/linux/pipe_fs_i.h --- v2.3.6/linux/include/linux/pipe_fs_i.h Tue May 11 14:37:40 1999 +++ linux/include/linux/pipe_fs_i.h Wed Jun 16 19:26:27 1999 @@ -12,15 +12,15 @@ unsigned int writers; }; -#define PIPE_WAIT(inode) ((inode).u.pipe_i.wait) -#define PIPE_BASE(inode) ((inode).u.pipe_i.base) -#define PIPE_START(inode) ((inode).u.pipe_i.start) +#define PIPE_WAIT(inode) ((inode).i_pipe->wait) +#define PIPE_BASE(inode) ((inode).i_pipe->base) +#define PIPE_START(inode) ((inode).i_pipe->start) #define PIPE_LEN(inode) ((inode).i_size) -#define PIPE_RD_OPENERS(inode) ((inode).u.pipe_i.rd_openers) -#define PIPE_WR_OPENERS(inode) ((inode).u.pipe_i.wr_openers) -#define PIPE_READERS(inode) ((inode).u.pipe_i.readers) -#define PIPE_WRITERS(inode) ((inode).u.pipe_i.writers) -#define PIPE_LOCK(inode) ((inode).u.pipe_i.lock) +#define PIPE_RD_OPENERS(inode) ((inode).i_pipe->rd_openers) +#define PIPE_WR_OPENERS(inode) ((inode).i_pipe->wr_openers) +#define PIPE_READERS(inode) ((inode).i_pipe->readers) +#define PIPE_WRITERS(inode) ((inode).i_pipe->writers) +#define PIPE_LOCK(inode) ((inode).i_pipe->lock) #define PIPE_SIZE(inode) PIPE_LEN(inode) #define PIPE_EMPTY(inode) (PIPE_SIZE(inode)==0) diff -u --recursive --new-file v2.3.6/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h --- v2.3.6/linux/include/linux/proc_fs.h Tue Jun 8 23:04:14 1999 +++ linux/include/linux/proc_fs.h Sun Jun 20 17:46:43 1999 @@ -37,6 +37,7 @@ PROC_KSYMS, PROC_DMA, PROC_IOPORTS, + PROC_MEMORY, PROC_PROFILE, /* whether enabled or not */ PROC_CMDLINE, PROC_SYS, diff -u --recursive --new-file v2.3.6/linux/include/linux/sched.h linux/include/linux/sched.h --- v2.3.6/linux/include/linux/sched.h Tue Jun 8 23:03:44 1999 +++ linux/include/linux/sched.h Sun Jun 20 17:46:18 1999 @@ -286,7 +286,7 @@ gid_t gid,egid,sgid,fsgid; int ngroups; gid_t groups[NGROUPS]; - kernel_cap_t cap_effective, cap_inheritable, cap_permitted; + kernel_cap_t cap_effective, cap_inheritable, cap_permitted; struct user_struct *user; /* limits */ struct rlimit rlim[RLIM_NLIMITS]; @@ -601,7 +601,7 @@ #else if (cap_is_fs_cap(cap) ? current->fsuid == 0 : current->euid == 0) #endif - { + { current->flags |= PF_SUPERPRIV; return 1; } diff -u --recursive --new-file v2.3.6/linux/include/linux/smb.h linux/include/linux/smb.h --- v2.3.6/linux/include/linux/smb.h Sun Dec 27 22:18:28 1998 +++ linux/include/linux/smb.h Wed Jun 16 19:26:27 1999 @@ -57,7 +57,7 @@ /* The following are NT LM 0.12 options */ __u32 maxraw; __u32 capabilities; - __u16 serverzone; + __s16 serverzone; }; #ifdef __KERNEL__ diff -u --recursive --new-file v2.3.6/linux/include/linux/smb_fs.h linux/include/linux/smb_fs.h --- v2.3.6/linux/include/linux/smb_fs.h Tue Jun 8 23:05:13 1999 +++ linux/include/linux/smb_fs.h Sun Jun 20 17:47:56 1999 @@ -77,6 +77,22 @@ #define SMB_FIX_OLDATTR 0x0002 /* Use core getattr (Win 95 speedup) */ #define SMB_FIX_DIRATTR 0x0004 /* Use find_first for getattr */ + +/* NT1 protocol capability bits */ +#define SMB_CAP_RAW_MODE 0x0001 +#define SMB_CAP_MPX_MODE 0x0002 +#define SMB_CAP_UNICODE 0x0004 +#define SMB_CAP_LARGE_FILES 0x0008 +#define SMB_CAP_NT_SMBS 0x0010 +#define SMB_CAP_RPC_REMOTE_APIS 0x0020 +#define SMB_CAP_STATUS32 0x0040 +#define SMB_CAP_LEVEL_II_OPLOCKS 0x0080 +#define SMB_CAP_LOCK_AND_READ 0x0100 +#define SMB_CAP_NT_FIND 0x0200 +#define SMB_CAP_DFS 0x1000 +#define SMB_CAP_LARGE_READX 0x4000 + + /* linux/fs/smbfs/mmap.c */ int smb_mmap(struct file *, struct vm_area_struct *); diff -u --recursive --new-file v2.3.6/linux/include/linux/swap.h linux/include/linux/swap.h --- v2.3.6/linux/include/linux/swap.h Tue Jun 8 23:03:40 1999 +++ linux/include/linux/swap.h Sun Jun 20 17:46:14 1999 @@ -67,7 +67,7 @@ extern int nr_free_pages; extern atomic_t nr_async_pages; extern struct inode swapper_inode; -extern unsigned long page_cache_size; +extern atomic_t page_cache_size; extern int buffermem; /* Incomplete types for prototype declarations: */ @@ -107,6 +107,7 @@ /* * Make these inline later once they are working properly. */ +extern void __delete_from_swap_cache(struct page *page); extern void delete_from_swap_cache(struct page *page); extern void free_page_and_swap_cache(unsigned long addr); @@ -163,7 +164,7 @@ unsigned int count; if (PageReserved(page)) return 1; - count = atomic_read(&page->count); + count = page_count(page); if (PageSwapCache(page)) count += swap_count(page->offset) - 2; if (PageFreeAfter(page)) diff -u --recursive --new-file v2.3.6/linux/include/linux/synclink.h linux/include/linux/synclink.h --- v2.3.6/linux/include/linux/synclink.h Fri Mar 12 08:38:44 1999 +++ linux/include/linux/synclink.h Wed Jun 16 19:26:27 1999 @@ -1,6 +1,8 @@ /* * SyncLink Multiprotocol Serial Adapter Driver * + * ==FILEDATE 19990523== + * * Copyright (C) 1998 by Microgate Corporation * * Redistribution of this file is permitted under @@ -66,11 +68,16 @@ #define HDLC_FLAG_AUTO_RTS 0x0080 #define HDLC_FLAG_RXC_DPLL 0x0100 #define HDLC_FLAG_RXC_BRG 0x0200 +#define HDLC_FLAG_RXC_TXCPIN 0x8000 +#define HDLC_FLAG_RXC_RXCPIN 0x0000 #define HDLC_FLAG_TXC_DPLL 0x0400 #define HDLC_FLAG_TXC_BRG 0x0800 +#define HDLC_FLAG_TXC_TXCPIN 0x0000 +#define HDLC_FLAG_TXC_RXCPIN 0x0008 #define HDLC_FLAG_DPLL_DIV8 0x1000 #define HDLC_FLAG_DPLL_DIV16 0x2000 #define HDLC_FLAG_DPLL_DIV32 0x0000 +#define HDLC_FLAG_HDLC_LOOPMODE 0x4000 #define HDLC_CRC_NONE 0 #define HDLC_CRC_16_CCITT 1 @@ -87,6 +94,7 @@ #define HDLC_ENCODING_NRZB 1 #define HDLC_ENCODING_NRZI_MARK 2 #define HDLC_ENCODING_NRZI_SPACE 3 +#define HDLC_ENCODING_NRZI HDLC_ENCODING_NRZI_SPACE #define HDLC_ENCODING_BIPHASE_MARK 4 #define HDLC_ENCODING_BIPHASE_SPACE 5 #define HDLC_ENCODING_BIPHASE_LEVEL 6 @@ -227,17 +235,19 @@ * MGSL_IOCTXABORT abort transmitting frame (HDLC) * MGSL_IOCGSTATS return current statistics * MGSL_IOCWAITEVENT wait for specified event to occur + * MGSL_LOOPTXDONE transmit in HDLC LoopMode done */ #define MGSL_MAGIC_IOC 'm' -#define MGSL_IOCSPARAMS _IOW(MGSL_MAGIC_IOC,0,sizeof(MGSL_PARAMS)) -#define MGSL_IOCGPARAMS _IOR(MGSL_MAGIC_IOC,1,sizeof(MGSL_PARAMS)) +#define MGSL_IOCSPARAMS _IOW(MGSL_MAGIC_IOC,0,struct _MGSL_PARAMS) +#define MGSL_IOCGPARAMS _IOR(MGSL_MAGIC_IOC,1,struct _MGSL_PARAMS) #define MGSL_IOCSTXIDLE _IO(MGSL_MAGIC_IOC,2) #define MGSL_IOCGTXIDLE _IO(MGSL_MAGIC_IOC,3) #define MGSL_IOCTXENABLE _IO(MGSL_MAGIC_IOC,4) #define MGSL_IOCRXENABLE _IO(MGSL_MAGIC_IOC,5) #define MGSL_IOCTXABORT _IO(MGSL_MAGIC_IOC,6) #define MGSL_IOCGSTATS _IO(MGSL_MAGIC_IOC,7) -#define MGSL_IOCWAITEVENT _IO(MGSL_MAGIC_IOC,8) +#define MGSL_IOCWAITEVENT _IOWR(MGSL_MAGIC_IOC,8,int) #define MGSL_IOCCLRMODCOUNT _IO(MGSL_MAGIC_IOC,15) +#define MGSL_IOCLOOPTXDONE _IO(MGSL_MAGIC_IOC,9) #endif /* _SYNCLINK_H_ */ diff -u --recursive --new-file v2.3.6/linux/include/linux/sysv_fs.h linux/include/linux/sysv_fs.h --- v2.3.6/linux/include/linux/sysv_fs.h Tue Jun 8 23:05:12 1999 +++ linux/include/linux/sysv_fs.h Sun Jun 20 17:47:55 1999 @@ -387,6 +387,7 @@ extern int sysv_bmap(struct inode *,int); extern struct buffer_head * sysv_getblk(struct inode *, unsigned int, int); +extern int sysv_getblk_block(struct inode *, long, int, int *, int *); extern struct buffer_head * sysv_file_bread(struct inode *, int, int); extern ssize_t sysv_file_read(struct file *, char *, size_t, loff_t *); diff -u --recursive --new-file v2.3.6/linux/include/linux/ufs_fs.h linux/include/linux/ufs_fs.h --- v2.3.6/linux/include/linux/ufs_fs.h Tue May 25 14:56:58 1999 +++ linux/include/linux/ufs_fs.h Wed Jun 16 19:26:27 1999 @@ -537,6 +537,7 @@ extern void ufs_write_inode (struct inode *); extern void ufs_delete_inode (struct inode *); extern struct buffer_head * ufs_getfrag (struct inode *, unsigned, int, int *); +extern int ufs_getfrag_block (struct inode *, long, int, int *, int *); extern struct buffer_head * ufs_bread (struct inode *, unsigned, int, int *); /* namei.c */ diff -u --recursive --new-file v2.3.6/linux/include/linux/umsdos_fs_i.h linux/include/linux/umsdos_fs_i.h --- v2.3.6/linux/include/linux/umsdos_fs_i.h Sat May 15 15:05:37 1999 +++ linux/include/linux/umsdos_fs_i.h Wed Jun 16 19:26:27 1999 @@ -28,9 +28,8 @@ * * For directory, we also have a reference to the inode of its * own EMD file. Also, we have dir_locking_info to help synchronise - * file creation and file lookup. This data is sharing space with - * the pipe_inode_info not used by directory. See also msdos_fs_i.h - * for more information about pipe_inode_info and msdos_inode_info. + * file creation and file lookup. See also msdos_fs_i.h for more + * information about msdos_inode_info. * * Special file and fifo do have an inode which correspond to an * empty MSDOS file. @@ -38,11 +37,6 @@ * symlink are processed mostly like regular file. The content is the * link. * - * fifos add there own extension to the inode. I have reserved some - * space for fifos side by side with msdos_inode_info. This is just - * to for the show, because msdos_inode_info already include the - * pipe_inode_info. - * * The UMSDOS specific extension is placed after the union. */ @@ -60,7 +54,6 @@ struct umsdos_inode_info { union { struct msdos_inode_info msdos_info; - struct pipe_inode_info pipe_info; struct dir_locking_info dir_info; } u; int i_patched; /* Inode has been patched */ diff -u --recursive --new-file v2.3.6/linux/init/main.c linux/init/main.c --- v2.3.6/linux/init/main.c Tue Jun 1 23:25:48 1999 +++ linux/init/main.c Wed Jun 16 19:26:27 1999 @@ -1139,6 +1139,7 @@ * Interrupts are still disabled. Do necessary setups, then * enable them */ + lock_kernel(); printk(linux_banner); setup_arch(&command_line, &memory_start, &memory_end); memory_start = paging_init(memory_start,memory_end); @@ -1205,6 +1206,7 @@ */ smp_init(); kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + unlock_kernel(); current->need_resched = 1; cpu_idle(NULL); } diff -u --recursive --new-file v2.3.6/linux/ipc/shm.c linux/ipc/shm.c --- v2.3.6/linux/ipc/shm.c Mon Jun 7 12:20:50 1999 +++ linux/ipc/shm.c Wed Jun 16 19:26:27 1999 @@ -675,7 +675,7 @@ done: /* pte_val(pte) == shp->shm_pages[idx] */ current->min_flt++; - atomic_inc(&mem_map[MAP_NR(pte_page(pte))].count); + get_page(mem_map + MAP_NR(pte_page(pte))); return pte_page(pte); } @@ -730,7 +730,7 @@ swap_free (swap_nr); return 0; } - if (atomic_read(&mem_map[MAP_NR(pte_page(page))].count) != 1) + if (page_count(mem_map + MAP_NR(pte_page(page))) != 1) goto check_table; shp->shm_pages[idx] = swap_nr; rw_swap_page_nocache (WRITE, swap_nr, (char *) pte_page(page)); @@ -751,7 +751,7 @@ pte = pte_mkdirty(mk_pte(page, PAGE_SHARED)); shp->shm_pages[idx] = pte_val(pte); - atomic_inc(&mem_map[MAP_NR(page)].count); + get_page(mem_map + MAP_NR(page)); shm_rss++; swap_free(entry); diff -u --recursive --new-file v2.3.6/linux/kernel/acct.c linux/kernel/acct.c --- v2.3.6/linux/kernel/acct.c Tue Mar 23 16:57:38 1999 +++ linux/kernel/acct.c Fri Jun 18 08:01:50 1999 @@ -333,10 +333,8 @@ fs = get_fs(); set_fs(KERNEL_DS); inode = file->f_dentry->d_inode; - down(&inode->i_sem); file->f_op->write(file, (char *)&ac, sizeof(struct acct), &file->f_pos); - up(&inode->i_sem); set_fs(fs); fput(file); return 0; diff -u --recursive --new-file v2.3.6/linux/kernel/ksyms.c linux/kernel/ksyms.c --- v2.3.6/linux/kernel/ksyms.c Tue Jun 8 10:47:58 1999 +++ linux/kernel/ksyms.c Sat Jun 19 12:12:07 1999 @@ -106,7 +106,6 @@ EXPORT_SYMBOL(remap_page_range); EXPORT_SYMBOL(max_mapnr); EXPORT_SYMBOL(high_memory); -EXPORT_SYMBOL(update_vm_cache); EXPORT_SYMBOL(vmtruncate); EXPORT_SYMBOL(find_vma); EXPORT_SYMBOL(get_unmapped_area); @@ -168,14 +167,12 @@ EXPORT_SYMBOL(generic_file_read); EXPORT_SYMBOL(generic_file_write); EXPORT_SYMBOL(generic_file_mmap); -EXPORT_SYMBOL(generic_readpage); EXPORT_SYMBOL(file_lock_table); EXPORT_SYMBOL(posix_lock_file); EXPORT_SYMBOL(posix_test_lock); EXPORT_SYMBOL(posix_block_lock); EXPORT_SYMBOL(posix_unblock_lock); EXPORT_SYMBOL(dput); -EXPORT_SYMBOL(get_cached_page); EXPORT_SYMBOL(put_cached_page); EXPORT_SYMBOL(is_root_busy); EXPORT_SYMBOL(prune_dcache); @@ -302,10 +299,12 @@ EXPORT_SYMBOL(enable_hlt); #endif -/* IO port handling */ -EXPORT_SYMBOL(check_region); -EXPORT_SYMBOL(request_region); -EXPORT_SYMBOL(release_region); +/* resource handling */ +EXPORT_SYMBOL(check_resource); +EXPORT_SYMBOL(request_resource); +EXPORT_SYMBOL(release_resource); +EXPORT_SYMBOL(occupy_resource); +EXPORT_SYMBOL(vacate_resource); /* process management */ EXPORT_SYMBOL(__wake_up); @@ -361,7 +360,6 @@ EXPORT_SYMBOL(__wait_on_super); EXPORT_SYMBOL(file_fsync); EXPORT_SYMBOL(clear_inode); -EXPORT_SYMBOL(refile_buffer); EXPORT_SYMBOL(nr_async_pages); EXPORT_SYMBOL(___strtok); EXPORT_SYMBOL(init_special_inode); diff -u --recursive --new-file v2.3.6/linux/kernel/resource.c linux/kernel/resource.c --- v2.3.6/linux/kernel/resource.c Thu Nov 12 09:58:32 1998 +++ linux/kernel/resource.c Fri Jun 18 12:43:41 1999 @@ -1,10 +1,17 @@ /* * linux/kernel/resource.c * - * Copyright (C) 1995 Linus Torvalds - * David Hinds + * Copyright (C) 1995, 1999 Linus Torvalds + * David Hinds * - * Kernel io-region resource management + * Kernel resource management + * + * We now distinguish between claiming space for devices (using the + * 'occupy' and 'vacate' calls), and associating a resource with a + * device driver (with the 'request', 'release', and 'check' calls). + * A resource can be claimed even if there is no associated driver + * (by occupying with name=NULL). Vacating a resource makes it + * available for other dynamically configured devices. */ #include @@ -12,47 +19,59 @@ #include #include -#define IOTABLE_SIZE 128 +#define RSRC_TABLE_SIZE 128 -typedef struct resource_entry_t { +struct resource_entry { u_long from, num; const char *name; - struct resource_entry_t *next; -} resource_entry_t; + struct resource_entry *next; +}; -static resource_entry_t iolist = { 0, 0, "", NULL }; +struct resource_entry res_list[] = { + { 0, 0, NULL, NULL }, /* IO */ + { 0, 0, NULL, NULL } /* mem */ +}; -static resource_entry_t iotable[IOTABLE_SIZE]; +static struct resource_entry rsrc_table[RSRC_TABLE_SIZE]; /* - * This generates the report for /proc/ioports + * This generates reports for /proc/ioports and /proc/memory */ -int get_ioport_list(char *buf) +int get_resource_list(int class, char *buf) { - resource_entry_t *p; + struct resource_entry *root = &res_list[class]; + struct resource_entry *p; int len = 0; - - for (p = iolist.next; (p) && (len < 4000); p = p->next) - len += sprintf(buf+len, "%04lx-%04lx : %s\n", - p->from, p->from+p->num-1, p->name); + char *fmt = (class == RES_IO) ? + "%04lx-%04lx : %s\n" : "%08lx-%08lx : %s\n"; + + for (p = root->next; (p) && (len < 4000); p = p->next) + len += sprintf(buf+len, fmt, p->from, p->from+p->num-1, + (p->name ? p->name : "occupied")); if (p) len += sprintf(buf+len, "4K limit reached!\n"); return len; } /* - * The workhorse function: find where to put a new entry + * Basics: find a matching resource entry, or find an insertion point */ -static resource_entry_t *find_gap(resource_entry_t *root, - u_long from, u_long num) +static struct resource_entry * +find_match(struct resource_entry *root, u_long from, u_long num) { - unsigned long flags; - resource_entry_t *p; - + struct resource_entry *p; + for (p = root; p; p = p->next) + if ((p->from == from) && (p->num == num)) + return p; + return NULL; +} + +static struct resource_entry * +find_gap(struct resource_entry *root, u_long from, u_long num) +{ + struct resource_entry *p; if (from > from+num-1) return NULL; - save_flags(flags); - cli(); for (p = root; ; p = p->next) { if ((p != root) && (p->from+p->num-1 >= from)) { p = NULL; @@ -61,123 +80,147 @@ if ((p->next == NULL) || (p->next->from > from+num-1)) break; } - restore_flags(flags); return p; } /* - * Call this from the device driver to register the ioport region. + * Call this from a driver to assert ownership of a resource */ -void request_region(unsigned long from, unsigned long num, const char *name) +void request_resource(int class, unsigned long from, + unsigned long num, const char *name) { - resource_entry_t *p; + struct resource_entry *root = &res_list[class]; + struct resource_entry *p; + long flags; int i; - for (i = 0; i < IOTABLE_SIZE; i++) - if (iotable[i].num == 0) + p = find_match(root, from, num); + if (p) { + p->name = name; + return; + } + + save_flags(flags); + cli(); + for (i = 0; i < RSRC_TABLE_SIZE; i++) + if (rsrc_table[i].num == 0) break; - if (i == IOTABLE_SIZE) - printk("warning: ioport table is full\n"); + if (i == RSRC_TABLE_SIZE) + printk("warning: resource table is full\n"); else { - p = find_gap(&iolist, from, num); - if (p == NULL) + p = find_gap(root, from, num); + if (p == NULL) { + restore_flags(flags); return; - iotable[i].name = name; - iotable[i].from = from; - iotable[i].num = num; - iotable[i].next = p->next; - p->next = &iotable[i]; - return; + } + rsrc_table[i].name = name; + rsrc_table[i].from = from; + rsrc_table[i].num = num; + rsrc_table[i].next = p->next; + p->next = &rsrc_table[i]; } + restore_flags(flags); } /* - * Call this when the device driver is unloaded + * Call these when a driver is unloaded but the device remains */ -void release_region(unsigned long from, unsigned long num) +void release_resource(int class, unsigned long from, unsigned long num) { - resource_entry_t *p, *q; - - for (p = &iolist; ; p = q) { - q = p->next; - if (q == NULL) - break; - if ((q->from == from) && (q->num == num)) { - q->num = 0; - p->next = q->next; - return; - } - } + struct resource_entry *root = &res_list[class]; + struct resource_entry *p; + p = find_match(root, from, num); + if (p) p->name = NULL; } /* - * Call this to check the ioport region before probing + * Call these to check a region for conflicts before probing */ -int check_region(unsigned long from, unsigned long num) +int check_resource(int class, unsigned long from, unsigned long num) { - return (find_gap(&iolist, from, num) == NULL) ? -EBUSY : 0; + struct resource_entry *root = &res_list[class]; + struct resource_entry *p; + p = find_match(root, from, num); + if (p != NULL) + return (p->name != NULL) ? -EBUSY : 0; + return (find_gap(root, from, num) == NULL) ? -EBUSY : 0; } -#ifdef __sparc__ /* Why to carry unused code on other architectures? */ /* - * This is for architectures with MMU-managed ports (sparc). + * Call this to claim a resource for a piece of hardware */ -unsigned long occupy_region(unsigned long base, unsigned long end, - unsigned long num, unsigned int align, const char *name) +unsigned long occupy_resource(int class, unsigned long base, + unsigned long end, unsigned long num, + unsigned long align, const char *name) { + struct resource_entry *root = &res_list[class]; unsigned long from = 0, till; unsigned long flags; int i; - resource_entry_t *p; /* Scanning ptr */ - resource_entry_t *p1; /* === p->next */ - resource_entry_t *s; /* Found slot */ + struct resource_entry *p, *q; - if (base > end-1) - return 0; - if (num > end - base) + if ((base > end-1) || (num > end - base)) return 0; - for (i = 0; i < IOTABLE_SIZE; i++) - if (iotable[i].num == 0) + for (i = 0; i < RSRC_TABLE_SIZE; i++) + if (rsrc_table[i].num == 0) break; - if (i == IOTABLE_SIZE) { - /* Driver prints a warning typically. */ + if (i == RSRC_TABLE_SIZE) return 0; - } save_flags(flags); cli(); /* printk("occupy: search in %08lx[%08lx] ", base, end - base); */ - s = NULL; - for (p = &iolist; p != NULL; p = p1) { - p1 = p->next; + for (p = root; p != NULL; p = q) { + q = p->next; /* Find window in list */ - from = (p->from+p->num + align-1) & ~((unsigned long)align-1); - till = (p1 == NULL)? (unsigned long) (0 - (unsigned long)align): p1->from; + from = (p->from+p->num + align-1) & ~(align-1); + till = (q == NULL) ? (0 - align) : q->from; /* printk(" %08lx:%08lx", from, till); */ /* Clip window with base and end */ if (from < base) from = base; if (till > end) till = end; /* See if result is large enougth */ - if (from < till && from + num < till) { - s = p; + if ((from < till) && (from + num < till)) break; - } } /* printk("\r\n"); */ restore_flags(flags); - if (s == NULL) + if (p == NULL) return 0; - iotable[i].name = name; - iotable[i].from = from; - iotable[i].num = num; - iotable[i].next = s->next; - s->next = &iotable[i]; + rsrc_table[i].name = name; + rsrc_table[i].from = from; + rsrc_table[i].num = num; + rsrc_table[i].next = p->next; + p->next = &rsrc_table[i]; return from; } -#endif + +/* + * Call this when a resource becomes available for other hardware + */ +void vacate_resource(int class, unsigned long from, unsigned long num) +{ + struct resource_entry *root = &res_list[class]; + struct resource_entry *p, *q; + long flags; + + save_flags(flags); + cli(); + for (p = root; ; p = q) { + q = p->next; + if (q == NULL) + break; + if ((q->from == from) && (q->num == num)) { + q->num = 0; + p->next = q->next; + break; + } + } + restore_flags(flags); +} /* Called from init/main.c to reserve IO ports. */ void __init reserve_setup(char *str, int *ints) diff -u --recursive --new-file v2.3.6/linux/kernel/sysctl.c linux/kernel/sysctl.c --- v2.3.6/linux/kernel/sysctl.c Sat Feb 6 12:22:24 1999 +++ linux/kernel/sysctl.c Sat Jun 19 11:45:29 1999 @@ -121,11 +121,14 @@ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ - proc_sys_permission + proc_sys_permission, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ }; extern struct proc_dir_entry proc_sys_root; diff -u --recursive --new-file v2.3.6/linux/mm/filemap.c linux/mm/filemap.c --- v2.3.6/linux/mm/filemap.c Wed Jun 2 13:47:49 1999 +++ linux/mm/filemap.c Sun Jun 20 16:21:41 1999 @@ -1,7 +1,7 @@ /* * linux/mm/filemap.c * - * Copyright (C) 1994, 1995 Linus Torvalds + * Copyright (C) 1994-1999 Linus Torvalds */ /* @@ -29,9 +29,12 @@ * though. * * Shared mappings now work. 15.8.1995 Bruno. + * + * finished 'unifying' the page and buffer cache and SMP-threaded the + * page-cache, 21.05.1999, Ingo Molnar */ -unsigned long page_cache_size = 0; +atomic_t page_cache_size = ATOMIC_INIT(0); struct page * page_hash_table[PAGE_HASH_SIZE]; /* @@ -50,38 +53,97 @@ static kmem_cache_t *pio_request_cache; static DECLARE_WAIT_QUEUE_HEAD(pio_wait); +spinlock_t pagecache_lock = SPIN_LOCK_UNLOCKED; + + static inline void make_pio_request(struct file *, unsigned long, unsigned long); +void __add_page_to_hash_queue(struct page * page, struct page **p){ + atomic_inc(&page_cache_size); + if((page->next_hash = *p) != NULL) + (*p)->pprev_hash = &page->next_hash; + *p = page; + page->pprev_hash = p; + if (page->buffers) + PAGE_BUG(page); +} + +static void remove_page_from_hash_queue(struct page * page) +{ + if(page->pprev_hash) { + if(page->next_hash) + page->next_hash->pprev_hash = page->pprev_hash; + *page->pprev_hash = page->next_hash; + page->pprev_hash = NULL; + } + atomic_dec(&page_cache_size); +} + +static void remove_page_from_inode_queue(struct page * page) +{ + struct inode * inode = page->inode; + struct page *prev, *next; + + inode->i_nrpages--; + next = page->next; + prev = page->prev; + if (inode->i_pages == page) + inode->i_pages = next; + if (next) + next->prev = prev; + if (prev) + prev->next = next; + page->next = NULL; + page->prev = NULL; +} /* - * Invalidate the pages of an inode, removing all pages that aren't - * locked down (those are sure to be up-to-date anyway, so we shouldn't - * invalidate them). + * Remove a page from the page cache and free it. Caller has to make + * sure the page is locked and that nobody else uses it - or that usage + * is safe. */ +void remove_inode_page(struct page *page) +{ + if (!PageLocked(page)) + PAGE_BUG(page); + + spin_lock(&pagecache_lock); + remove_page_from_inode_queue(page); + remove_page_from_hash_queue(page); + page->inode = NULL; + spin_unlock(&pagecache_lock); +} + void invalidate_inode_pages(struct inode * inode) { struct page ** p; struct page * page; +repeat: + spin_lock(&pagecache_lock); p = &inode->i_pages; while ((page = *p) != NULL) { - if (PageLocked(page)) { - p = &page->next; - continue; + get_page(page); + if (TryLockPage(page)) { + spin_unlock(&pagecache_lock); + wait_on_page(page); + page_cache_release(page); + goto repeat; } - inode->i_nrpages--; - if ((*p = page->next) != NULL) - (*p)->prev = page->prev; - page->next = NULL; - page->prev = NULL; + if (page_count(page) != 2) + printk("hm, busy page invalidated? (not necesserily a bug)\n"); + + remove_page_from_inode_queue(page); remove_page_from_hash_queue(page); page->inode = NULL; + UnlockPage(page); + page_cache_release(page); page_cache_release(page); - continue; + } + spin_unlock(&pagecache_lock); } - /* * Truncate the page cache at a set offset, removing the pages * that are beyond that offset (and zeroing out partial pages). @@ -90,55 +152,90 @@ { struct page ** p; struct page * page; + int partial = 0; repeat: + spin_lock(&pagecache_lock); p = &inode->i_pages; while ((page = *p) != NULL) { unsigned long offset = page->offset; /* page wholly truncated - free it */ if (offset >= start) { - if (PageLocked(page)) { - wait_on_page(page); - goto repeat; - } - inode->i_nrpages--; - if ((*p = page->next) != NULL) - (*p)->prev = page->prev; - page->next = NULL; - page->prev = NULL; - remove_page_from_hash_queue(page); - page->inode = NULL; + get_page(page); + spin_unlock(&pagecache_lock); + + lock_page(page); + + if (inode->i_op->flushpage) + inode->i_op->flushpage(inode, page, 0); + + /* + * We remove the page from the page cache + * _after_ we have destroyed all buffer-cache + * references to it. Otherwise some other process + * might think this inode page is not in the + * page cache and creates a buffer-cache alias + * to it causing all sorts of fun problems ... + */ + remove_inode_page(page); + + UnlockPage(page); page_cache_release(page); - continue; + page_cache_release(page); + + /* + * We have done things without the pagecache lock, + * so we'll have to repeat the scan. + * It's not possible to deadlock here because + * we are guaranteed to make progress. (ie. we have + * just removed a page) + */ + goto repeat; } p = &page->next; + /* + * there is only one partial page possible. + */ + if (partial) + continue; + offset = start - offset; /* partial truncate, clear end of page */ if (offset < PAGE_CACHE_SIZE) { - unsigned long address = page_address(page); + unsigned long address; + get_page(page); + spin_unlock(&pagecache_lock); + + lock_page(page); + partial = 1; + + address = page_address(page); memset((void *) (offset + address), 0, PAGE_CACHE_SIZE - offset); flush_page_to_ram(address); + + if (inode->i_op->flushpage) + inode->i_op->flushpage(inode, page, offset); + /* + * we have dropped the spinlock so we have to + * restart. + */ + UnlockPage(page); + page_cache_release(page); + goto repeat; } } + spin_unlock(&pagecache_lock); } -/* - * Remove a page from the page cache and free it. - */ -void remove_inode_page(struct page *page) -{ - remove_page_from_hash_queue(page); - remove_page_from_inode_queue(page); - page_cache_release(page); -} +extern atomic_t too_many_dirty_buffers; int shrink_mmap(int priority, int gfp_mask) { static unsigned long clock = 0; unsigned long limit = num_physpages; struct page * page; - int count; + int count, users; count = limit >> priority; @@ -164,15 +261,67 @@ referenced = test_and_clear_bit(PG_referenced, &page->flags); - if (PageLocked(page)) + if ((gfp_mask & __GFP_DMA) && !PageDMA(page)) continue; - if ((gfp_mask & __GFP_DMA) && !PageDMA(page)) + /* + * Some common cases that we just short-circuit without + * getting the locks - we need to re-check this once we + * have the lock, but that's fine. + */ + users = page_count(page); + if (!users) continue; + if (!page->buffers) { + if (!page->inode) + continue; + if (users > 1) + continue; + } - /* We can't free pages unless there's just one user */ - if (atomic_read(&page->count) != 1) + /* + * ok, now the page looks interesting. Re-check things + * and keep the lock. + */ + spin_lock(&pagecache_lock); + if (!page->inode && !page->buffers) { + spin_unlock(&pagecache_lock); continue; + } + if (!page_count(page)) { +// BUG(); + spin_unlock(&pagecache_lock); + continue; + } + get_page(page); + if (TryLockPage(page)) { + spin_unlock(&pagecache_lock); + goto put_continue; + } + + /* + * we keep pagecache_lock locked and unlock it in + * each branch, so that the page->inode case doesnt + * have to re-grab it. Here comes the 'real' logic + * to free memory: + */ + + /* Is it a buffer page? */ + if (page->buffers) { + kdev_t dev = page->buffers->b_dev; + spin_unlock(&pagecache_lock); + if (try_to_free_buffers(page)) + goto made_progress; + if (!atomic_read(&too_many_dirty_buffers)) { + atomic_set(&too_many_dirty_buffers, 1); + balance_dirty(dev); + } + goto unlock_continue; + } + + /* We can't free pages unless there's just one user */ + if (page_count(page) != 2) + goto spin_unlock_continue; count--; @@ -182,77 +331,180 @@ * were to be marked referenced.. */ if (PageSwapCache(page)) { - if (referenced && swap_count(page->offset) != 1) - continue; - delete_from_swap_cache(page); - return 1; + spin_unlock(&pagecache_lock); + if (referenced && swap_count(page->offset) != 2) + goto unlock_continue; + __delete_from_swap_cache(page); + page_cache_release(page); + goto made_progress; } - if (referenced) - continue; - - /* Is it a buffer page? */ - if (page->buffers) { - if (buffer_under_min()) - continue; - if (!try_to_free_buffers(page)) - continue; - return 1; - } - /* is it a page-cache page? */ - if (page->inode) { - if (pgcache_under_min()) - continue; - remove_inode_page(page); - return 1; - } + if (!referenced && page->inode && !pgcache_under_min()) { + remove_page_from_inode_queue(page); + remove_page_from_hash_queue(page); + page->inode = NULL; + spin_unlock(&pagecache_lock); + page_cache_release(page); + goto made_progress; + } +spin_unlock_continue: + spin_unlock(&pagecache_lock); +unlock_continue: + UnlockPage(page); +put_continue: + put_page(page); } while (count > 0); return 0; +made_progress: + UnlockPage(page); + put_page(page); + return 1; +} + +static inline struct page * __find_page_nolock(struct inode * inode, unsigned long offset, struct page *page) +{ + goto inside; + + for (;;) { + page = page->next_hash; +inside: + if (!page) + goto not_found; + if (page->inode != inode) + continue; + if (page->offset == offset) + break; + } +not_found: + return page; } /* - * Update a page cache copy, when we're doing a "write()" system call - * See also "update_vm_cache()". + * By the time this is called, the page is locked and + * we don't have to worry about any races any more. + * + * Start the IO.. */ -void update_vm_cache(struct inode * inode, unsigned long pos, const char * buf, int count) +static int writeout_one_page(struct page *page) { - unsigned long offset, len; + struct buffer_head *bh, *head = page->buffers; - offset = (pos & ~PAGE_CACHE_MASK); - pos = pos & PAGE_CACHE_MASK; - len = PAGE_CACHE_SIZE - offset; + bh = head; do { - struct page * page; + if (buffer_locked(bh) || !buffer_dirty(bh) || !buffer_uptodate(bh)) + continue; - if (len > count) - len = count; - page = find_page(inode, pos); - if (page) { - wait_on_page(page); - memcpy((void *) (offset + page_address(page)), buf, len); - page_cache_release(page); - } - count -= len; - buf += len; - len = PAGE_CACHE_SIZE; - offset = 0; - pos += PAGE_CACHE_SIZE; - } while (count); + bh->b_flushtime = 0; + ll_rw_block(WRITE, 1, &bh); + } while ((bh = bh->b_this_page) != head); + return 0; +} + +static int waitfor_one_page(struct page *page) +{ + int error = 0; + struct buffer_head *bh, *head = page->buffers; + + bh = head; + do { + wait_on_buffer(bh); + if (buffer_req(bh) && !buffer_uptodate(bh)) + error = -EIO; + } while ((bh = bh->b_this_page) != head); + return error; +} + +static int do_buffer_fdatasync(struct inode *inode, unsigned long start, unsigned long end, int (*fn)(struct page *)) +{ + struct page *next; + int retval = 0; + + start &= PAGE_MASK; + + spin_lock(&pagecache_lock); + next = inode->i_pages; + while (next) { + struct page *page = next; + next = page->next; + if (!page->buffers) + continue; + if (page->offset >= end) + continue; + if (page->offset < start) + continue; + + get_page(page); + spin_unlock(&pagecache_lock); + lock_page(page); + + /* The buffers could have been free'd while we waited for the page lock */ + if (page->buffers) + retval |= fn(page); + + UnlockPage(page); + spin_lock(&pagecache_lock); + next = page->next; + page_cache_release(page); + } + spin_unlock(&pagecache_lock); + + return retval; +} + +/* + * Two-stage data sync: first start the IO, then go back and + * collect the information.. + */ +int generic_buffer_fdatasync(struct inode *inode, unsigned long start, unsigned long end) +{ + int retval; + + retval = do_buffer_fdatasync(inode, start, end, writeout_one_page); + retval |= do_buffer_fdatasync(inode, start, end, waitfor_one_page); + return retval; } -static inline void add_to_page_cache(struct page * page, +/* + * This adds a page to the page cache, starting out as locked, + * owned by us, referenced, but not uptodate and with no errors. + */ +static inline void __add_to_page_cache(struct page * page, struct inode * inode, unsigned long offset, struct page **hash) { - atomic_inc(&page->count); - page->flags = (page->flags & ~((1 << PG_uptodate) | (1 << PG_error))) | (1 << PG_referenced); + unsigned long flags; + + flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error)); + page->flags = flags | ((1 << PG_locked) | (1 << PG_referenced)); + page->owner = (int)current; /* REMOVEME */ + get_page(page); page->offset = offset; add_page_to_inode_queue(inode, page); __add_page_to_hash_queue(page, hash); } +int add_to_page_cache_unique(struct page * page, + struct inode * inode, unsigned long offset, + struct page **hash) +{ + int err; + struct page *alias; + + spin_lock(&pagecache_lock); + alias = __find_page_nolock(inode, offset, *hash); + + err = 1; + if (!alias) { + __add_to_page_cache(page,inode,offset,hash); + err = 0; + } + + spin_unlock(&pagecache_lock); + return err; +} + /* * Try to read ahead in the file. "page_cache" is a potentially free page * that we could use for the cache (if it is 0 we can try to create one, @@ -275,45 +527,173 @@ if (offset >= inode->i_size) break; hash = page_hash(inode, offset); - page = __find_page(inode, offset, *hash); - if (!page) { + page = page_cache_entry(page_cache); + if (!add_to_page_cache_unique(page, inode, offset, hash)) { /* - * Ok, add the new page to the hash-queues... + * We do not have to check the return value here + * because it's a readahead. */ - page = page_cache_entry(page_cache); - add_to_page_cache(page, inode, offset, hash); inode->i_op->readpage(file, page); page_cache = 0; + page_cache_release(page); } - page_cache_release(page); } return page_cache; } /* - * Wait for IO to complete on a locked page. + * Wait for a page to get unlocked. * * This must be called with the caller "holding" the page, * ie with increased "page->count" so that the page won't * go away during the wait.. */ -void __wait_on_page(struct page *page) +void ___wait_on_page(struct page *page) { struct task_struct *tsk = current; DECLARE_WAITQUEUE(wait, tsk); add_wait_queue(&page->wait, &wait); -repeat: - tsk->state = TASK_UNINTERRUPTIBLE; - run_task_queue(&tq_disk); - if (PageLocked(page)) { + do { + tsk->state = TASK_UNINTERRUPTIBLE; + run_task_queue(&tq_disk); + if (!PageLocked(page)) + break; schedule(); - goto repeat; - } + } while (PageLocked(page)); tsk->state = TASK_RUNNING; remove_wait_queue(&page->wait, &wait); } +/* + * Get an exclusive lock on the page.. + */ +void lock_page(struct page *page) +{ + if (TryLockPage(page)) { + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, current); + + run_task_queue(&tq_disk); + add_wait_queue(&page->wait, &wait); + tsk->state = TASK_UNINTERRUPTIBLE; + + while (TryLockPage(page)) { + run_task_queue(&tq_disk); + schedule(); + tsk->state = TASK_UNINTERRUPTIBLE; + } + + remove_wait_queue(&page->wait, &wait); + tsk->state = TASK_RUNNING; + } +} + + +/* + * a rather lightweight function, finding and getting a reference to a + * hashed page atomically, waiting for it if it's locked. + */ +struct page * __find_get_page (struct inode * inode, + unsigned long offset, struct page **hash) +{ + struct page *page; + + /* + * We scan the hash list read-only. Addition to and removal from + * the hash-list needs a held write-lock. + */ +repeat: + spin_lock(&pagecache_lock); + page = __find_page_nolock(inode, offset, *hash); + if (page) + get_page(page); + spin_unlock(&pagecache_lock); + + /* Found the page, sleep if locked. */ + if (page && PageLocked(page)) { + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue(&page->wait, &wait); + tsk->state = TASK_UNINTERRUPTIBLE; + + run_task_queue(&tq_disk); + if (PageLocked(page)) + schedule(); + tsk->state = TASK_RUNNING; + remove_wait_queue(&page->wait, &wait); + + /* + * The page might have been unhashed meanwhile. It's + * not freed though because we hold a reference to it. + * If this is the case then it will be freed _here_, + * and we recheck the hash anyway. + */ + page_cache_release(page); + goto repeat; + } + /* + * It's not locked so we can return the page and we hold + * a reference to it. + */ + return page; +} + +/* + * Get the lock to a page atomically. + */ +struct page * __find_lock_page (struct inode * inode, + unsigned long offset, struct page **hash) +{ + int locked; + struct page *page; + + /* + * We scan the hash list read-only. Addition to and removal from + * the hash-list needs a held write-lock. + */ +repeat: + spin_lock(&pagecache_lock); + page = __find_page_nolock(inode, offset, *hash); + locked = 0; + if (page) { + get_page(page); + if (TryLockPage(page)) + locked = 1; + } + spin_unlock(&pagecache_lock); + + /* Found the page, sleep if locked. */ + if (page && locked) { + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue(&page->wait, &wait); + tsk->state = TASK_UNINTERRUPTIBLE; + + run_task_queue(&tq_disk); + if (PageLocked(page)) + schedule(); + tsk->state = TASK_RUNNING; + remove_wait_queue(&page->wait, &wait); + + /* + * The page might have been unhashed meanwhile. It's + * not freed though because we hold a reference to it. + * If this is the case then it will be freed _here_, + * and we recheck the hash anyway. + */ + page_cache_release(page); + goto repeat; + } + /* + * It's not locked so we can return the page and we hold + * a reference to it. + */ + return page; +} + #if 0 #define PROFILE_READAHEAD #define DEBUG_READAHEAD @@ -386,14 +766,14 @@ * ------------------- * The read ahead context fields of the "struct file" are the following: * - f_raend : position of the first byte after the last page we tried to - * read ahead. + * read ahead. * - f_ramax : current read-ahead maximum size. * - f_ralen : length of the current IO read block we tried to read-ahead. * - f_rawin : length of the current read-ahead window. - * if last read-ahead was synchronous then - * f_rawin = f_ralen - * otherwise (was asynchronous) - * f_rawin = previous value of f_ralen + f_ralen + * if last read-ahead was synchronous then + * f_rawin = f_ralen + * otherwise (was asynchronous) + * f_rawin = previous value of f_ralen + f_ralen * * Read-ahead limits: * ------------------ @@ -485,7 +865,7 @@ * We will later force unplug device in order to force asynchronous read IO. */ else if (reada_ok && filp->f_ramax && raend >= PAGE_CACHE_SIZE && - ppos <= raend && ppos + filp->f_ralen >= raend) { + ppos <= raend && ppos + filp->f_ralen >= raend) { /* * Add ONE page to max_ahead in order to try to have about the same IO max size * as synchronous read-ahead (MAX_READAHEAD + 1)*PAGE_CACHE_SIZE. @@ -578,6 +958,7 @@ struct inode *inode = dentry->d_inode; size_t pos, pgpos, page_cache; int reada_ok; + int error; int max_readahead = get_max_readahead(inode); page_cache = 0; @@ -633,33 +1014,22 @@ * Try to find the data in the page cache.. */ hash = page_hash(inode, pos & PAGE_CACHE_MASK); - page = __find_page(inode, pos & PAGE_CACHE_MASK, *hash); + + spin_lock(&pagecache_lock); + page = __find_page_nolock(inode, pos & PAGE_CACHE_MASK, *hash); if (!page) goto no_cached_page; - found_page: -/* - * Try to read ahead only if the current page is filled or being filled. - * Otherwise, if we were reading ahead, decrease max read ahead size to - * the minimum value. - * In this context, that seems to may happen only on some read error or if - * the page has been rewritten. - */ - if (PageUptodate(page) || PageLocked(page)) - page_cache = generic_file_readahead(reada_ok, filp, inode, pos & PAGE_CACHE_MASK, page, page_cache); - else if (reada_ok && filp->f_ramax > MIN_READAHEAD) - filp->f_ramax = MIN_READAHEAD; + get_page(page); + spin_unlock(&pagecache_lock); - wait_on_page(page); - - if (!PageUptodate(page)) - goto page_read_error; - -success: - /* - * Ok, we have the page, it's up-to-date and ok, - * so now we can finally copy it to user space... - */ + if (!Page_Uptodate(page)) + goto page_not_up_to_date; +page_ok: + /* + * Ok, we have the page, and it's up-to-date, so + * now we can copy it to user space... + */ { unsigned long offset, nr; @@ -683,75 +1053,77 @@ break; } +/* + * Ok, the page was not immediately readable, so let's try to read ahead while we're at it.. + */ +page_not_up_to_date: + page_cache = generic_file_readahead(reada_ok, filp, inode, pos & PAGE_CACHE_MASK, page, page_cache); + + if (Page_Uptodate(page)) + goto page_ok; + + /* Get exclusive access to the page ... */ + lock_page(page); + if (Page_Uptodate(page)) { + UnlockPage(page); + goto page_ok; + } + +readpage: + /* ... and start the actual read. The read will unlock the page. */ + error = inode->i_op->readpage(filp, page); + + if (!error) { + if (Page_Uptodate(page)) + goto page_ok; + + /* Again, try some read-ahead while waiting for the page to finish.. */ + page_cache = generic_file_readahead(reada_ok, filp, inode, pos & PAGE_CACHE_MASK, page, page_cache); + wait_on_page(page); + if (Page_Uptodate(page)) + goto page_ok; + error = -EIO; + } + + /* UHHUH! A synchronous read error occurred. Report it */ + desc->error = error; + page_cache_release(page); + break; + no_cached_page: /* * Ok, it wasn't cached, so we need to create a new * page.. + * + * We get here with the page cache lock held. */ if (!page_cache) { + spin_unlock(&pagecache_lock); page_cache = page_cache_alloc(); + if (!page_cache) { + desc->error = -ENOMEM; + break; + } + /* - * That could have slept, so go around to the - * very beginning.. + * Somebody may have added the page while we + * dropped the page cache lock. Check for that. */ - if (page_cache) - continue; - desc->error = -ENOMEM; - break; + spin_lock(&pagecache_lock); + page = __find_page_nolock(inode, pos & PAGE_CACHE_MASK, *hash); + if (page) + goto found_page; } /* * Ok, add the new page to the hash-queues... */ page = page_cache_entry(page_cache); - page_cache = 0; - add_to_page_cache(page, inode, pos & PAGE_CACHE_MASK, hash); - - /* - * Error handling is tricky. If we get a read error, - * the cached page stays in the cache (but uptodate=0), - * and the next process that accesses it will try to - * re-read it. This is needed for NFS etc, where the - * identity of the reader can decide if we can read the - * page or not.. - */ -/* - * We have to read the page. - * If we were reading ahead, we had previously tried to read this page, - * That means that the page has probably been removed from the cache before - * the application process needs it, or has been rewritten. - * Decrease max readahead size to the minimum value in that situation. - */ - if (reada_ok && filp->f_ramax > MIN_READAHEAD) - filp->f_ramax = MIN_READAHEAD; - - { - int error = inode->i_op->readpage(filp, page); - if (!error) - goto found_page; - desc->error = error; - page_cache_release(page); - break; - } + __add_to_page_cache(page, inode, pos & PAGE_CACHE_MASK, hash); + spin_unlock(&pagecache_lock); -page_read_error: - /* - * We found the page, but it wasn't up-to-date. - * Try to re-read it _once_. We do this synchronously, - * because this happens only if there were errors. - */ - { - int error = inode->i_op->readpage(filp, page); - if (!error) { - wait_on_page(page); - if (PageUptodate(page) && !PageError(page)) - goto success; - error = -EIO; /* Some unspecified error occurred.. */ - } - desc->error = error; - page_cache_release(page); - break; - } + page_cache = 0; + goto readpage; } *ppos = pos; @@ -787,6 +1159,7 @@ { ssize_t retval; + unlock_kernel(); retval = -EFAULT; if (access_ok(VERIFY_WRITE, buf, count)) { retval = 0; @@ -804,6 +1177,7 @@ retval = desc.error; } } + lock_kernel(); return retval; } @@ -812,17 +1186,14 @@ ssize_t written; unsigned long count = desc->count; struct file *file = (struct file *) desc->buf; - struct inode *inode = file->f_dentry->d_inode; mm_segment_t old_fs; if (size > count) size = count; - down(&inode->i_sem); old_fs = get_fs(); set_fs(KERNEL_DS); written = file->f_op->write(file, area, size, &file->f_pos); set_fs(old_fs); - up(&inode->i_sem); if (written < 0) { desc->error = written; written = 0; @@ -878,6 +1249,7 @@ if (retval) goto fput_out; + unlock_kernel(); retval = 0; if (count) { read_descriptor_t desc; @@ -887,7 +1259,7 @@ ppos = &in_file->f_pos; if (offset) { if (get_user(pos, offset)) - goto fput_out; + goto fput_out_lock; ppos = &pos; } @@ -904,7 +1276,8 @@ put_user(pos, offset); } - +fput_out_lock: + lock_kernel(); fput_out: fput(out_file); fput_in: @@ -934,17 +1307,21 @@ unsigned long offset, reada, i; struct page * page, **hash; unsigned long old_page, new_page; + int error; new_page = 0; offset = (address & PAGE_MASK) - area->vm_start + area->vm_offset; if (offset >= inode->i_size && (area->vm_flags & VM_SHARED) && area->vm_mm == current->mm) - goto no_page; + goto no_page_nolock; + + unlock_kernel(); /* * Do we have something in the page cache already? */ hash = page_hash(inode, offset); - page = __find_page(inode, offset, *hash); +retry_find: + page = __find_get_page(inode, offset, hash); if (!page) goto no_cached_page; @@ -960,15 +1337,17 @@ goto failure; } - if (PageLocked(page)) - goto page_locked_wait; - if (!PageUptodate(page)) - goto page_read_error; + if (!Page_Uptodate(page)) { + lock_page(page); + if (!Page_Uptodate(page)) + goto page_not_uptodate; + UnlockPage(page); + } success: /* - * Found the page, need to check sharing and possibly - * copy it over to another page.. + * Found the page and have a reference on it, need to check sharing + * and possibly copy it over to another page.. */ old_page = page_address(page); if (!no_share) { @@ -980,6 +1359,7 @@ page_cache_free(new_page); flush_page_to_ram(old_page); + lock_kernel(); return old_page; } @@ -989,6 +1369,7 @@ copy_page(new_page, old_page); flush_page_to_ram(new_page); page_cache_release(page); + lock_kernel(); return new_page; no_cached_page: @@ -1013,7 +1394,7 @@ * cache.. The page we just got may be useful if we * can't share, so don't get rid of it here. */ - page = find_page(inode, offset); + page = __find_get_page(inode, offset, hash); if (page) goto found_page; @@ -1021,19 +1402,24 @@ * Now, create a new page-cache page from the page we got */ page = page_cache_entry(new_page); - new_page = 0; - add_to_page_cache(page, inode, offset, hash); + if (add_to_page_cache_unique(page, inode, offset, hash)) + goto retry_find; - if (inode->i_op->readpage(file, page) != 0) - goto failure; + /* + * Now it's ours and locked, we can do initial IO to it: + */ + new_page = 0; - goto found_page; +page_not_uptodate: + error = inode->i_op->readpage(file, page); -page_locked_wait: - __wait_on_page(page); - if (PageUptodate(page)) + if (!error) { + wait_on_page(page); + if (PageError(page)) + goto page_read_error; goto success; - + } + page_read_error: /* * Umm, take care of errors if the page isn't up-to-date. @@ -1041,12 +1427,14 @@ * because there really aren't any performance issues here * and we need to check for errors. */ - if (inode->i_op->readpage(file, page) != 0) + if (!PageLocked(page)) + PAGE_BUG(page); + ClearPageError(page); + error = inode->i_op->readpage(file, page); + if (error) goto failure; wait_on_page(page); - if (PageError(page)) - goto failure; - if (PageUptodate(page)) + if (Page_Uptodate(page)) goto success; /* @@ -1058,6 +1446,8 @@ if (new_page) page_cache_free(new_page); no_page: + lock_kernel(); +no_page_nolock: return 0; } @@ -1066,12 +1456,13 @@ * if the disk is full. */ static inline int do_write_page(struct inode * inode, struct file * file, - const char * page, unsigned long offset) + const char * page_addr, unsigned long offset) { int retval; unsigned long size; loff_t loff = offset; - mm_segment_t old_fs; + int (*writepage) (struct file *, struct page *); + struct page * page; size = offset + PAGE_SIZE; /* refuse to extend file size.. */ @@ -1083,12 +1474,21 @@ return -EIO; } size -= offset; - old_fs = get_fs(); - set_fs(KERNEL_DS); retval = -EIO; - if (size == file->f_op->write(file, (const char *) page, size, &loff)) - retval = 0; - set_fs(old_fs); + writepage = inode->i_op->writepage; + page = mem_map + MAP_NR(page_addr); + lock_page(page); + + if (writepage) { + retval = writepage(file, page); + } else { + mm_segment_t old_fs = get_fs(); + set_fs(KERNEL_DS); + if (size == file->f_op->write(file, page_addr, size, &loff)) + retval = 0; + set_fs(old_fs); + } + UnlockPage(page); return retval; } @@ -1124,9 +1524,7 @@ return 0; } - down(&inode->i_sem); result = do_write_page(inode, file, (const char *) page, offset); - up(&inode->i_sem); fput(file); return result; } @@ -1146,7 +1544,8 @@ unsigned long address, unsigned int flags) { pte_t pte = *ptep; - unsigned long page; + unsigned long pageaddr; + struct page *page; int error; if (!(flags & MS_INVALIDATE)) { @@ -1158,8 +1557,9 @@ flush_cache_page(vma, address); set_pte(ptep, pte_mkclean(pte)); flush_tlb_page(vma, address); - page = pte_page(pte); - atomic_inc(&page_cache_entry(page)->count); + pageaddr = pte_page(pte); + page = page_cache_entry(pageaddr); + get_page(page); } else { if (pte_none(pte)) return 0; @@ -1170,14 +1570,14 @@ swap_free(pte_val(pte)); return 0; } - page = pte_page(pte); + pageaddr = pte_page(pte); if (!pte_dirty(pte) || flags == MS_INVALIDATE) { - page_cache_free(page); + page_cache_free(pageaddr); return 0; } } - error = filemap_write_page(vma, address - vma->vm_start + vma->vm_offset, page, 1); - page_cache_free(page); + error = filemap_write_page(vma, address - vma->vm_start + vma->vm_offset, pageaddr, 1); + page_cache_free(pageaddr); return error; } @@ -1338,10 +1738,7 @@ struct file * file = vma->vm_file; if (file) { struct dentry * dentry = file->f_dentry; - struct inode * inode = dentry->d_inode; - down(&inode->i_sem); error = file_fsync(file, dentry); - up(&inode->i_sem); } } return error; @@ -1436,11 +1833,12 @@ unsigned long page_cache = 0; unsigned long written; long status; + int err; - if (file->f_error) { - int error = file->f_error; + err = file->f_error; + if (err) { file->f_error = 0; - return error; + goto out; } written = 0; @@ -1451,7 +1849,7 @@ /* * Check whether we've reached the file size limit. */ - status = -EFBIG; + err = -EFBIG; if (pos >= limit) { send_sig(SIGXFSZ, current, 0); goto out; @@ -1467,6 +1865,8 @@ count = limit - pos; } + unlock_kernel(); + while (count) { unsigned long bytes, pgpos, offset; /* @@ -1480,29 +1880,36 @@ bytes = count; hash = page_hash(inode, pgpos); - page = __find_page(inode, pgpos, *hash); +repeat_find: + page = __find_lock_page(inode, pgpos, hash); if (!page) { if (!page_cache) { page_cache = page_cache_alloc(); if (page_cache) - continue; + goto repeat_find; status = -ENOMEM; break; } page = page_cache_entry(page_cache); - add_to_page_cache(page, inode, pgpos, hash); + if (add_to_page_cache_unique(page,inode,pgpos,hash)) + goto repeat_find; + page_cache = 0; } - /* Get exclusive IO access to the page.. */ - wait_on_page(page); - set_bit(PG_locked, &page->flags); + /* We have exclusive IO access to the page.. */ + if (!PageLocked(page)) { + PAGE_BUG(page); + } else { + if (page->owner != (int)current) { + PAGE_BUG(page); + } + } status = write_one_page(file, page, offset, bytes, buf); /* Mark it unlocked again and drop the page.. */ - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + UnlockPage(page); page_cache_release(page); if (status < 0) @@ -1519,51 +1926,16 @@ if (page_cache) page_cache_free(page_cache); + + err = written ? written : status; + lock_kernel(); out: - return written ? written : status; + return err; } /* - * Support routines for directory cacheing using the page cache. - */ - -/* - * Finds the page at the specified offset, installing a new page - * if requested. The count is incremented and the page is locked. - * - * Note: we don't have to worry about races here, as the caller - * is holding the inode semaphore. + * Support routines for directory caching using the page cache. */ -unsigned long get_cached_page(struct inode * inode, unsigned long offset, - int new) -{ - struct page * page; - struct page ** hash; - unsigned long page_cache = 0; - - hash = page_hash(inode, offset); - page = __find_page(inode, offset, *hash); - if (!page) { - if (!new) - goto out; - page_cache = page_cache_alloc(); - if (!page_cache) - goto out; - clear_page(page_cache); - page = page_cache_entry(page_cache); - add_to_page_cache(page, inode, offset, hash); - } - if (atomic_read(&page->count) != 2) - printk(KERN_ERR "get_cached_page: page count=%d\n", - atomic_read(&page->count)); - if (test_bit(PG_locked, &page->flags)) - printk(KERN_ERR "get_cached_page: page already locked!\n"); - set_bit(PG_locked, &page->flags); - page_cache = page_address(page); - -out: - return page_cache; -} /* * Unlock and free a page. @@ -1572,13 +1944,10 @@ { struct page * page = page_cache_entry(addr); - if (!test_bit(PG_locked, &page->flags)) - printk("put_cached_page: page not locked!\n"); - if (atomic_read(&page->count) != 2) - printk("put_cached_page: page count=%d\n", - atomic_read(&page->count)); - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + UnlockPage(page); + if (page_count(page) != 2) + panic("put_cached_page: page count=%d\n", + page_count(page)); page_cache_release(page); } @@ -1607,11 +1976,13 @@ static inline void make_pio_request(struct file *file, unsigned long offset, - unsigned long page) + unsigned long pageaddr) { struct pio_request *p; + struct page *page; - atomic_inc(&page_cache_entry(page)->count); + page = page_cache_entry(pageaddr); + get_page(page); /* * We need to allocate without causing any recursive IO in the @@ -1634,7 +2005,7 @@ p->file = file; p->offset = offset; - p->page = page; + p->page = pageaddr; put_pio_request(p); wake_up(&pio_wait); @@ -1694,10 +2065,8 @@ dentry = p->file->f_dentry; inode = dentry->d_inode; - down(&inode->i_sem); do_write_page(inode, p->file, (const char *) p->page, p->offset); - up(&inode->i_sem); fput(p->file); page_cache_free(p->page); kmem_cache_free(pio_request_cache, p); diff -u --recursive --new-file v2.3.6/linux/mm/memory.c linux/mm/memory.c --- v2.3.6/linux/mm/memory.c Tue Jun 8 14:09:57 1999 +++ linux/mm/memory.c Wed Jun 16 19:26:27 1999 @@ -272,7 +272,7 @@ if (vma->vm_flags & VM_SHARED) pte = pte_mkclean(pte); set_pte(dst_pte, pte_mkold(pte)); - atomic_inc(&mem_map[page_nr].count); + get_page(mem_map + page_nr); cont_copy_pte_range: address += PAGE_SIZE; if (address >= end) @@ -554,7 +554,7 @@ if (MAP_NR(page) >= max_mapnr) printk("put_dirty_page: trying to put page %08lx at %08lx\n",page,address); - if (atomic_read(&mem_map[MAP_NR(page)].count) != 1) + if (page_count(mem_map + MAP_NR(page)) != 1) printk("mem_map disagrees with %08lx at %08lx\n",page,address); pgd = pgd_offset(tsk->mm,address); pmd = pmd_alloc(pgd, address); @@ -602,17 +602,17 @@ unsigned long address, pte_t *page_table, pte_t pte) { unsigned long old_page, new_page; - struct page * page_map; + struct page * page; new_page = __get_free_page(GFP_USER); - /* Did swap_out() unmapped the protected page while we slept? */ + /* Did swap_out() unmap the protected page while we slept? */ if (pte_val(*page_table) != pte_val(pte)) goto end_wp_page; old_page = pte_page(pte); if (MAP_NR(old_page) >= max_mapnr) goto bad_wp_page; tsk->min_flt++; - page_map = mem_map + MAP_NR(old_page); + page = mem_map + MAP_NR(old_page); /* * We can avoid the copy if: @@ -622,13 +622,13 @@ * in which case we can remove the page * from the swap cache. */ - switch (atomic_read(&page_map->count)) { + switch (page_count(page)) { case 2: - if (!PageSwapCache(page_map)) + if (!PageSwapCache(page)) break; - if (swap_count(page_map->offset) != 1) + if (swap_count(page->offset) != 1) break; - delete_from_swap_cache(page_map); + delete_from_swap_cache(page); /* FallThrough */ case 1: flush_cache_page(vma, address); @@ -650,7 +650,7 @@ if (!new_page) goto no_new_page; - if (PageReserved(page_map)) + if (PageReserved(page)) ++vma->vm_mm->rss; copy_cow_page(old_page,new_page); flush_page_to_ram(old_page); @@ -659,7 +659,7 @@ set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot)))); flush_tlb_page(vma, address); unlock_kernel(); - __free_page(page_map); + __free_page(page); return 1; bad_wp_page: @@ -774,7 +774,7 @@ if (pte_val(*page_table) != pte_val(entry)) { free_page(pte_page(page)); } else { - if (atomic_read(&mem_map[MAP_NR(pte_page(page))].count) > 1 && + if (page_count(mem_map + MAP_NR(pte_page(page))) > 1 && !(vma->vm_flags & VM_SHARED)) page = pte_wrprotect(page); ++vma->vm_mm->rss; @@ -858,7 +858,7 @@ entry = mk_pte(page, vma->vm_page_prot); if (write_access) { entry = pte_mkwrite(pte_mkdirty(entry)); - } else if (atomic_read(&mem_map[MAP_NR(page)].count) > 1 && + } else if (page_count(mem_map+MAP_NR(page)) > 1 && !(vma->vm_flags & VM_SHARED)) entry = pte_wrprotect(entry); set_pte(page_table, entry); diff -u --recursive --new-file v2.3.6/linux/mm/mmap.c linux/mm/mmap.c --- v2.3.6/linux/mm/mmap.c Mon Jun 7 11:15:33 1999 +++ linux/mm/mmap.c Wed Jun 16 19:26:27 1999 @@ -63,7 +63,7 @@ return 1; free = buffermem >> PAGE_SHIFT; - free += page_cache_size; + free += atomic_read(&page_cache_size); free += nr_free_pages; free += nr_swap_pages; free -= (page_cache.min_percent + buffer_mem.min_percent + 2)*num_physpages/100; @@ -727,6 +727,10 @@ struct mm_struct * mm = current->mm; struct vm_area_struct * vma; unsigned long flags, retval; + + len = PAGE_ALIGN(len); + if (!len) + return addr; /* * mlock MCL_FUTURE? diff -u --recursive --new-file v2.3.6/linux/mm/page_alloc.c linux/mm/page_alloc.c --- v2.3.6/linux/mm/page_alloc.c Tue May 11 14:37:40 1999 +++ linux/mm/page_alloc.c Sun Jun 20 16:00:24 1999 @@ -119,33 +119,33 @@ spin_unlock_irqrestore(&page_alloc_lock, flags); } -void __free_page(struct page *page) +int __free_page(struct page *page) { - if (!PageReserved(page) && atomic_dec_and_test(&page->count)) { + if (!PageReserved(page) && put_page_testzero(page)) { if (PageSwapCache(page)) - panic ("Freeing swap cache page"); + PAGE_BUG(page); page->flags &= ~(1 << PG_referenced); free_pages_ok(page - mem_map, 0); - return; + return 1; } + return 0; } -void free_pages(unsigned long addr, unsigned long order) +int free_pages(unsigned long addr, unsigned long order) { unsigned long map_nr = MAP_NR(addr); if (map_nr < max_mapnr) { mem_map_t * map = mem_map + map_nr; - if (PageReserved(map)) - return; - if (atomic_dec_and_test(&map->count)) { + if (!PageReserved(map) && put_page_testzero(map)) { if (PageSwapCache(map)) - panic ("Freeing swap cache pages"); + PAGE_BUG(map); map->flags &= ~(1 << PG_referenced); free_pages_ok(map_nr, order); - return; + return 1; } } + return 0; } /* @@ -167,7 +167,7 @@ MARK_USED(map_nr, new_order, area); \ nr_free_pages -= 1 << order; \ EXPAND(ret, map_nr, order, new_order, area); \ - spin_unlock_irqrestore(&page_alloc_lock, flags); \ + spin_unlock_irqrestore(&page_alloc_lock,flags);\ return ADDRESS(map_nr); \ } \ prev = ret; \ @@ -186,7 +186,7 @@ index += size; \ map += size; \ } \ - atomic_set(&map->count, 1); \ + set_page_count(map, 1); \ } while (0) int low_on_memory = 0; @@ -321,7 +321,7 @@ memset(mem_map, 0, start_mem - (unsigned long) mem_map); do { --p; - atomic_set(&p->count, 0); + set_page_count(p, 0); p->flags = (1 << PG_DMA) | (1 << PG_reserved); init_waitqueue_head(&p->wait); } while (p > mem_map); diff -u --recursive --new-file v2.3.6/linux/mm/page_io.c linux/mm/page_io.c --- v2.3.6/linux/mm/page_io.c Tue May 11 14:37:40 1999 +++ linux/mm/page_io.c Wed Jun 16 19:26:27 1999 @@ -47,7 +47,7 @@ #ifdef DEBUG_SWAP printk ("DebugVM: %s_swap_page entry %08lx, page %p (count %d), %s\n", (rw == READ) ? "read" : "write", - entry, (char *) page_address(page), atomic_read(&page->count), + entry, (char *) page_address(page), page_count(page), wait ? "wait" : "nowait"); #endif @@ -105,12 +105,12 @@ } } if (rw == READ) { - clear_bit(PG_uptodate, &page->flags); + ClearPageUptodate(page); kstat.pswpin++; } else kstat.pswpout++; - atomic_inc(&page->count); + get_page(page); if (p->swap_device) { zones[0] = offset; zones_used = 1; @@ -167,7 +167,7 @@ printk("swap_after_unlock_page: lock already cleared\n"); wake_up(&lock_queue); } - atomic_dec(&page->count); + put_page(page); return; } if (!wait) { @@ -182,23 +182,24 @@ /* block_size == PAGE_SIZE/zones_used */ brw_page(rw, page, dev, zones, block_size, 0); - + /* Note! For consistency we do all of the logic, * decrementing the page count, and unlocking the page in the * swap lock map - in the IO completion handler. */ - if (!wait) + if (!wait) { return; + } wait_on_page(page); /* This shouldn't happen, but check to be sure. */ - if (atomic_read(&page->count) == 0) + if (page_count(page) == 0) printk(KERN_ERR "rw_swap_page: page unused while waiting!\n"); #ifdef DEBUG_SWAP printk ("DebugVM: %s_swap_page finished on page %p (count %d)\n", (rw == READ) ? "read" : "write", - (char *) page_adddress(page), - atomic_read(&page->count)); + (char *) page_address(page), + page_count(page)); #endif } @@ -238,7 +239,7 @@ struct page *page = mem_map + MAP_NR(buf); if (page->inode && page->inode != &swapper_inode) - panic ("Tried to swap a non-swapper page"); + PAGE_BUG(page); /* * Make sure that we have a swap cache association for this @@ -268,23 +269,27 @@ struct page *page; page = mem_map + MAP_NR((unsigned long) buffer); - wait_on_page(page); - set_bit(PG_locked, &page->flags); - if (test_and_set_bit(PG_swap_cache, &page->flags)) { - printk ("VM: read_swap_page: page already in swap cache!\n"); - return; - } - if (page->inode) { - printk ("VM: read_swap_page: page already in page cache!\n"); - return; - } + + if (TryLockPage(page)) + PAGE_BUG(page); + if (test_and_set_bit(PG_swap_cache, &page->flags)) + PAGE_BUG(page); + if (page->inode) + PAGE_BUG(page); + get_page(page); /* Protect from shrink_mmap() */ page->inode = &swapper_inode; page->offset = entry; - atomic_inc(&page->count); /* Protect from shrink_mmap() */ rw_swap_page(rw, entry, buffer, 1); - atomic_dec(&page->count); - page->inode = 0; - clear_bit(PG_swap_cache, &page->flags); + + /* + * and now remove it from the pagecache ... + */ + if (TryLockPage(page)) + PAGE_BUG(page); + PageClearSwapCache(page); + remove_inode_page(page); + page_cache_release(page); + UnlockPage(page); } /* diff -u --recursive --new-file v2.3.6/linux/mm/swap_state.c linux/mm/swap_state.c --- v2.3.6/linux/mm/swap_state.c Wed Jan 13 09:54:50 1999 +++ linux/mm/swap_state.c Sun Jun 20 15:58:20 1999 @@ -25,7 +25,31 @@ * ensure that any mistaken dereferences of this structure cause a * kernel oops. */ -struct inode swapper_inode; + +static struct inode_operations swapper_inode_operations = { + NULL, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* bmap */ + NULL, /* readpage */ + NULL, /* writepage */ + block_flushpage, /* flushpage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ +}; + +struct inode swapper_inode = { i_op: &swapper_inode_operations }; #ifdef SWAP_CACHE_INFO unsigned long swap_cache_add_total = 0; @@ -49,20 +73,20 @@ #endif #ifdef DEBUG_SWAP printk("DebugVM: add_to_swap_cache(%08lx count %d, entry %08lx)\n", - page_address(page), atomic_read(&page->count), entry); + page_address(page), page_count(page), entry); #endif if (PageTestandSetSwapCache(page)) { printk(KERN_ERR "swap_cache: replacing non-empty entry %08lx " - "on page %08lx\n", - page->offset, page_address(page)); + "on page %08lx\n", + page->offset, page_address(page)); return 0; } if (page->inode) { printk(KERN_ERR "swap_cache: replacing page-cached entry " - "on page %08lx\n", page_address(page)); + "on page %08lx\n", page_address(page)); return 0; } - atomic_inc(&page->count); + get_page(page); page->inode = &swapper_inode; page->offset = entry; add_page_to_hash_queue(page, &swapper_inode, entry); @@ -111,7 +135,7 @@ result = 1; #ifdef DEBUG_SWAP printk("DebugVM: swap_duplicate(entry %08lx, count now %d)\n", - entry, p->swap_map[offset]); + entry, p->swap_map[offset]); #endif out: return result; @@ -127,7 +151,7 @@ bad_unused: printk(KERN_ERR "swap_duplicate at %8p: entry %08lx, unused page\n", - __builtin_return_address(0), entry); + __builtin_return_address(0), entry); goto out; } @@ -153,7 +177,7 @@ retval = p->swap_map[offset]; #ifdef DEBUG_SWAP printk("DebugVM: swap_count(entry %08lx, count %d)\n", - entry, retval); + entry, retval); #endif out: return retval; @@ -163,16 +187,16 @@ goto out; bad_file: printk(KERN_ERR - "swap_count: entry %08lx, nonexistent swap file!\n", entry); + "swap_count: entry %08lx, nonexistent swap file!\n", entry); goto out; bad_offset: printk(KERN_ERR - "swap_count: entry %08lx, offset exceeds max!\n", entry); + "swap_count: entry %08lx, offset exceeds max!\n", entry); goto out; bad_unused: printk(KERN_ERR - "swap_count at %8p: entry %08lx, unused page!\n", - __builtin_return_address(0), entry); + "swap_count at %8p: entry %08lx, unused page!\n", + __builtin_return_address(0), entry); goto out; } @@ -190,18 +214,17 @@ #ifdef DEBUG_SWAP printk("DebugVM: remove_from_swap_cache(%08lx count %d)\n", - page_address(page), atomic_read(&page->count)); + page_address(page), page_count(page)); #endif - PageClearSwapCache (page); + PageClearSwapCache(page); remove_inode_page(page); } - /* * This must be called only on pages that have * been verified to be in the swap cache. */ -void delete_from_swap_cache(struct page *page) +void __delete_from_swap_cache(struct page *page) { long entry = page->offset; @@ -210,13 +233,27 @@ #endif #ifdef DEBUG_SWAP printk("DebugVM: delete_from_swap_cache(%08lx count %d, " - "entry %08lx)\n", - page_address(page), atomic_read(&page->count), entry); + "entry %08lx)\n", + page_address(page), page_count(page), entry); #endif remove_from_swap_cache (page); swap_free (entry); } +/* + * This must be called only on pages that have + * been verified to be in the swap cache. + */ +void delete_from_swap_cache(struct page *page) +{ + lock_page(page); + + __delete_from_swap_cache(page); + + UnlockPage(page); + page_cache_release(page); +} + /* * Perform a free_page(), also freeing any swap cache associated with * this page if it is the last user of the page. @@ -229,18 +266,18 @@ /* * If we are the only user, then free up the swap cache. */ - if (PageSwapCache(page) && !is_page_shared(page)) { + if (PageSwapCache(page) && !is_page_shared(page)) delete_from_swap_cache(page); - } __free_page(page); } /* - * Lookup a swap entry in the swap cache. We need to be careful about - * locked pages. A found page will be returned with its refcount - * incremented. + * Lookup a swap entry in the swap cache. A found page will be returned + * unlocked and with its refcount incremented - we rely on the kernel + * lock getting page table operations atomic even if we drop the page + * lock before returning. */ struct page * lookup_swap_cache(unsigned long entry) @@ -251,23 +288,21 @@ swap_cache_find_total++; #endif while (1) { - found = find_page(&swapper_inode, entry); + found = find_lock_page(&swapper_inode, entry); if (!found) return 0; if (found->inode != &swapper_inode || !PageSwapCache(found)) goto out_bad; - if (!PageLocked(found)) { #ifdef SWAP_CACHE_INFO - swap_cache_find_success++; + swap_cache_find_success++; #endif - return found; - } - __free_page(found); - __wait_on_page(found); + UnlockPage(found); + return found; } out_bad: printk (KERN_ERR "VM: Found a non-swapper swap page!\n"); + UnlockPage(found); __free_page(found); return 0; } @@ -288,7 +323,7 @@ #ifdef DEBUG_SWAP printk("DebugVM: read_swap_cache_async entry %08lx%s\n", - entry, wait ? ", wait" : ""); + entry, wait ? ", wait" : ""); #endif /* * Make sure the swap entry is still in use. @@ -319,12 +354,12 @@ if (!add_to_swap_cache(new_page, entry)) goto out_free_page; - set_bit(PG_locked, &new_page->flags); + LockPage(new_page); rw_swap_page(READ, entry, (char *) new_page_addr, wait); #ifdef DEBUG_SWAP printk("DebugVM: read_swap_cache_async created " - "entry %08lx at %p\n", - entry, (char *) page_address(new_page)); + "entry %08lx at %p\n", + entry, (char *) page_address(new_page)); #endif return new_page; @@ -335,3 +370,4 @@ out: return found_page; } + diff -u --recursive --new-file v2.3.6/linux/mm/swapfile.c linux/mm/swapfile.c --- v2.3.6/linux/mm/swapfile.c Mon Jun 7 16:20:36 1999 +++ linux/mm/swapfile.c Wed Jun 16 19:26:27 1999 @@ -192,7 +192,7 @@ return; set_pte(dir, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); swap_free(entry); - atomic_inc(&mem_map[MAP_NR(page)].count); + get_page(mem_map + MAP_NR(page)); ++vma->vm_mm->rss; } diff -u --recursive --new-file v2.3.6/linux/mm/vmscan.c linux/mm/vmscan.c --- v2.3.6/linux/mm/vmscan.c Fri Apr 23 11:07:30 1999 +++ linux/mm/vmscan.c Wed Jun 16 19:26:27 1999 @@ -157,7 +157,7 @@ add_to_swap_cache(page_map, entry); /* We checked we were unlocked way up above, and we have been careful not to stall until here */ - set_bit(PG_locked, &page_map->flags); + LockPage(page_map); /* OK, do a physical asynchronous write to swap. */ rw_swap_page(WRITE, entry, (char *) page, 0); diff -u --recursive --new-file v2.3.6/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c --- v2.3.6/linux/net/ipv4/ip_fragment.c Wed May 26 18:14:37 1999 +++ linux/net/ipv4/ip_fragment.c Wed Jun 16 19:26:27 1999 @@ -5,7 +5,7 @@ * * The IP fragmentation functionality. * - * Version: $Id: ip_fragment.c,v 1.41 1999/05/27 00:38:07 davem Exp $ + * Version: $Id: ip_fragment.c,v 1.42 1999/06/12 13:11:34 davem Exp $ * * Authors: Fred N. van Kempen * Alan Cox @@ -218,7 +218,7 @@ out: /* Nuke the fragment queue. */ ip_free(qp); - spin_lock(&ipfrag_lock); + spin_unlock(&ipfrag_lock); } /* Memory limiting on fragments. Evictor trashes the oldest diff -u --recursive --new-file v2.3.6/linux/net/ipv4/udp.c linux/net/ipv4/udp.c --- v2.3.6/linux/net/ipv4/udp.c Wed Jun 9 14:45:37 1999 +++ linux/net/ipv4/udp.c Wed Jun 16 19:26:27 1999 @@ -655,11 +655,7 @@ if (msg->msg_namelen < sizeof(*usin)) return(-EINVAL); if (usin->sin_family != AF_INET) { - static int complained; - if (!complained++) - printk(KERN_WARNING "%s forgot to set AF_INET in udp sendmsg. Fix it!\n", current->comm); - if (usin->sin_family) - return -EINVAL; + return -EINVAL; } ufh.daddr = usin->sin_addr.s_addr; ufh.uh.dest = usin->sin_port; diff -u --recursive --new-file v2.3.6/linux/net/netsyms.c linux/net/netsyms.c --- v2.3.6/linux/net/netsyms.c Wed Jun 9 14:45:37 1999 +++ linux/net/netsyms.c Wed Jun 16 19:26:27 1999 @@ -377,7 +377,6 @@ EXPORT_SYMBOL(rtnetlink_links); EXPORT_SYMBOL(__rta_fill); EXPORT_SYMBOL(rtnetlink_dump_ifinfo); -EXPORT_SYMBOL(rtnl_wlockct); EXPORT_SYMBOL(rtnl); EXPORT_SYMBOL(neigh_delete); EXPORT_SYMBOL(neigh_add);