home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-09-07 | 1.8 MB | 69,168 lines |
Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
- diff -u -X rej --recursive --new-file linux.orig/CREDITS linux/CREDITS
- --- linux.orig/CREDITS Sat Sep 7 19:07:16 1996
- +++ linux/CREDITS Fri Sep 6 21:14:50 1996
- @@ -692,6 +692,12 @@
- S: D-64283 Darmstadt
- S: Germany
-
- +N: Russell King
- +E: rmk92@ecs.soton.ac.uk
- +D: Linux/arm port & hacker
- +S: 28 Waterer Gardens, Burgh Heath, Tadworth, Surrey. KT20 5PB
- +S: England
- +
- N: Olaf Kirch
- E: okir@monad.swb.de
- D: Author of the Linux Network Administrators' Guide
- diff -u -X rej --recursive --new-file linux.orig/Documentation/Configure.help linux/Documentation/Configure.help
- --- linux.orig/Documentation/Configure.help Sat Sep 7 18:59:20 1996
- +++ linux/Documentation/Configure.help Sat Aug 10 10:29:48 1996
- @@ -322,6 +322,11 @@
- in and removed from the running kernel whenever you want), say M
- here and read Documentation/modules.txt. If unsure, say Y.
-
- +Adfs Image File System (IMG)
- +CONFIG_BLK_DEV_IMG
- + This is a device driver that allows access to partitions held in
- + files on a RiscOS formatted hard disk drive.
- +
- Support for Deskstation RPC44
- CONFIG_DESKSTATION_RPC44
- This is a machine with a R4400 100 MHz CPU. To compile a Linux
- diff -u -X rej --recursive --new-file linux.orig/MAINTAINERS linux/MAINTAINERS
- --- linux.orig/MAINTAINERS Sat Sep 7 19:00:01 1996
- +++ linux/MAINTAINERS Sat Aug 17 23:13:11 1996
- @@ -341,6 +341,18 @@
- M: jam@acm.org
- S: Maintained
-
- +ARM:
- +P: Russell King
- +M: rmk92@ecs.soton.ac.uk
- +L: linux-arm@vger.rutgers.edu
- +W: http://whirligig.ecs.soton.ac.uk/~rmk92/armlinux.html
- +S: Maintained
- +
- +ARM MFM AND FLOPPY DRIVERS
- +P: Dave Gilbert
- +M: dag@cs.man.ac.uk
- +S: Maintained
- +
- REST:
- P: Linus Torvalds
- S: Buried alive in email
- diff -u -X rej --recursive --new-file linux.orig/Makefile linux/Makefile
- --- linux.orig/Makefile Sat Sep 7 19:07:17 1996
- +++ linux/Makefile Fri Sep 6 21:14:50 1996
- @@ -2,7 +2,7 @@
- PATCHLEVEL = 0
- SUBLEVEL = 18
-
- -ARCH = i386
- +ARCH = arm
-
- #
- # For SMP kernels, set this. We don't want to have this in the config file
- @@ -113,12 +113,16 @@
- # Include the make variables (CC, etc...)
- #
-
- +# Modified 11/02/96 by Russell King
- +# - Doesn't include the drivers subdirectory as standard.
- +# - Modified the 'nm' line in vmlinux so that we remove local and absolute symbols.
- +
- ARCHIVES =kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o net/network.a
- FILESYSTEMS =fs/filesystems.a
- -DRIVERS =drivers/block/block.a \
- +DRIVERS =drivers/block/block.a \
- drivers/char/char.a
- LIBS =$(TOPDIR)/lib/lib.a
- -SUBDIRS =kernel drivers mm fs net ipc lib
- +SUBDIRS =kernel mm fs net ipc lib
-
- ifeq ($(CONFIG_ISDN),y)
- DRIVERS := $(DRIVERS) drivers/isdn/isdn.a
- @@ -148,10 +152,14 @@
-
- include arch/$(ARCH)/Makefile
-
- +ifndef CONFIG_ARM
- +SUBDIRS := drivers $(SUBDIRS)
- +endif
- +
- ifdef SMP
-
- .S.s:
- - $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -E -o $*.s $<
- + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -E -o $*.o $<
- .S.o:
- $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $<
-
- @@ -176,9 +184,9 @@
- $(FILESYSTEMS) \
- $(DRIVERS) \
- $(LIBS) -o vmlinux
- - $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)' | sort > System.map
- + $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( a \)\|\( A \)\|\( t L\)' | sort > System.map
-
- -symlinks:
- +symlinks::
- rm -f include/asm
- ( cd include ; ln -sf asm-$(ARCH) asm)
-
- @@ -316,7 +324,7 @@
- rm -fr modules/*
- rm -f submenu*
-
- -mrproper: clean
- +mrproper:: clean
- rm -f include/linux/autoconf.h include/linux/version.h
- rm -f drivers/sound/local.h drivers/sound/.defines
- rm -f drivers/scsi/aic7xxx_asm drivers/scsi/aic7xxx_seq.h
- @@ -331,11 +339,11 @@
- rm -f $(TOPDIR)/include/linux/modversions.h
- rm -f $(TOPDIR)/include/linux/modules/*
-
- -
- distclean: mrproper
- rm -f core `find . \( -name '*.orig' -o -name '*.rej' -o -name '*~' \
- - -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \
- - -o -name '.*.rej' -o -name '.SUMS' -o -size 0 \) -print` TAGS
- + -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \
- + -o -name '.*.rej' -o -name '.SUMS' -o -size 0 \) -print` TAGS
- +
-
- backup: mrproper
- cd .. && tar cf - linux/ | gzip -9 > backup.gz
- diff -u -X rej --recursive --new-file linux.orig/README.arm linux/README.arm
- --- linux.orig/README.arm Thu Jan 1 01:00:00 1970
- +++ linux/README.arm Sat Aug 17 23:32:37 1996
- @@ -0,0 +1,121 @@
- +ARM Linux 2.0.13
- +================
- +
- +*** Warning ***
- +---------------
- +
- + Currently, 1.3.45 onwards contains new Image file drivers (IMG)
- + that are in alpha testing. I make no claims to their stability
- + at the moment, so use it at your peril - it may destroy your
- + hard disk!
- +
- +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: rmk92@ecs.soton.ac.uk
- + Desc: Original developer of ARM Linux, A5000 code and project
- + co-ordinator.
- +
- + Name: Dave Gilbert
- + Mail: gilbertd@cs.man.ac.uk
- + Desc: A3/4/5xx floppy and hard disk code maintainer.
- +
- + Name: Ian Jeffray
- + Mail: ian_jeffray@MENTORG.COM
- + Desc: A3/4/5xx serial and parallel port code maintainer.
- +
- +
- +Notes
- +=====
- +
- +Cross compilation
- +-----------------
- +
- + In order to compile ARM Linux, you will need the cross-utilities on
- + ftp.ecs.soton.ac.uk in the /pub/armlinux/cross-compilers subdirectory,
- + or a working version of ARM Linux with the compiler distribution.
- +
- + To configure the kernel, edit the top-level makefile, replacing:
- +
- + ARCH = i386 with ARCH = arm
- +
- + Also, alter CC= and HOSTCC= to point to your compiler.
- +
- + Then examine arch/arm/Makefile for sub-architecture options (including
- + processor and machine type), setting them as required.
- +
- + 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'.
- +
- + Please send all patches, bug reports and code for the ARM Linux project
- + to rmk92@ecs.soton.ac.uk. Patches will not be included into future
- + kernels unless they come to me (or the relevant person concerned).
- +
- +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,Green:
- + Wrong/incorrect processor architecture detected.
- + Red:
- + Undefined instruction handler was entered.
- + Green:
- + Prefetch abort handler entered.
- + Yellow:
- + Data abort handler entered.
- + Magenta:
- + Address exception handler entered.
- +
- +Request to developers
- +---------------------
- +
- + When writing device drivers which include a separate assember file, please
- + include it in with the C file, and not the arch/arm/lib directory. This
- + helps to allow the driver to be compiled as a loadable module without
- + requiring half the code to be needlessly compiled into the main kernel
- + image.
- +
- +IMG drivers
- +-----------
- +
- + The new image drivers (introduced in v1.3.45) have changed again. They
- + now hook into the ll_rw code, and appears to give 770k/s now (used to be
- + 660k/s). However, they're not fully tested yet!
- +
- +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 harddrive set to 2). I've got an internal
- + 20MB and a great big external 5.25" FH 64MB drive (who could
- + ever want more :-) ).
- +
- + I've just got 240K/s off it (a dd with bs=128k); thats about half of what
- + RiscOS gets; but its a heck of a lot better than the 50K/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.
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/Makefile linux/arch/arm/Makefile
- --- linux.orig/arch/arm/Makefile Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/Makefile Sun Aug 25 10:33:23 1996
- @@ -0,0 +1,135 @@
- +#
- +# arch/arm/Makefile
- +#
- +# THIS IS THE VERSION/[PATCHLEVELS]: 2.0.10/[2,1,0]
- +# [RMK,DAG,IC]
- +#
- +# This file is included by the global makefile so that you can add your own
- +# architecture-specific flags and dependencies. Remember to do have actions
- +# for "archclean" and "archdep" for cleaning up and making dependencies for
- +# this architecture
- +#
- +# This file is subject to the terms and conditions of the GNU General Public
- +# License. See the file "COPYING" in the main directory of this archive
- +# for more details.
- +#
- +# Copyright (C) 1995, 1996 by Russell King
- +
- +
- +# MACHINE can be: arc a5k rpc
- +# PROCESSOR can be: arm2 arm3 arm6
- +# Computer: MACHINE: PROCESSOR:
- +# A3xx arc arm2
- +# A4xx arc arm2
- +# A5xx arc arm2
- +# A5000 a5k arm3
- +# A4000 a5k arm3
- +# A3020 a5k arm3 (arm250 really)
- +#
- +CONFIG_ARCH_A5K = 1
- +#CONFIG_ARCH_ARM = 1
- +#CONFIG_ARCH_SA110EVAL = 1
- +#
- +#CONFIG_PROC_ARM2 = 1
- +CONFIG_PROC_ARM3 = 1
- +#CONFIG_PROC_ARM6 = 1
- +#CONFIG_PROC_SA110 = 1
- +
- +# Machine Architecture
- +ifdef CONFIG_ARCH_A5K
- +CFLAGS += -DCONFIG_ARCH_A5K
- +MACHINE = a5k
- +endif
- +
- +ifdef CONFIG_ARCH_ARM
- +CFLAGS += -DCONFIG_ARCH_ARM
- +MACHINE = a5k
- +endif
- +
- +ifdef CONFIG_ARCH_SA110EVAL
- +CFLAGS += -DCONFIG_ARCH_SA110EVAL
- +MACHINE = sa110eval
- +endif
- +
- +# Processor Architecture
- +ifdef CONFIG_PROC_ARM2
- +CFLAGS_ARCH += -m2
- +PROCESSOR = arm2
- +TEXTADDR = 0x01800000
- +endif
- +
- +ifdef CONFIG_PROC_ARM3
- +CFLAGS_ARCH += -m3
- +PROCESSOR = arm3
- +TEXTADDR = 0x01800000
- +endif
- +
- +ifdef CONFIG_PROC_ARM6
- +CFLAGS_ARCH += -m6
- +PROCESSOR = arm6
- +TEXTADDR = 0xC0000000
- +endif
- +
- +ifdef CONFIG_PROC_SA110
- +CFLAGS_ARCH += -m6
- +PROCESSOR = sa110
- +TEXTADDR = 0xC0000000
- +endif
- +
- +OBJDUMP = objdump
- +CPP = $(CC) -E
- +ARCHCC := $(word 1,$(CC))
- +GCCLIB := `$(ARCHCC) $(CFLAGS_ARCH) --print-libgcc-file-name`
- +HOSTCFLAGS := $(CFLAGS:-fomit-frame-pointer=)
- +CFLAGS := $(CFLAGS:-fomit-frame-pointer=) $(CFLAGS_ARCH)
- +LINKFLAGS = -Ttext $(TEXTADDR)
- +ZLINKFLAGS = -N -Ttext $(TEXTADDR)
- +
- +HEAD := arch/arm/kernel/head.o
- +SUBDIRS := $(SUBDIRS) arch/arm/kernel arch/arm/mm arch/arm/lib arch/arm/drivers
- +ARCHIVES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(ARCHIVES)
- +LIBS := arch/arm/lib/lib.a $(LIBS) $(GCCLIB)
- +DRIVERS := arch/arm/drivers/block/block.a arch/arm/drivers/char/char.a \
- + arch/arm/drivers/net/net.a
- +
- +ifdef CONFIG_SOUND
- +DRIVERS := $(DRIVERS) arch/arm/drivers/sound/sound.a
- +endif
- +
- +ifdef CONFIG_SCSI
- +DRIVERS := $(DRIVERS) arch/arm/drivers/scsi/scsi.a
- +endif
- +
- +symlinks::
- + rm -f include/asm-arm/arch include/asm-arm/proc
- + (cd include/asm-arm; ln -sf arch-$(MACHINE) arch; ln -sf proc-$(PROCESSOR) proc)
- +
- +mrproper::
- + 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
- +
- +arch/arm/mm: dummy
- + $(MAKE) linuxsubdirs SUBDIRS=arch/arm/mm
- +
- +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
- +
- +zImage:
- + @$(MAKEBOOT) zImage
- +
- +zinstall:
- + @$(MAKEBOOT) zinstall
- +
- +Image: vmlinux
- + @$(MAKEBOOT) Image
- +
- +install: vmlinux
- + @$(MAKEBOOT) install
- +
- +archclean:
- + @$(MAKEBOOT) clean
- +
- +archdep:
- + @$(MAKEBOOT) dep
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/boot/Makefile linux/arch/arm/boot/Makefile
- --- linux.orig/arch/arm/boot/Makefile Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/boot/Makefile Mon Mar 11 20:46:17 1996
- @@ -0,0 +1,34 @@
- +#
- +# arch/arm/boot/Makefile
- +#
- +# This file is subject to the terms and conditions of the GNU General Public
- +# License. See the file "COPYING" in the main directory of this archive
- +# for more details.
- +#
- +# Copyright (C) 1995, 1996 Russell King
- +#
- +
- +Image: $(CONFIGURE) tools/build $(TOPDIR)/vmlinux
- + tools/build $(TOPDIR)/vmlinux > Image
- + sync
- +
- +zImage: $(CONFIGURE) tools/build compressed/vmlinux
- + tools/build compressed/vmlinux > zImage
- +
- +compressed/vmlinux: $(TOPDIR)/vmlinux dep
- + @$(MAKE) -C compressed vmlinux
- +
- +install: $(CONFIGURE) Image
- + sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) Image $(TOPDIR)/System.map "$(INSTALL_PATH)"
- +
- +zinstall: $(CONFIGURE) zImage
- + sh ./install.sh $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) zImage $(TOPDIR)/System.map "$(INSTALL_PATH)"
- +
- +tools/build: tools/build.c
- + $(HOSTCC) $(HOSTCFLAGS) -o $@ $< -I$(TOPDIR)/include
- +
- +clean:
- + rm -f Image zImage tools/build
- + @$(MAKE) -C compressed clean
- +
- +dep:
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/boot/compressed/Makefile linux/arch/arm/boot/compressed/Makefile
- --- linux.orig/arch/arm/boot/compressed/Makefile Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/boot/compressed/Makefile Thu Jul 11 09:44:26 1996
- @@ -0,0 +1,59 @@
- +#
- +# linux/arch/arm/boot/compressed/Makefile
- +#
- +# create a compressed vmlinux image from the original vmlinux
- +#
- +# With this config, max compressed image size = 640k
- +# Uncompressed image size = 1.3M (text+data)
- +
- +CFLAGS = -O2 -DSTDC_HEADERS
- +LOADADDR = 0x01800000
- +RELADDR = 0x01960000
- +ARFLAGS = rcv
- +
- +SYSTEM = $(TOPDIR)/vmlinux
- +DECOMP_OBJS = misc.o ../../lib/ll_char_wr.o
- +
- +
- +all: vmlinux
- +
- +vmlinux: head.o piggy.o
- + $(LD) -Ttext=$(LOADADDR) -o vmlinux head.o piggy.o
- +
- +head.o: head.S
- + $(CC) -traditional -DRELADDR=$(RELADDR) -c head.S
- +
- +piggy.o: decomphead.o decompress.a compressed.o
- + $(LD) -s -Ttext=$(RELADDR) -o a.out decomphead.o compressed.o decompress.a
- + strip a.out
- + ./piggyback a.out > piggy.o
- + rm -f a.out
- +
- +compressed.o: $(SYSTEM) xtract piggyback
- + ./xtract $(SYSTEM) | gzip -9 | ./piggyback > compressed.o
- +
- +
- +# rules for decompression part of kernel
- +
- +decompress.a: $(DECOMP_OBJS)
- + $(AR) $(ARFLAGS) decompress.a $(DECOMP_OBJS)
- +
- +decomphead.o: decomphead.S
- + $(CC) -traditional -DLOADADDR=$(LOADADDR) -c decomphead.S
- +
- +../../lib/ll_char_wr.o:
- + make -C ../../lib ll_char_wr.o
- +
- +
- +# rules for extracting & piggybacking the kernel
- +
- +xtract: xtract.c
- + $(HOSTCC) $(HOSTCFLAGS) -o xtract xtract.c
- +
- +piggyback: piggyback.c
- + $(HOSTCC) $(HOSTCFLAGS) -o piggyback piggyback.c
- +
- +
- +clean:
- + rm -f xtract piggyback vmlinux decompress.a a.out
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/boot/compressed/decomphead.S linux/arch/arm/boot/compressed/decomphead.S
- --- linux.orig/arch/arm/boot/compressed/decomphead.S Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/boot/compressed/decomphead.S Thu Jul 11 09:59:45 1996
- @@ -0,0 +1,25 @@
- +/*
- + * linux/arch/arm/boot/decomphead.S
- + */
- +
- +.text
- + .global __stext
- +__stext:
- + b start
- +LC0:
- + .word __edata
- + .word __end
- + .word LOADADDR + 0x23
- + .word _user_stack+4096
- +start:
- + adr r1, LC0
- + ldmia r1, {r2, r3, r4, sp}
- + mov r1, #0
- +clrzlp: str r1, [r2], #4
- + cmp r2, r3
- + blt clrzlp
- + bic r0, r4, #3
- + bl _decompress_kernel
- + mov r0, #0
- + mov pc, r4
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/boot/compressed/head.S linux/arch/arm/boot/compressed/head.S
- --- linux.orig/arch/arm/boot/compressed/head.S Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/boot/compressed/head.S Sun Feb 11 09:32:10 1996
- @@ -0,0 +1,36 @@
- + .text
- + .align
- +
- +@ Entry point
- +@ r0 = kernel info page
- +@ r1 = kernel length (length of this code+data)
- +
- + .global __stext
- +__stext:
- + b start
- +LC0:
- + .word _input_data
- + .word _input_end
- + .word RELADDR
- +start:
- + teq r0, #0
- + beq newparams
- + mov r4, #0x02000000
- + add r4, r4, #0x0007C000 @ = 0x0207C000 [new param location]
- + mov r3, #0x4000
- + sub r3, r3, #4
- +lp2: ldmia r0!, {r5, r6, r7, r8, r9, r10, r11, r12}
- + stmia r4!, {r5, r6, r7, r8, r9, r10, r11, r12}
- + subs r3, r3, #32
- + bpl lp2
- +newparams: adr r3, LC0
- + ldmia r3, {r2, r3, r4}
- + sub r3, r3, r2
- + mov r1, r4
- +lp: ldmia r2!, {r5, r6, r7, r8, r9, r10, r11, r12}
- + stmia r4!, {r5, r6, r7, r8, r9, r10, r11, r12}
- + subs r3, r3, #32
- + bpl lp
- + ldr r1, [r1, #0x14]
- + mov pc, r1
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/boot/compressed/misc.c linux/arch/arm/boot/compressed/misc.c
- --- linux.orig/arch/arm/boot/compressed/misc.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/boot/compressed/misc.c Thu Jul 11 09:57:17 1996
- @@ -0,0 +1,402 @@
- +/*
- + * misc.c
- + *
- + * This is a collection of several routines from gzip-1.0.3
- + * adapted for Linux.
- + *
- + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
- + * puts by Nick Holloway 1993
- + */
- +
- +#include <string.h>
- +
- +#include <asm/segment.h>
- +#include <asm/io.h>
- +
- +/*
- + * gzip delarations
- + */
- +
- +#define OF(args) args
- +#define STATIC static
- +
- +/*
- + * Optimised C version of memzero for the ARM.
- + */
- +extern __inline__ __ptr_t memzero (__ptr_t s, size_t n)
- +{
- + int i = n;
- + union { int i; char *cp; unsigned long *lp; } ss;
- + ss.cp = (char *)s;
- +
- + if (i > 3) {
- + while (ss.i & 3) {
- + *ss.cp++ = 0;
- + i --;
- + }
- + }
- +
- + while (i > 32) {
- + *(unsigned long *)ss.lp++ = 0;
- + *(unsigned long *)ss.lp++ = 0;
- + *(unsigned long *)ss.lp++ = 0;
- + *(unsigned long *)ss.lp++ = 0;
- + *(unsigned long *)ss.lp++ = 0;
- + *(unsigned long *)ss.lp++ = 0;
- + *(unsigned long *)ss.lp++ = 0;
- + *(unsigned long *)ss.lp++ = 0;
- + i -= 32;
- + }
- +
- + for (; i > 0; i--)
- + *ss.cp++ = 0;
- + return s;
- +}
- +
- +typedef unsigned char uch;
- +typedef unsigned short ush;
- +typedef unsigned long ulg;
- +
- +#define WSIZE 0x8000 /* Window size must be at least 32k, */
- + /* and a power of two */
- +
- +static uch *inbuf; /* input buffer */
- +static uch window[WSIZE]; /* Sliding window buffer */
- +
- +static unsigned insize = 0; /* valid bytes in inbuf */
- +static unsigned inptr = 0; /* index of next byte to be processed in inbuf */
- +static unsigned outcnt = 0; /* bytes in output buffer */
- +
- +/* gzip flag byte */
- +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
- +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
- +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
- +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
- +#define COMMENT 0x10 /* bit 4 set: file comment present */
- +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
- +#define RESERVED 0xC0 /* bit 6,7: reserved */
- +
- +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
- +
- +/* Diagnostic functions */
- +#ifdef DEBUG
- +# define Assert(cond,msg) {if(!(cond)) error(msg);}
- +# define Trace(x) fprintf x
- +# define Tracev(x) {if (verbose) fprintf x ;}
- +# define Tracevv(x) {if (verbose>1) fprintf x ;}
- +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
- +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
- +#else
- +# define Assert(cond,msg)
- +# define Trace(x)
- +# define Tracev(x)
- +# define Tracevv(x)
- +# define Tracec(c,x)
- +# define Tracecv(c,x)
- +#endif
- +
- +static int fill_inbuf(void);
- +static void flush_window(void);
- +static void error(char *m);
- +static void gzip_mark(void **);
- +static void gzip_release(void **);
- +
- +/*
- + * These are set up by the setup-routine at boot-time:
- + */
- +
- +struct param_struct
- +{
- + unsigned long page_size;
- + unsigned long nr_pages;
- + unsigned long ramdisk_size;
- + unsigned long mountrootrdonly;
- + unsigned long rootdev;
- + unsigned long video_num_cols;
- + unsigned long video_num_rows;
- + unsigned long video_x;
- + unsigned long video_y;
- + unsigned long memc_control_reg;
- + unsigned char sounddefault;
- + unsigned char adfsdrives;
- + unsigned char bytes_per_char_h;
- + unsigned char bytes_per_char_v;
- + unsigned long unused[256/4-11];
- + char paths[8][128];
- + char commandline[256];
- +};
- +
- +static struct param_struct *params=(struct param_struct *)0x0207C000;
- +
- +extern char input_data[], input_end[];
- +
- +static long bytes_out = 0;
- +static uch *output_data;
- +static unsigned long output_ptr = 0;
- +
- +static void *malloc(int size);
- +static void free(void *where);
- +static void error(char *m);
- +static void gzip_mark(void **);
- +static void gzip_release(void **);
- +
- +static void puts(const char *);
- +
- +unsigned int bytes_per_char_h;
- +unsigned int video_num_lines;
- +unsigned int video_num_columns;
- +static unsigned int white;
- +static unsigned char *vidmem = (unsigned char *)0x02000000;
- +
- +extern int end;
- +static long free_mem_ptr = (long)&end;
- +
- +#define HEAP_SIZE 0x2000
- +
- +#include "../../../../lib/inflate.c"
- +
- +static void *malloc(int size)
- +{
- + void *p;
- +
- + if (size <0) error("Malloc error\n");
- + if (free_mem_ptr <= 0) error("Memory error\n");
- +
- + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */
- +
- + p = (void *)free_mem_ptr;
- + free_mem_ptr += size;
- +
- + /*
- + * The part of the compressed kernel which has already been expanded
- + * is no longer needed. Therefore we can reuse it for malloc.
- + * With bigger kernels, this is necessary.
- + */
- +
- + if (free_mem_ptr >= 0x01a00000)
- + error("\nOut of memory\n");
- + return p;
- +}
- +
- +static void free(void *where)
- +{ /* Don't care */
- +}
- +
- +static void gzip_mark(void **ptr)
- +{
- + *ptr = (void *) free_mem_ptr;
- +}
- +
- +static void gzip_release(void **ptr)
- +{
- + free_mem_ptr = (long) *ptr;
- +}
- +
- +const char cmap_80[][8]=
- +{
- + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* */
- + {0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x00}, /* ! */
- + {0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00}, /* " */
- + {0x36,0x36,0x7F,0x36,0x7F,0x36,0x36,0x00}, /* # */
- + {0x0C,0x3F,0x68,0x3E,0x0B,0x7E,0x18,0x00}, /* $ */
- + {0x60,0x66,0x0C,0x18,0x30,0x66,0x06,0x00}, /* % */
- + {0x38,0x6C,0x6C,0x38,0x6D,0x66,0x3B,0x00}, /* & */
- + {0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00}, /* ' */
- + {0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00}, /* ( */
- + {0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00}, /* ) */
- + {0x00,0x18,0x7E,0x3C,0x7E,0x18,0x00,0x00}, /* * */
- + {0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00}, /* + */
- + {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30}, /* , */
- + {0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00}, /* - */
- + {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00}, /* . */
- + {0x00,0x06,0x0C,0x18,0x30,0x60,0x00,0x00}, /* / */
- + {0x3C,0x66,0x6E,0x7E,0x76,0x66,0x3C,0x00}, /* 0 */
- + {0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00}, /* 1 */
- + {0x3C,0x66,0x06,0x0C,0x18,0x30,0x7E,0x00}, /* 2 */
- + {0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, /* 3 */
- + {0x0C,0x1C,0x3C,0x6C,0x7E,0x0C,0x0C,0x00}, /* 4 */
- + {0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, /* 5 */
- + {0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00}, /* 6 */
- + {0x7E,0x06,0x0C,0x18,0x30,0x30,0x30,0x00}, /* 7 */
- + {0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, /* 8 */
- + {0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00}, /* 9 */
- + {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x00}, /* : */
- + {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x30}, /* ; */
- + {0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x00}, /* < */
- + {0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00}, /* = */
- + {0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x00}, /* > */
- + {0x3C,0x66,0x0C,0x18,0x18,0x00,0x18,0x00}, /* ? */
- + {0x3C,0x66,0x6E,0x6A,0x6E,0x60,0x3C,0x00}, /* @ */
- + {0x3C,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* A */
- + {0x7C,0x66,0x66,0x7C,0x66,0x66,0x7C,0x00}, /* B */
- + {0x3C,0x66,0x60,0x60,0x60,0x66,0x3C,0x00}, /* C */
- + {0x78,0x6C,0x66,0x66,0x66,0x6C,0x78,0x00}, /* D */
- + {0x7E,0x60,0x60,0x7C,0x60,0x60,0x7E,0x00}, /* E */
- + {0x7E,0x60,0x60,0x7C,0x60,0x60,0x60,0x00}, /* F */
- + {0x3C,0x66,0x60,0x6E,0x66,0x66,0x3C,0x00}, /* G */
- + {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, /* H */
- + {0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00}, /* I */
- + {0x3E,0x0C,0x0C,0x0C,0x0C,0x6C,0x38,0x00}, /* J */
- + {0x66,0x6C,0x78,0x70,0x78,0x6C,0x66,0x00}, /* K */
- + {0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00}, /* L */
- + {0x63,0x77,0x7F,0x6B,0x6B,0x63,0x63,0x00}, /* M */
- + {0x66,0x66,0x76,0x7E,0x6E,0x66,0x66,0x00}, /* N */
- + {0x3C,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* O */
- + {0x7C,0x66,0x66,0x7C,0x60,0x60,0x60,0x00}, /* P */
- + {0x3C,0x66,0x66,0x66,0x6A,0x6C,0x36,0x00}, /* Q */
- + {0x7C,0x66,0x66,0x7C,0x6C,0x66,0x66,0x00}, /* R */
- + {0x3C,0x66,0x60,0x3C,0x06,0x66,0x3C,0x00}, /* S */
- + {0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* T */
- + {0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x00}, /* U */
- + {0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00}, /* V */
- + {0x63,0x63,0x6B,0x6B,0x7F,0x77,0x63,0x00}, /* W */
- + {0x66,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00}, /* X */
- + {0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00}, /* Y */
- + {0x7E,0x06,0x0C,0x18,0x30,0x60,0x7E,0x00}, /* Z */
- + {0x7C,0x60,0x60,0x60,0x60,0x60,0x7C,0x00}, /* [ */
- + {0x00,0x60,0x30,0x18,0x0C,0x06,0x00,0x00}, /* \ */
- + {0x3E,0x06,0x06,0x06,0x06,0x06,0x3E,0x00}, /* ] */
- + {0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00}, /* ^ */
- + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF}, /* _ */
- + {0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00}, /* ` */
- + {0x00,0x00,0x3C,0x06,0x3E,0x66,0x3E,0x00}, /* a */
- + {0x60,0x60,0x7C,0x66,0x66,0x66,0x7C,0x00}, /* b */
- + {0x00,0x00,0x3C,0x66,0x60,0x66,0x3C,0x00}, /* c */
- + {0x06,0x06,0x3E,0x66,0x66,0x66,0x3E,0x00}, /* d */
- + {0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00}, /* e */
- + {0x1C,0x30,0x30,0x7C,0x30,0x30,0x30,0x00}, /* f */
- + {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x3C}, /* g */
- + {0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x00}, /* h */
- + {0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00}, /* i */
- + {0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x70}, /* j */
- + {0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00}, /* k */
- + {0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00}, /* l */
- + {0x00,0x00,0x36,0x7F,0x6B,0x6B,0x63,0x00}, /* m */
- + {0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x00}, /* n */
- + {0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00}, /* o */
- + {0x00,0x00,0x7C,0x66,0x66,0x7C,0x60,0x60}, /* p */
- + {0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x07}, /* q */
- + {0x00,0x00,0x6C,0x76,0x60,0x60,0x60,0x00}, /* r */
- + {0x00,0x00,0x3E,0x60,0x3C,0x06,0x7C,0x00}, /* s */
- + {0x30,0x30,0x7C,0x30,0x30,0x30,0x1C,0x00}, /* t */
- + {0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x00}, /* u */
- + {0x00,0x00,0x66,0x66,0x66,0x3C,0x18,0x00}, /* v */
- + {0x00,0x00,0x63,0x6B,0x6B,0x7F,0x36,0x00}, /* w */
- + {0x00,0x00,0x66,0x3C,0x18,0x3C,0x66,0x00}, /* x */
- + {0x00,0x00,0x66,0x66,0x66,0x3E,0x06,0x3C}, /* y */
- + {0x00,0x00,0x7E,0x0C,0x18,0x30,0x7E,0x00}, /* z */
- + {0x0C,0x18,0x18,0x70,0x18,0x18,0x0C,0x00}, /* { */
- + {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, /* | */
- + {0x30,0x18,0x18,0x0E,0x18,0x18,0x30,0x00}, /* } */
- + {0x31,0x6B,0x46,0x00,0x00,0x00,0x00,0x00}, /* ~ */
- + {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF} /* */
- +};
- +
- +static void puts(const char *s)
- +{
- + extern void ll_write_char(char *, unsigned long);
- + int x,y;
- + unsigned char c;
- + char *ptr;
- +
- + x = params->video_x;
- + y = params->video_y;
- +
- + while ( ( c = *(unsigned char *)s++ ) != '\0' ) {
- + if ( c == '\n' ) {
- + x = 0;
- + if ( ++y >= video_num_lines ) {
- + y--;
- + }
- + } else {
- + ptr = vidmem + (y*video_num_columns*params->bytes_per_char_v+x)*bytes_per_char_h;
- + ll_write_char (ptr, c|(white<<8));
- + if ( ++x >= video_num_columns ) {
- + x = 0;
- + if ( ++y >= video_num_lines ) {
- + y--;
- + }
- + }
- + }
- + }
- +
- + params->video_x = x;
- + params->video_y = y;
- +}
- +
- +__ptr_t memcpy(__ptr_t __dest, __const __ptr_t __src,
- + size_t __n)
- +{
- + int i;
- + char *d = (char *)__dest, *s = (char *)__src;
- +
- + for (i=0;i<__n;i++) d[i] = s[i];
- +}
- +
- +/* ===========================================================================
- + * Fill the input buffer. This is called only when the buffer is empty
- + * and at least one byte is really needed.
- + */
- +int fill_inbuf()
- +{
- + if (insize != 0) {
- + error("ran out of input data\n");
- + }
- +
- + inbuf = input_data;
- + insize = input_end - input_data;
- + inptr = 1;
- + return inbuf[0];
- +}
- +
- +/* ===========================================================================
- + * Write the output window window[0..outcnt-1] and update crc and bytes_out.
- + * (Used for the decompressed data only.)
- + */
- +void flush_window()
- +{
- + ulg c = crc;
- + unsigned n;
- + uch *in, *out, ch;
- +
- + in = window;
- + out = &output_data[output_ptr];
- + for (n = 0; n < outcnt; n++) {
- + ch = *out++ = *in++;
- + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
- + }
- + crc = c;
- + bytes_out += (ulg)outcnt;
- + output_ptr += (ulg)outcnt;
- + outcnt = 0;
- +}
- +
- +static void error(char *x)
- +{
- + puts("\n\n");
- + puts(x);
- + puts("\n\n -- System halted");
- +
- + while(1); /* Halt */
- +}
- +
- +#define STACK_SIZE (4096)
- +
- +long user_stack [STACK_SIZE];
- +
- +void decompress_kernel(int output_start)
- +{
- + video_num_lines = params->video_num_rows;
- + video_num_columns = params->video_num_cols;
- + bytes_per_char_h = params->bytes_per_char_h;
- + white = bytes_per_char_h == 8 ? 0xfc : 7;
- +
- + if (params->nr_pages * params->page_size < 2048*1024) error("<2M of mem\n");
- +
- + output_data = (char *)output_start; /* Points to kernel start */
- +
- + makecrc();
- + puts("Uncompressing Linux...");
- + gunzip();
- + puts("done.\nNow booting the kernel\n");
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/boot/compressed/piggyback.c linux/arch/arm/boot/compressed/piggyback.c
- --- linux.orig/arch/arm/boot/compressed/piggyback.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/boot/compressed/piggyback.c Sun Jul 7 12:34:25 1996
- @@ -0,0 +1,91 @@
- +/*
- + * linux/zBoot/piggyback.c
- + *
- + * (C) 1993 Hannu Savolainen
- + */
- +
- +/*
- + * This program reads the compressed system image from stdin and
- + * encapsulates it into an object file written to the stdout.
- + */
- +
- +#include <stdio.h>
- +#include <unistd.h>
- +#include <a.out.h>
- +#include <sys/fcntl.h>
- +
- +int main(int argc, char *argv[])
- +{
- + int n=0, len=0, fd = 0;
- + char tmp_buf[640*1024];
- +
- + struct exec obj = {0x00670107}; /* object header */
- + char string_names[] = {"_input_data\0_input_end\0"};
- +
- + struct nlist var_names[2] = /* Symbol table */
- + {
- + { /* _input_data */
- + {(char *)4}, 7, 0, 0, 0
- + },
- + { /* _input_len */
- + {(char *)16}, 7, 0, 0, 0
- + }
- + };
- +
- +
- + if (argc > 1) {
- + fd = open (argv[1], O_RDONLY);
- + if (fd < 0) {
- + fprintf (stderr, "%s: ", argv[0]);
- + perror (argv[1]);
- + exit (1);
- + }
- + }
- +
- + len = 0;
- + while ((n = read(fd, &tmp_buf[len], sizeof(tmp_buf)-len+1)) > 0)
- + len += n;
- +
- + len = (len + 3) & ~3;
- +
- + if (n==-1) {
- + perror("stdin");
- + exit(-1);
- + }
- +
- + if (len >= sizeof(tmp_buf)) {
- + fprintf(stderr, "%s: Input too large\n", argv[0]);
- + exit(-1);
- + }
- +
- + if (argc < 2)
- + fprintf(stderr, "Compressed size %d.\n", len);
- +
- +/*
- + * Output object header
- + */
- + obj.a_data = len /* + sizeof(long) */;
- + obj.a_syms = sizeof(var_names);
- + write(1, (char *)&obj, sizeof(obj));
- +
- +/*
- + * Output data segment (compressed system & len)
- + */
- + write(1, tmp_buf, len);
- +
- +/*
- + * Output symbol table
- + */
- + var_names[1].n_value = len;
- + write(1, (char *)&var_names, sizeof(var_names));
- +
- +/*
- + * Output string table
- + */
- + len = sizeof(string_names) + sizeof(len);
- + write(1, (char *)&len, sizeof(len));
- + write(1, string_names, sizeof(string_names));
- +
- + exit(0);
- +
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/boot/compressed/xtract.c linux/arch/arm/boot/compressed/xtract.c
- --- linux.orig/arch/arm/boot/compressed/xtract.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/boot/compressed/xtract.c Sat Jul 13 19:20:28 1996
- @@ -0,0 +1,86 @@
- +/*
- + * linux/zBoot/xtract.c
- + *
- + * Copyright (C) 1993 Hannu Savolainen
- + *
- + * Extracts the system image and writes it to the stdout.
- + * based on tools/build.c by Linus Torvalds
- + */
- +
- +#include <stdio.h> /* fprintf */
- +#include <string.h>
- +#include <stdlib.h> /* contains exit */
- +#include <sys/types.h> /* unistd.h needs this */
- +#include <sys/stat.h>
- +#include <sys/sysmacros.h>
- +#include <unistd.h> /* contains read/write */
- +#include <fcntl.h>
- +#include <a.out.h>
- +
- +#define N_MAGIC_OFFSET 1024
- +
- +static int GCC_HEADER = sizeof(struct exec);
- +
- +#define STRINGIFY(x) #x
- +
- +void die(char * str)
- +{
- + fprintf(stderr,"%s\n",str);
- + exit(1);
- +}
- +
- +void usage(void)
- +{
- + die("Usage: xtract system [ | gzip | piggyback > piggy.s]");
- +}
- +
- +int main(int argc, char ** argv)
- +{
- + int /*i,c,*/id, sz;
- + char buf[1024];
- +/* char major_root, minor_root;*/
- +/* struct stat sb;*/
- +
- + struct exec *ex = (struct exec *)buf;
- +
- + if (argc != 2)
- + usage();
- +
- + if ((id=open(argv[1],O_RDONLY,0))<0)
- + die("Unable to open 'system'");
- + if (read(id,buf,GCC_HEADER) != GCC_HEADER)
- + die("Unable to read header of 'system'");
- + if (N_MAGIC(*ex) == ZMAGIC) {
- + GCC_HEADER = N_MAGIC_OFFSET;
- + lseek(id, GCC_HEADER, SEEK_SET);
- + } else if (N_MAGIC(*ex) != QMAGIC)
- + die("Non-GCC header of 'system'");
- +
- + sz = N_SYMOFF(*ex) - GCC_HEADER + 4; /* +4 to get the same result than tools/build */
- +
- + fprintf(stderr, "System size is %d\n", sz);
- +
- + while (sz)
- + {
- + int l, n;
- +
- + l = sz;
- + if (l > sizeof(buf)) l = sizeof(buf);
- +
- + if ((n=read(id, buf, l)) !=l)
- + {
- + if (n == -1)
- + perror(argv[1]);
- + else
- + fprintf(stderr, "Unexpected EOF\n");
- +
- + die("Can't read system");
- + }
- +
- + write(1, buf, l);
- + sz -= l;
- + }
- +
- + close(id);
- + return(0);
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/boot/install.sh linux/arch/arm/boot/install.sh
- --- linux.orig/arch/arm/boot/install.sh Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/boot/install.sh Wed May 8 22:00:32 1996
- @@ -0,0 +1,60 @@
- +#!/bin/sh
- +#
- +# arch/i386/boot/install.sh
- +#
- +# This file is subject to the terms and conditions of the GNU General Public
- +# License. See the file "COPYING" in the main directory of this archive
- +# for more details.
- +#
- +# Copyright (C) 1995 by Linus Torvalds
- +#
- +# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin
- +#
- +# "make install" script for i386 architecture
- +#
- +# Arguments:
- +# $1 - kernel version
- +# $2 - kernel image file
- +# $3 - kernel map file
- +# $4 - default install path (blank if root directory)
- +#
- +
- +# User may have a custom install script
- +
- +if [ -x /sbin/installkernel ]; then
- + exec /sbin/installkernel "$@"
- +fi
- +
- +if [ "$2" = "zImage" ]; then
- +# Compressed install
- + echo "Installing compressed kernel"
- + if [ -f $4/vmlinuz-$1 ]; then
- + mv $4/vmlinuz-$1 $4/vmlinuz.old
- + fi
- +
- + if [ -f $4/System.map-$1 ]; then
- + mv $4/System.map-$1 $4/System.old
- + fi
- +
- + cat $2 > $4/vmlinuz-$1
- + cp $3 $4/System.map-$1
- +else
- +# Normal install
- + echo "Installing normal kernel"
- + if [ -f $4/vmlinux-$1 ]; then
- + mv $4/vmlinux-$1 $4/vmlinux.old
- + fi
- +
- + if [ -f $4/System.map ]; then
- + mv $4/System.map $4/System.old
- + fi
- +
- + cat $2 > $4/vmlinux-$1
- + cp $3 $4/System.map
- +fi
- +
- +if [ -x /sbin/loadmap ]; then
- + /sbin/loadmap --rdev /dev/ima
- +else
- + echo "You have to install it yourself"
- +fi
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/boot/tools/build.c linux/arch/arm/boot/tools/build.c
- --- linux.orig/arch/arm/boot/tools/build.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/boot/tools/build.c Sun Feb 11 22:01:19 1996
- @@ -0,0 +1,56 @@
- +#include <stdio.h>
- +#include <stdlib.h>
- +#include <a.out.h>
- +#include <string.h>
- +
- +int main(int argc, char **argv)
- +{
- + void *data;
- + struct exec ex;
- + FILE *f;
- + int totlen;
- +
- + if (argc < 2) {
- + fprintf(stderr, "Usage: build kernel-name\n");
- + exit(1);
- + }
- +
- + f = fopen(argv[1], "rb");
- +
- + fread(&ex, 1, sizeof(ex), f);
- +
- + if(N_MAGIC(ex) == ZMAGIC) {
- + fseek(f, 4096, SEEK_SET);
- + totlen = ex.a_text + ex.a_data + ex.a_bss;
- + } else
- + if(N_MAGIC(ex) == QMAGIC) {
- + unsigned long my_header[8];
- +
- + fseek(f, 0x20, SEEK_SET);
- +
- + memset(my_header, 0, 0x20);
- +
- + my_header[0] = 0xea000006;
- +
- + fwrite(my_header, 4, 8, stdout);
- +
- + totlen = ex.a_text + ex.a_data + ex.a_bss - 0x20;
- + } else {
- + fprintf(stderr, "Unacceptable a.out header on kernel\n");
- + fclose(f);
- + exit(1);
- + }
- +
- + fprintf(stderr, "Kernel is %dk (%dk text, %dk data, %dk bss)\n",
- + (ex.a_text + ex.a_data + ex.a_bss)/1024,
- + ex.a_text/1024, ex.a_data/1024, ex.a_bss/1024);
- +
- + data = malloc(totlen);
- + fread(data, 1, totlen, f);
- + fwrite(data, 1, totlen, stdout);
- +
- + free(data);
- + fclose(f);
- + fflush(stdout);
- + return 0;
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/config.in linux/arch/arm/config.in
- --- linux.orig/arch/arm/config.in Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/config.in Wed Jul 31 13:35:08 1996
- @@ -0,0 +1,89 @@
- +#
- +# For a description of the syntax of this configuration file,
- +# see the Configure script.
- +#
- +# architecture in arch/arm/Makefile'
- +
- +mainmenu_name "Linux kernel configuration"
- +
- +define_bool CONFIG_ARM y
- +
- +mainmenu_option next_comment
- +comment 'Code maturity level options'
- +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
- +endmenu
- +
- +mainmenu_option next_comment
- +comment 'Loadable module support'
- +bool 'Enable loadable module support' CONFIG_MODULES
- +if [ "$CONFIG_MODULES" = "y" ]; then
- + bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
- + bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD
- +fi
- +endmenu
- +
- +mainmenu_option next_comment
- +comment 'General setup'
- +bool 'Networking support' CONFIG_NET
- +bool 'System V IPC' CONFIG_SYSVIPC
- +define_bool CONFIG_BINFMT_AOUT y
- +define_bool CONFIG_BINFMT_ELF n
- +define_bool CONFIG_BINFMT_JAVA n
- +endmenu
- +
- +source arch/arm/drivers/block/Config.in
- +
- +if [ "$CONFIG_NET" = "y" ]; then
- + source net/Config.in
- +fi
- +
- +mainmenu_option next_comment
- +comment 'SCSI support'
- +
- +tristate 'SCSI support?' CONFIG_SCSI
- +
- +if [ "$CONFIG_SCSI" != "n" ]; then
- + source arch/arm/drivers/scsi/Config.in
- +fi
- +endmenu
- +
- +if [ "$CONFIG_NET" = "y" ]; then
- + mainmenu_option next_comment
- + comment 'Network device support'
- +
- + bool 'Network device support?' CONFIG_NETDEVICES
- + if [ "$CONFIG_NETDEVICES" = "y" ]; then
- + source arch/arm/drivers/net/Config.in
- + fi
- + 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
- +
- +source arch/arm/drivers/char/Config.in
- +
- +mainmenu_option next_comment
- +comment 'Sound'
- +
- +bool 'Sound support' CONFIG_SOUND
- +endmenu
- +
- +mainmenu_option next_comment
- +comment 'Kernel hacking'
- +
- +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
- +bool 'Kernel profiling support' CONFIG_PROFILE
- +if [ "$CONFIG_PROFILE" = "y" ]; then
- + int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
- +fi
- +endmenu
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/defconfig linux/arch/arm/defconfig
- --- linux.orig/arch/arm/defconfig Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/defconfig Tue Jul 30 17:17:14 1996
- @@ -0,0 +1,164 @@
- +#
- +# Automatically generated make config: don't edit
- +#
- +CONFIG_ARM=y
- +
- +#
- +# Code maturity level options
- +#
- +# CONFIG_EXPERIMENTAL is not set
- +
- +#
- +# Loadable module support
- +#
- +CONFIG_MODULES=y
- +CONFIG_MODVERSIONS=y
- +# CONFIG_KERNELD is not set
- +
- +#
- +# General setup
- +#
- +CONFIG_NET=y
- +CONFIG_SYSVIPC=y
- +CONFIG_BINFMT_AOUT=y
- +# CONFIG_BINFMT_ELF is not set
- +# CONFIG_BINFMT_JAVA is not set
- +
- +#
- +# Floppy, IDE, and other block devices
- +#
- +CONFIG_BLK_DEV_FD=y
- +CONFIG_BLK_DEV_IDE=y
- +
- +#
- +# Please see Documentation/ide.txt for help/info on IDE drives
- +#
- +# CONFIG_BLK_DEV_IDECD is not set
- +# CONFIG_BLK_DEV_IDETAPE is not set
- +# CONFIG_BLK_DEV_IDE_CARDS is not set
- +# CONFIG_BLK_DEV_XD is not set
- +
- +#
- +# Additional Block Devices
- +#
- +# CONFIG_BLK_DEV_LOOP is not set
- +# CONFIG_BLK_DEV_MD is not set
- +# CONFIG_BLK_DEV_RAM is not set
- +CONFIG_BLK_DEV_IMG=y
- +
- +#
- +# Networking options
- +#
- +# CONFIG_FIREWALL is not set
- +# CONFIG_NET_ALIAS is not set
- +CONFIG_INET=y
- +# CONFIG_IP_FORWARD is not set
- +# CONFIG_IP_MULTICAST is not set
- +# CONFIG_IP_ACCT is not set
- +# CONFIG_IP_ROUTER is not set
- +# CONFIG_NET_IPIP is not set
- +
- +#
- +# (it is safe to leave these untouched)
- +#
- +# CONFIG_INET_PCTCP is not set
- +# CONFIG_INET_RARP is not set
- +# CONFIG_NO_PATH_MTU_DISCOVERY is not set
- +# CONFIG_IP_NOSR is not set
- +# CONFIG_SKB_LARGE is not set
- +
- +#
- +#
- +#
- +# CONFIG_IPX is not set
- +# CONFIG_ATALK is not set
- +# CONFIG_AX25 is not set
- +# CONFIG_BRIDGE is not set
- +# CONFIG_NETLINK is not set
- +
- +#
- +# SCSI support
- +#
- +# CONFIG_SCSI is not set
- +
- +#
- +# SCSI support type (disk, tape, CDrom)
- +#
- +# CONFIG_BLK_DEV_SD is not set
- +# CONFIG_CHR_DEV_ST is not set
- +# CONFIG_BLK_DEV_SR is not set
- +# CONFIG_CHR_DEV_SG is not set
- +
- +#
- +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
- +#
- +# CONFIG_SCSI_MULTI_LUN is not set
- +# CONFIG_SCSI_CONSTANTS is not set
- +
- +#
- +# SCSI low-level drivers
- +#
- +# CONFIG_SCSI_ACORNSCSI_3 is not set
- +# CONFIG_SCSI_CUMANA_1 is not set
- +# CONFIG_SCSI_ECOSCSI is not set
- +# CONFIG_SCSI_OAK is not set
- +
- +#
- +# Network device support
- +#
- +CONFIG_NETDEVICES=y
- +CONFIG_DUMMY=y
- +# CONFIG_EQUALIZER is not set
- +CONFIG_PPP=y
- +
- +#
- +# CCP compressors for PPP are only built as modules.
- +#
- +# CONFIG_SLIP is not set
- +# CONFIG_ETHER1 is not set
- +# CONFIG_ETHER3 is not set
- +
- +#
- +# Filesystems
- +#
- +# CONFIG_QUOTA is not set
- +# CONFIG_LOCK_MANDATORY is not set
- +CONFIG_MINIX_FS=y
- +# CONFIG_EXT_FS is not set
- +CONFIG_EXT2_FS=y
- +# CONFIG_XIA_FS is not set
- +CONFIG_FAT_FS=y
- +CONFIG_MSDOS_FS=y
- +# CONFIG_VFAT_FS is not set
- +# CONFIG_UMSDOS_FS is not set
- +CONFIG_PROC_FS=y
- +CONFIG_NFS_FS=y
- +# CONFIG_ROOT_NFS is not set
- +CONFIG_SMB_FS=m
- +CONFIG_SMB_LONG=y
- +# CONFIG_ISO9660_FS is not set
- +# CONFIG_HPFS_FS is not set
- +# CONFIG_SYSV_FS is not set
- +# CONFIG_AFFS_FS is not set
- +# CONFIG_UFS_FS is not set
- +
- +#
- +# Character devices
- +#
- +# CONFIG_SERIAL_ECHO is not set
- +CONFIG_SERIAL=y
- +CONFIG_ATOMWIDE_SERIAL=y
- +# CONFIG_DUALSP_SERIAL is not set
- +CONFIG_MOUSE=y
- +CONFIG_KBDMOUSE=y
- +CONFIG_PRINTER=y
- +
- +#
- +# Sound
- +#
- +CONFIG_SOUND=y
- +
- +#
- +# Kernel hacking
- +#
- +# CONFIG_PROFILE is not set
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/Makefile linux/arch/arm/drivers/Makefile
- --- linux.orig/arch/arm/drivers/Makefile Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/Makefile Thu Mar 28 11:51:08 1996
- @@ -0,0 +1,37 @@
- +#
- +# Makefile for the linux kernel device drivers.
- +#
- +# Note! Dependencies are done automagically by 'make dep', which also
- +# removes any old dependencies. DON'T put your own dependencies here
- +# unless it's something special (ie not a .c file).
- +#
- +# Note 2! The CFLAGS definitions are now in the main makefile...
- +
- +SUB_DIRS := block char net #streams
- +MOD_SUB_DIRS := $(SUB_DIRS)
- +ALL_SUB_DIRS := $(SUB_DIRS) scsi sound
- +
- +# If CONFIG_SCSI is set, the core of scsi support will be added to the kernel,
- +# but some of the low-level things may also be modules.
- +ifeq ($(CONFIG_SCSI),y)
- +SUB_DIRS += scsi
- +MOD_SUB_DIRS += scsi
- +else
- + ifeq ($(CONFIG_SCSI),m)
- + MOD_SUB_DIRS += scsi
- + endif
- +endif
- +
- +ifeq ($(CONFIG_SOUND),y)
- +SUB_DIRS += sound
- +else
- + ifeq ($(CONFIG_SOUND),m)
- + MOD_SUB_DIRS += sound
- + endif
- +endif
- +
- +include $(TOPDIR)/Rules.make
- +
- +mrproper:
- + set -e; for i in $(ALL_SUB_DIRS); do $(MAKE) -C $$i mrproper; done
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/Config.in linux/arch/arm/drivers/block/Config.in
- --- linux.orig/arch/arm/drivers/block/Config.in Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/Config.in Fri Aug 9 22:51:44 1996
- @@ -0,0 +1,43 @@
- +#
- +# Block device driver configuration
- +#
- +mainmenu_option next_comment
- +comment 'Floppy, IDE, and other block devices'
- +
- +tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
- +bool 'Enhanced IDE disk/cdrom/tape support' CONFIG_BLK_DEV_IDE
- +comment 'Please see Documentation/ide.txt for help/info on IDE drives'
- +if [ "$CONFIG_BLK_DEV_IDE" = "n" ]; then
- + bool 'Use Old IDE harddisk driver' CONFIG_BLK_DEV_HD_ONLY
- +else
- + bool ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD
- + bool ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE
- + bool ' Support expansion card IDE interfaces' CONFIG_BLK_DEV_IDE_CARDS
- + if [ "$CONFIG_BLK_DEV_IDE_CARDS" = "y" ]; then
- + bool ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICS
- + fi
- +fi
- +
- +tristate 'MFM harddisk support' CONFIG_BLK_DEV_XD
- +
- +comment 'Additional Block Devices'
- +
- +tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
- +bool 'Multiple device driver support' CONFIG_BLK_DEV_MD
- +if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
- + tristate ' Linear (append) mode' CONFIG_MD_LINEAR
- + tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED
- +fi
- +tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
- +if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
- + bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD
- +fi
- +bool 'ADFS image file support' CONFIG_BLK_DEV_IMG
- +
- +if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then
- + define_bool CONFIG_BLK_DEV_HD y
- +else
- + define_bool CONFIG_BLK_DEV_HD n
- +fi
- +
- +endmenu
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/Makefile linux/arch/arm/drivers/block/Makefile
- --- linux.orig/arch/arm/drivers/block/Makefile Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/Makefile Sun Aug 25 15:35:26 1996
- @@ -0,0 +1,148 @@
- +#
- +# Makefile for the kernel block device drivers.
- +#
- +# Note! Dependencies are done automagically by 'make dep', which also
- +# removes any old dependencies. DON'T put your own dependencies here
- +# unless it's something special (ie not a .c file).
- +#
- +# Note 2! The CFLAGS definition is now inherited from the
- +# parent makefile.
- +#
- +
- +all: links first_rule
- +
- +L_TARGET := block.a
- +L_OBJS := ll_rw_blk.o genhd.o
- +M_OBJS :=
- +MOD_LIST_NAME := BLOCK_MODULES
- +
- +LK = README.fd README.hd README.ide README.md \
- + ide_modes.h ide-cd.c ide-tape.c ide-tape.h \
- + linear.c loop.c md.c raid0.c rd.c
- +
- +# Architecture dependencies
- +
- +ifeq ($(MACHINE),a5k)
- + FLOPPY = floppy.o floppydma.o
- + FLOPPYMOD = floppy-mod.o
- +endif
- +
- +ifeq ($(MACHINE),arc)
- + FLOPPY = fd1772.o fd1772dma.o
- + FLOPPYMOD = fd1772-mod.o
- +endif
- +
- +# Common dependencies
- +
- +ifdef FLOPPY
- + ifeq ($(CONFIG_BLK_DEV_FD),y)
- + L_OBJS += $(FLOPPY)
- + else
- + ifeq ($(CONFIG_BLK_DEV_FD),m)
- + M_OBJS += $(FLOPPYMOD)
- + endif
- + endif
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_IMG),y)
- + L_OBJS += adfsimg.o adfspart.o
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_RAM),y)
- + L_OBJS += rd.o
- +else
- + ifeq ($(CONFIG_BLK_DEV_RAM),m)
- + M_OBJS += rd.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_LOOP),y)
- + L_OBJS += loop.o
- +else
- + ifeq ($(CONFIG_BLK_DEV_LOOP),m)
- + M_OBJS += loop.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_HD),y)
- + L_OBJS += hd.o
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_IDE),y)
- + L_OBJS += ide.o
- +endif
- +
- +ifeq (*(CONFIG_BLK_DEV_IDE_ICS),y)
- + L_OBJS += ide-ics.o
- +else
- + ifeq ($(CONFIG_BLK_DEV_IDE_ICS),m)
- + M_OBJS += ide-ics.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_IDECD),y)
- + L_OBJS += ide-cd.o
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_IDETAPE),y)
- + L_OBJS += ide-tape.o
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_XD),y)
- + L_OBJS += mfmhd.o mfm.o
- +else
- + ifeq ($(CONFIG_BLK_DEV_XD),m)
- + M_OBJS += mfmhd_mod.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_BLK_DEV_MD),y)
- + LX_OBJS += md.o
- + ifeq ($(CONFIG_MD_LINEAR),y)
- + L_OBJS += linear.o
- + else
- + ifeq ($(CONFIG_MD_LINEAR),m)
- + M_OBJS += linear.o
- + endif
- + endif
- + ifeq ($(CONFIG_MD_STRIPED),y)
- + L_OBJS += raid0.o
- + else
- + ifeq ($(CONFIG_MD_STRIPED),m)
- + M_OBJS += raid0.o
- + endif
- + endif
- +endif
- +
- +include $(TOPDIR)/Rules.make
- +
- +fastdep: links
- +
- +# Most modules require to be linked against GCCLIB.
- +
- +floppy_mod.o: $(FLOPPY)
- + ld -r -o $@ $(FLOPPY) $(GCCLIB)
- +
- +fd1772_mod.o: $(FLOPPY)
- + ld -r -o $@ $(FLOPPY) $(GCCLIB)
- +
- +mfmhd_mod.o: mfmhd.o mfm.o
- + ld -r -o $@ mfmhd.o mfm.o $(GCCLIB)
- +
- +.PHONY: links
- +links:
- + -@for f in $(LK); do \
- + if [ ! -e $$f ]; then \
- + echo "ln -s ../../../../drivers/block/$$f ."; \
- + ln -s ../../../../drivers/block/$$f .; \
- + fi; \
- + done
- +
- +mrproper:
- + -@for f in $(LK); do \
- + if [ -L $$f ]; then \
- + echo $(RM) $$f; \
- + $(RM) $$f; \
- + fi; \
- + done
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/adfs.h linux/arch/arm/drivers/block/adfs.h
- --- linux.orig/arch/arm/drivers/block/adfs.h Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/adfs.h Tue Apr 2 19:36:15 1996
- @@ -0,0 +1,75 @@
- +/*
- + * linux/arch/arm/drivers/block/adfs.h
- + *
- + * Copyright (c) 1996 Russell King.
- + */
- +
- +/*
- + * Disc record size
- + */
- +#define RECSIZE 60
- +
- +/*
- + * Disc record
- + */
- +struct disc_record {
- + unsigned char log2secsize;
- + unsigned char secspertrack;
- + unsigned char heads;
- + unsigned char density;
- + unsigned char idlen;
- + unsigned char log2bpmb;
- + unsigned char skew;
- + unsigned char bootoption;
- + unsigned char lowsector;
- + unsigned char nzones;
- + unsigned short zone_spare;
- + unsigned long root;
- + unsigned long disc_size;
- + unsigned short disc_id;
- + unsigned char disc_name[10];
- + unsigned long disc_type;
- +};
- +
- +/*
- + * Partition types.
- + */
- +#define PARTITION_RISCIX 1
- +
- +/*
- + * New-directory disks have a separate attributes byte - this is the bit mask.
- + */
- +#define OBJ_OWNREAD 1
- +#define OBJ_OWNWRITE 2
- +#define OBJ_LOCKED 4
- +#define OBJ_DIRECTORY 8
- +#define OBJ_EXECUTE 16
- +#define OBJ_PUBREAD 32
- +#define OBJ_PUBWRITE 64
- +
- +/*
- + * Offset in bytes of the boot block on the disk.
- + */
- +#define BOOT_SECTOR_ADDRESS 0xc00
- +
- +/*
- + * Calculate the boot block checksum on an ADFS drive. Note that this will
- + * appear to be correct if the sector contains all zeros, so also check that
- + * the disk size is non-zero!!!
- + */
- +extern inline int adfs_checkbblk (unsigned char *ptr)
- +{
- + unsigned int result;
- +
- + __asm__ ("
- + adds %3, %2, %3
- + sub %3, %3, #1
- +1: ldrb %4, [%3, #-1]!
- + adc %0, %0, %4
- + movs %0, %0, lsl #24
- + mov %0, %0, lsr #24
- + teqs %2, %3
- + bne 1b
- + " : "=r" (result) : "0" (0), "r" (ptr), "r" (512), "r" (0));
- + return result != ptr[511];
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/adfsimg.c linux/arch/arm/drivers/block/adfsimg.c
- --- linux.orig/arch/arm/drivers/block/adfsimg.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/adfsimg.c Sat Aug 3 23:03:05 1996
- @@ -0,0 +1,1119 @@
- +/*
- + * linux/arch/arm/drivers/block/adfsimg.c
- + *
- + * Copyright (c) 1996 Russell King.
- + *
- + * Implement image files under Linux (allows Linux to access partition files
- + * on ADFS disks).
- + *
- + * This file is split into two halves: the first half is the actual image
- + * mapper creation code, and the second half is the device driver.
- + *
- + * Changelog:
- + * 28-03-1996 RMK Created
- + * 02-04-1996 RMK Cleaned up per-device data into structure [should
- + * improve code].
- + * 24-06-1996 RMK Marked 4GB problem areas.
- + * Altered blocksize checking code - now uses the larger
- + * of the sector size and map bit size.
- + * 04-06-1996 RMK Re-wrote mapping function. Now applies mapping as
- + * blocks come to be put on the queues.
- + * 06-06-1996 RMK Pass through blocks that need to be split up.
- + */
- +
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +#include <linux/string.h>
- +#include <linux/ctype.h>
- +#include <linux/malloc.h>
- +#include <linux/blkdev.h>
- +#include <linux/errno.h>
- +#include <linux/genhd.h>
- +#include <linux/hdreg.h>
- +
- +#include <asm/segment.h>
- +#include <asm/bitops.h>
- +
- +#define MAJOR_NR ADFSIMG_MAJOR
- +#include "blk.h"
- +#include "adfs.h"
- +
- +/*----------------------------------------------------------------------------------------
- + * User configuration
- + *
- + * Number of minors per device, and number of devices. Note that we have
- + * a limit of 256 on the number of minors at the moment.
- + */
- +#define ADFSIMG_SHIFT 5
- +#define ADFSIMG_NRDEV (256 / (1 << ADFSIMG_SHIFT))
- +#define ADFSIMG_PARMASK ((1 << ADFSIMG_SHIFT) - 1)
- +/*
- + * This maps a device onto an index into the adfsimage structure
- + */
- +#define TARGETMAP(kdev) DEVICE_NR(kdev)
- +/*
- + * Debugging contants...
- + */
- +#define DEBUG_SWAP 32
- +#define DEBUG_CHANGED 16
- +#define DEBUG_MAPPING 8
- +#define DEBUG_BUFFERS 4
- +#define DEBUG_REQUESTS 2
- +#define DEBUG_ADDMAPPING 1
- +#define DEBUG (DEBUG_CHANGED)
- +
- +/*
- + * This is left over from the old code - the old !Config filenames for the files
- + * get copied into this array.
- + */
- +static char arc_hd_files[8][128];
- +
- +struct adfsimage {
- + kdev_t dev; /* device on which to look for filename */
- + char *filename; /* adfs filename without the 'fsname::drive.$.' */
- + /*
- + * Private data
- + */
- + kdev_t mapdev; /* Current device map array is associated with */
- + struct map *mapstrt; /* Map array */
- + unsigned int mapsize; /* number of map entries - 1 */
- + unsigned int mapalloced; /* Size of map array */
- + unsigned int blocksize; /* blocksize this device supports */
- + unsigned int accesscount; /* number of times this has been opened */
- + unsigned int capacity; /* number of sectors in this image file */
- + struct request *req; /* Currently executing request */
- + struct file filp; /* fake filp for open device */
- + struct inode inode; /* fake inode for open device */
- + unsigned char busy:1, /* Device is currently busy */
- + changed:1; /* Mapped device has been changed */
- +} adfsimg_images[ADFSIMG_NRDEV] = {
- + { 0, arc_hd_files[0], 0x000, NULL, 0, 0, 0, 0, 0, NULL, {0, }, {0, }, 0, 0 },
- + { 0, arc_hd_files[1], 0x000, NULL, 0, 0, 0, 0, 0, NULL, {0, }, {0, }, 0, 0 },
- + { 0, arc_hd_files[2], 0x000, NULL, 0, 0, 0, 0, 0, NULL, {0, }, {0, }, 0, 0 },
- + { 0, arc_hd_files[3], 0x000, NULL, 0, 0, 0, 0, 0, NULL, {0, }, {0, }, 0, 0 },
- + { 0, arc_hd_files[4], 0x000, NULL, 0, 0, 0, 0, 0, NULL, {0, }, {0, }, 0, 0 },
- + { 0, arc_hd_files[5], 0x000, NULL, 0, 0, 0, 0, 0, NULL, {0, }, {0, }, 0, 0 },
- + { 0, arc_hd_files[6], 0x000, NULL, 0, 0, 0, 0, 0, NULL, {0, }, {0, }, 0, 0 },
- + { 0, arc_hd_files[7], 0x000, NULL, 0, 0, 0, 0, 0, NULL, {0, }, {0, }, 0, 0 }
- +};
- +
- +void adfsimg_add_image (int slot, kdev_t dev, char *path)
- +{
- + adfsimg_images[slot].dev = dev;
- + strcpy (adfsimg_images[slot].filename, path);
- +}
- +
- +struct map {
- + unsigned long strt_sblk;
- + unsigned long strt_dblk;
- + unsigned long length;
- +};
- +
- +static struct wait_queue *busy_wait;
- +
- +/*
- + * These are private to the map generation code
- + */
- +static void *_adfsimg_map;
- +static struct disc_record adfsimg_dr;
- +static int adfsimg_zonesize;
- +static int adfsimg_idsperzone;
- +static int adfsimg_maplen;
- +
- +/*
- + * Lock a device - prevents opens/closes/revalidates all happening at the same time.
- + */
- +static inline void
- +adfsimg_lockdev (struct adfsimage *img)
- +{
- + while (img->busy) {
- + barrier ();
- + sleep_on (&busy_wait);
- + }
- + img->busy = 1;
- +}
- +
- +static inline void
- +adfsimg_unlockdev (struct adfsimage *img)
- +{
- + img->busy = 0;
- + wake_up (&busy_wait);
- +}
- +
- +/*
- + * read blocks to an area of memory. [eg map, boot block etc].
- + */
- +static void *
- +adfsimg_read (void *data, struct adfsimage *img, unsigned long blkaddr, int length)
- +{
- + struct buffer_head *bh;
- + unsigned char *p;
- +
- + for (p = (unsigned char *)data; length > 0; blkaddr ++, length -= img->blocksize) {
- + bh = breada (img->dev, blkaddr, img->blocksize, 0, PAGE_SIZE);
- +
- + if (!bh)
- + return NULL;
- + memcpy (p, bh->b_data, img->blocksize);
- + p += img->blocksize;
- + brelse (bh);
- + }
- +
- + return data;
- +}
- +
- +/*
- + * Calculate block number for zone, zone index.
- + */
- +static inline unsigned long
- +adfsimg_calcblockno (struct adfsimage *img, unsigned int zone, unsigned int start)
- +{
- + /* *** 4GB LIMITATION ALERT! *** */
- + return (((zone ? zone * adfsimg_zonesize - RECSIZE * 8 : 0) + start) << adfsimg_dr.log2bpmb) /
- + img->blocksize;
- +}
- +
- +static inline void
- +adfsimg_addmapping (struct adfsimage *img, unsigned int zone, unsigned int start, unsigned int len)
- +{
- + struct map newmap;
- +
- + /*
- + * totaladdr - starting block number for this segment
- + * startaddr - sector on media for start
- + * lengthaddr - length in sectors of this block
- + */
- +#if DEBUG & DEBUG_ADDMAPPING
- + printk ("Z: %8d S: %8d E: %8d ", zone, start, start + len);
- +#endif
- + newmap.strt_sblk = 0;
- + newmap.strt_dblk = adfsimg_calcblockno (img, zone, start);
- + /* *** 4GB LIMITATION ALERT! *** */
- + newmap.length = (len << adfsimg_dr.log2bpmb) / img->blocksize;
- +
- + if (!img->mapstrt) {
- + img->mapstrt = kmalloc (5 * sizeof (newmap), GFP_KERNEL);
- + img->mapsize = 0; /* Size - 1 */
- + img->mapalloced = 5; /* allocated size */
- + memcpy (img->mapstrt, &newmap, sizeof (newmap));
- + } else {
- + struct map *mp;
- + mp = img->mapstrt + img->mapsize;
- + if (mp->strt_dblk + mp->length == newmap.strt_dblk)
- + mp->length += newmap.length;
- + else
- + if (img->mapsize < img->mapalloced - 1) {
- + newmap.strt_sblk = mp->strt_sblk + mp->length;
- + img->mapsize ++;
- + memcpy (&img->mapstrt[img->mapsize], &newmap, sizeof (newmap));
- + } else {
- + newmap.strt_sblk = mp->strt_sblk + mp->length;
- + mp = kmalloc ((img->mapalloced + 5) * sizeof (newmap), GFP_KERNEL);
- + img->mapalloced += 5;
- + img->mapsize ++;
- + memcpy (mp, img->mapstrt, img->mapsize * sizeof (newmap));
- + kfree (img->mapstrt);
- + img->mapstrt = mp;
- + memcpy (&img->mapstrt[img->mapsize], &newmap, sizeof (newmap));
- + }
- + }
- +#if DEBUG & DEBUG_ADDMAPPING
- + printk ("SSB: %08lX STB: %08lX LEN: %08lX\n", img->mapstrt[img->mapsize].strt_sblk,
- + img->mapstrt[img->mapsize].strt_dblk, img->mapstrt[img->mapsize].length);
- +#endif
- +}
- +
- +static unsigned long
- +adfsimg_scanmap (struct adfsimage *img, unsigned long internaladdr, int objlength, int simple)
- +{
- + unsigned int fragment, zone, startzone, sectoroff, mapoff, found;
- +
- + fragment = (int)internaladdr >> 8;
- + startzone = zone = fragment / adfsimg_idsperzone;
- + sectoroff = (internaladdr & 0xff) ? (internaladdr & 0xff) - 1 : 0;
- +/*
- + * Problem area: if log2secsize < log2bpmb, then the map offset calculation
- + * breaks horribly. However, can an internal address refer to a non-allocation
- + * unit sector offset?
- + *
- + * Ideas:
- + * if (adfsimg_dr.log2secsize > adfsimg_dr.log2bpmb)
- + * mapoff = sectoroff << (adfsimg_dr.log2secsize - adfsimg_dr.log2bpmb);
- + * else
- + * mapoff = sectoroff >> (adfsimg_dr.log2bpmb - adfsimg_dr.log2secsize);
- + *
- + * shift = adfsimg_dr.log2secsize - adfsimg_dr.log2bpmb;
- + * if (shift < 0)
- + * mapoff = sectoroff >> (- shift);
- + * else
- + * mapoff = sectoroff << shift;
- + */
- + mapoff = sectoroff << (adfsimg_dr.log2secsize - adfsimg_dr.log2bpmb);
- + objlength >>= adfsimg_dr.log2bpmb;
- + found = 0;
- +
- + do {
- + unsigned char const *map;
- + unsigned int bmoff, bmoff_max;
- +
- + map = ((unsigned char *)_adfsimg_map) + (zone << adfsimg_dr.log2secsize) + 4 +
- + (zone == 0 ? RECSIZE : 0);
- + bmoff_max = adfsimg_zonesize - ((zone == 0) ? RECSIZE << 3 : 0);
- + bmoff = 0;
- +
- + while (bmoff < bmoff_max) {
- + unsigned long t;
- + unsigned int n, start;
- + int z;
- +
- + start = bmoff;
- + z = bmoff >> 3;
- +
- + t = ((map[z] | (map[z + 1] << 8) | (map[z + 2] << 16)) >> (bmoff & 7))
- + & ((1 << adfsimg_dr.idlen) - 1);
- + bmoff += adfsimg_dr.idlen;
- +
- + /*
- + * I think that this would be better coded in assembler...
- + */
- + if (bmoff & 7) {
- + n = map[bmoff >> 3] >> (bmoff & 7);
- + if (!n)
- + bmoff = (bmoff + 8) & ~7;
- + else
- + for (; (n & 1) == 0; n >>= 1, bmoff += 1);
- + }
- + if (!(bmoff & 7)) {
- + while ((n = map[bmoff >> 3]) == 0)
- + bmoff += 8;
- +
- + for (; (n & 1) == 0; n >>= 1, bmoff += 1);
- + }
- + bmoff += 1;
- + if (t == fragment) {
- + int length = bmoff - start;
- + if (mapoff) {
- + if (length < mapoff) {
- + mapoff -= length;
- + length = 0;
- + } else {
- + start += mapoff;
- + length -= mapoff;
- + mapoff = 0;
- + }
- + }
- + if (length) {
- + if (objlength < length)
- + length = objlength;
- + objlength -= length;
- + if (simple)
- + return adfsimg_calcblockno (img, zone, start);
- + else
- + adfsimg_addmapping(img, zone, start, length);
- + found += 1;
- + if (!objlength)
- + return found;
- + }
- + }
- + }
- + zone += 1;
- + if (zone == adfsimg_dr.nzones)
- + zone = 0;
- + bmoff = 0;
- + } while (zone != startzone);
- + return found;
- +}
- +
- +static int
- +adfsimg_decenddirs (struct adfsimage *img, int showinfo, unsigned long diraddr, char **ptr)
- +{
- + char *leftptr, *p;
- + char dirname[16];
- + unsigned char *dir;
- + int i, length;
- +
- + dir = kmalloc (2048, GFP_KERNEL);
- + if (!dir)
- + return 0;
- +
- + leftptr = *ptr;
- +
- + while (1) {
- + unsigned char *dirptr;
- + if (!adfsimg_read (dir, img, diraddr, 2048)) {
- + if (showinfo)
- + printk (" dir unreadable");
- + kfree (dir);
- + return 0;
- + }
- + /*
- + * Check that the directory looks feasable
- + */
- + if ((strncmp ((char *)dir + 1, "Nick", 4) && strncmp ((char *)dir + 1, "Hugo", 4)) ||
- + strncmp ((char *)dir + 1, (char *)dir + 2043, 4)) {
- + if (showinfo)
- + printk (" broken dir");
- + kfree (dir);
- + return 0;
- + }
- +
- + p = strchr (leftptr, '.');
- + if (!p)
- + strcpy (dirname, leftptr);
- + else {
- + strncpy (dirname, leftptr, p - leftptr);
- + dirname[p - leftptr] = '\0';
- + leftptr = ++p;
- + }
- +
- + for (dirptr = dir + 5; *dirptr; dirptr += 26) {
- + for (i = 0; i < 10; i++) {
- + char c1, c2;
- + if (dirptr[i] == 0x0d || dirname[i] == '\0')
- + break;
- + c1 = tolower(dirptr[i]);
- + c2 = tolower(dirname[i]);
- + if (c1 != c2)
- + break;
- + }
- + if (dirname[i] == '\0' && (i == 10 || dirptr[i] == 0x0d))
- + break;
- + }
- +
- + if (!*dirptr) {
- + if (showinfo)
- + printk (" %s '%s' not found\n", p ? "dir" : "file", dirname);
- + kfree (dir);
- + return 0;
- + }
- +
- + /*
- + * Check that we're not referencing a file when we mean a directory or
- + * vice versa.
- + */
- + if ((dirptr[25] & OBJ_DIRECTORY && !p) || (!(dirptr[25] & OBJ_DIRECTORY) && p)) {
- + if (showinfo)
- + printk (" %s '%s' is not a %s\n", p ? "file" : "dir", dirname, p ? "dir" : "file");
- + kfree (dir);
- + return 0;
- + }
- +
- + length = (unsigned long) dirptr[18] | (dirptr[19] << 8) |
- + (dirptr[20] << 16) | (dirptr[21] << 24);
- + diraddr = (unsigned long) dirptr[22] | (dirptr[23] << 8) | (dirptr[24] << 16);
- + diraddr = adfsimg_scanmap (img, diraddr, length, p ? 1 : 0);
- + if (!diraddr) {
- + if (showinfo)
- + printk (" internal error");
- + kfree (dir);
- + return 0;
- + }
- + if (!p) {
- + /*
- + * We must have come to the end of the string. This entry is the
- + * filename.
- + */
- + kfree (dir);
- + return 1;
- + }
- + }
- +}
- +
- +/*
- + * Read in either map, and check the cross-check. If the first map
- + * has an invalid cross-check, try the other map.
- + */
- +static unsigned long
- +adfsimg_readmap (struct adfsimage *img, int showinfo)
- +{
- + unsigned int map_no, bad, maplen;
- + unsigned long mapaddr;
- +
- + /* *** 4GB LIMITATION ALERT! *** */
- + mapaddr = (((adfsimg_dr.nzones >> 1) * adfsimg_zonesize -
- + ((adfsimg_dr.nzones > 1) ? RECSIZE * 8 : 0)) << adfsimg_dr.log2bpmb) /
- + img->blocksize;
- + adfsimg_maplen = adfsimg_dr.nzones << adfsimg_dr.log2secsize;
- + maplen = (adfsimg_maplen | (img->blocksize - 1)) / img->blocksize;
- +
- + if (!(_adfsimg_map = vmalloc (maplen * img->blocksize))) {
- + if (showinfo)
- + printk (" no mem");
- + return 0;
- + }
- +
- + /*
- + * We try to read the first map.
- + */
- + for (map_no = 0; map_no < 2; map_no ++) {
- + unsigned long blk_strt;
- + unsigned int off, chk;
- + unsigned char *map;
- +
- + /*
- + * If img->blocksize > 1<<adfsimg_dr.log2secsize, then we could
- + * have problems here when trying the second map.
- + */
- + blk_strt = mapaddr + (map_no ? maplen : 0);
- + bad = 0;
- +
- + if (!adfsimg_read (_adfsimg_map, img, blk_strt, maplen * img->blocksize)) {
- + bad = 1;
- + if (showinfo)
- + printk (" map %d unreadable", map_no);
- + break;
- + }
- +
- + map = (unsigned char *)_adfsimg_map;
- + for (off = 3, chk = 0; off < (adfsimg_dr.nzones << adfsimg_dr.log2secsize);
- + off += 1 << adfsimg_dr.log2secsize)
- + chk ^= map[off];
- +
- + if (chk != 0xff) {
- + bad = 1;
- + if (showinfo)
- + printk (" map %d corrupt", map_no + 1);
- + } else
- + break;
- + }
- + if (bad)
- + vfree (_adfsimg_map);
- +
- + return bad ? 0 : mapaddr + 2 * maplen;
- +}
- +
- +/*
- + * Read in the boot sector, and validate it. Also validate the
- + * block size that we are using.
- + */
- +static struct disc_record *
- +adfsimg_readbootsect (struct adfsimage *img, int showinfo)
- +{
- + unsigned char *boot_sectors;
- + int allocation_unit_size;
- + struct buffer_head *bh;
- +
- + if (!(bh = bread (img->dev, BOOT_SECTOR_ADDRESS / img->blocksize,
- + img->blocksize > 512 ? img->blocksize : 512))) {
- + if (showinfo)
- + printk (" bread failed");
- + return NULL;
- + }
- +
- + boot_sectors = bh->b_data + BOOT_SECTOR_ADDRESS % img->blocksize;
- +
- + if (adfs_checkbblk (boot_sectors)) {
- + if (showinfo)
- + printk (" non-adfs");
- + brelse (bh);
- + return NULL;
- + }
- +
- + memcpy (&adfsimg_dr, boot_sectors + 0x1c0, sizeof (adfsimg_dr));
- + brelse (bh);
- +
- + /*
- + * The blocksize must not be larger than the sector size specified
- + * in the boot sector, or else all this code breaks.
- + */
- + if (adfsimg_dr.log2secsize > adfsimg_dr.log2bpmb)
- + allocation_unit_size = 1 << adfsimg_dr.log2secsize;
- + else
- + allocation_unit_size = 1 << adfsimg_dr.log2bpmb;
- + if (img->blocksize > allocation_unit_size) {
- + if (showinfo)
- + printk (" allocation unit size too large - %d > %d", img->blocksize, allocation_unit_size);
- + return NULL;
- + }
- +
- + return &adfsimg_dr;
- +}
- +
- +static int adfsimg_init_onedisk (int target, int showinfo)
- +{
- + struct adfsimage *img = adfsimg_images + target;
- + unsigned long root_dir_address;
- + unsigned int blocksize;
- + char *p;
- +
- + img->mapdev = MKDEV(UNNAMED_MAJOR, 0);
- + if (MAJOR(img->dev) == ADFSIMG_MAJOR)
- + return 0;
- +
- + if (img->filename == NULL || strlen (img->filename) == 0 || MAJOR(img->dev) == UNNAMED_MAJOR)
- + return 0;
- +
- + if (showinfo)
- + printk (" im%c", target + 'a');
- +
- + /*
- + * Sort out the sector size that we can support. Most devices
- + * default to 1024 byte blocks, and 512 byte sectors.
- + *
- + * TODO:
- + * However, we do check this against the size wanted in the boot
- + * block. If it says that the sector size is 1024, then we might
- + * as well stick with 1024 byte blocks, otherwise switch to 512...
- + *
- + * Change:
- + * We now check the blocksize against the larger of the sector size
- + * and the map bit size.
- + */
- + if (hardsect_size[MAJOR(img->dev)])
- + blocksize = hardsect_size[MAJOR(img->dev)][MINOR(img->dev)];
- + else
- + blocksize = 512;
- +
- + set_blocksize (img->dev, blocksize);
- + img->blocksize = blocksize;
- +
- + /*
- + * Now read in the boot sector. This has to be blocksize
- + * independent.
- + */
- + if (!adfsimg_readbootsect (img, showinfo))
- + return 0;
- +
- + adfsimg_zonesize = (8 << adfsimg_dr.log2secsize) - adfsimg_dr.zone_spare;
- + adfsimg_idsperzone = adfsimg_zonesize / (adfsimg_dr.idlen + 1);
- +
- + if (!(root_dir_address = adfsimg_readmap (img, showinfo)))
- + return 0;
- +
- + p = img->filename;
- + adfsimg_decenddirs (img, showinfo, root_dir_address, &p);
- +
- + if (_adfsimg_map != NULL) {
- + vfree (_adfsimg_map);
- + _adfsimg_map = NULL;
- + }
- +
- + img->mapdev = img->dev;
- +
- + return img->mapstrt[img->mapsize].strt_sblk + img->mapstrt[img->mapsize].length;
- +}
- +
- +/*
- + * Get an index into the mapping array for this 'block'. Once we have found
- + * it, then as we map following blocks, we can just increment the index.
- + */
- +static inline int adfsimg_getindex (struct adfsimage *img, unsigned long block)
- +{
- + unsigned int i, low, high;
- + const struct map *mp;
- +
- + mp = img->mapstrt;
- +
- + if (block >= (mp[img->mapsize].strt_sblk + mp[img->mapsize].length)) {
- + printk ("%s: access requested to block outside device: blk %lX\n",
- + kdevname (img->dev), block);
- + return -1;
- + }
- + for (low = 0, i = (high = img->mapsize) / 2; low != high; i = (high + low) >> 1) {
- + if (block < mp[i].strt_sblk)
- + high = i;
- + else if (block >= mp[i].strt_sblk + mp[i].length)
- + low = i + 1;
- + else break;
- + }
- +#if DEBUG & DEBUG_MAPPING
- + printk ("Map entry: %lX [%lX-%lX]->[%lX-%lX]\n", block,
- + mp[i].strt_sblk, mp[i].strt_sblk + mp[i].length,
- + mp[i].strt_dblk, mp[i].strt_dblk + mp[i].length);
- +#endif
- + return i;
- +}
- +
- +/*========================================================================================
- + * This is the 'device' driver for the image files.
- + */
- +
- +static struct hd_struct adfsimg_parts[256] = { {0, 0}, };
- +
- +/*
- + * Every request that comes in here is a request that needs to be split up into
- + * a maximum of 64 separate requests.
- + */
- +static void adfsimg_request (void)
- +{
- + while (1) {
- + unsigned int target, minor;
- + unsigned long block, length, nsectors, offset;
- + struct adfsimage *img;
- + int index;
- +
- + INIT_REQUEST;
- +
- + target = TARGETMAP(CURRENT->rq_dev);
- + img = adfsimg_images + target;
- +
- + if (MAJOR(img->mapdev) == UNNAMED_MAJOR) {
- + printk (KERN_ERR
- + "im%c: request for mapping to unnamed device\n",
- + target + 'a');
- + end_request (0);
- + continue;
- + }
- +
- + if (img->changed) {
- + end_request (0);
- + continue;
- + }
- +
- + minor = MINOR(CURRENT->rq_dev);
- + block = CURRENT->sector;
- + nsectors = CURRENT->current_nr_sectors;
- + if (block >= adfsimg_parts[minor].nr_sects ||
- + (block + nsectors) > adfsimg_parts[minor].nr_sects) {
- + printk (KERN_ERR
- + "im%c: invalid block/size: minor = %u, block = %lu count = %lu\n",
- + target + 'a', minor, block, nsectors);
- + end_request (0);
- + continue;
- + }
- +
- + block += adfsimg_parts[minor].start_sect;
- + index = adfsimg_getindex (img, block);
- + if (index < 0) {
- + printk (KERN_ERR
- + "im%c: invalid index: block = %lX\n", target + 'a', block);
- + end_request (0);
- + continue;
- + }
- +#if 0
- + printk (KERN_ERR "Received an adfsimg_request (blk %lu len %lu ", block, nsectors);
- +#endif
- + offset = block - img->mapstrt[index].strt_sblk;
- + block = offset + img->mapstrt[index].strt_dblk;
- + length = img->mapstrt[index].length - offset;
- +#if 0
- + printk ("-> blk %lu len %lu), blksz=%d ", block, length, img->blocksize);
- +#endif
- +
- + while (nsectors) {
- + struct buffer_head *bh;
- + bh = getblk (img->mapdev, block, img->blocksize);
- + if (!bh) {
- + printk (KERN_ERR
- + "im%c: getblk(-,%ld,%d) returned NULL",
- + target + 'a', block, img->blocksize);
- + end_request (0);
- + break;
- + }
- + if (!buffer_uptodate(bh) && CURRENT->cmd == READ) {
- + ll_rw_block(READ, 1, &bh);
- + wait_on_buffer(bh);
- + if (!buffer_uptodate(bh)) {
- + brelse(bh);
- +#if 0
- + printk ("error\n");
- +#endif
- + end_request (0);
- + break;
- + }
- + }
- + if (CURRENT->cmd == READ)
- + memcpy (CURRENT->buffer, bh->b_data, img->blocksize);
- + else {
- + memcpy (bh->b_data, CURRENT->buffer, img->blocksize);
- + mark_buffer_uptodate(bh, 1);
- + mark_buffer_dirty(bh, 1);
- + }
- + brelse(bh);
- + CURRENT->buffer += img->blocksize;
- + CURRENT->sector += img->blocksize >> 9;
- + nsectors -= 1;
- + block ++;
- + if (!--length) {
- + index ++;
- + block = img->mapstrt[index].strt_dblk;
- + length = img->mapstrt[index].length;
- + }
- + if (nsectors == 0) {
- +#if 0
- + printk ("ok\n");
- +#endif
- + end_request (1);
- + }
- + }
- + }
- +}
- +
- +static int adfsimg_revalidate_disk (kdev_t dev, int maxusage);
- +/*
- + * TODO:
- + * IOCTL to get mapping filenames
- + * IOCTL to set mapping filenames
- + * - lock device.
- + * - set changed flag on device.
- + * - create new map.
- + * - unlock device.
- + */
- +static int
- +adfsimg_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
- +{
- + struct hd_geometry *loc = (struct hd_geometry *) arg;
- + int err, target;
- +
- + if (!inode || !(inode->i_rdev))
- + return -EINVAL;
- +
- + target = TARGETMAP(inode->i_rdev);
- +
- + if (target >= ADFSIMG_NRDEV)
- + return -ENODEV;
- +
- + switch (cmd) {
- + case HDIO_GETGEO:
- + if (!loc) return -EINVAL;
- + err = verify_area (VERIFY_WRITE, loc, sizeof (*loc));
- + if (err)
- + return err;
- + put_user (4, (char *)&loc->heads);
- + put_user (17, (char *)&loc->sectors);
- + put_user (adfsimg_parts[MINOR(inode->i_rdev)].nr_sects / (17 * 4), (short *)&loc->cylinders);
- + put_user (adfsimg_parts[MINOR(inode->i_rdev)].start_sect, (long *)&loc->start);
- + return 0;
- + case BLKRASET:
- + if (!suser ()) return -EACCES;
- + if (arg > 0xff) return -EINVAL;
- + read_ahead[MAJOR(inode->i_rdev)] = arg;
- + return 0;
- + case BLKRAGET:
- + if (!arg) return -EINVAL;
- + err = verify_area (VERIFY_WRITE, (long *)arg, sizeof (long));
- + if (err)
- + return err;
- + put_user (read_ahead[MAJOR(inode->i_rdev)], (long *)arg);
- + return 0;
- + case BLKGETSIZE: /* Return device size */
- + if (!arg) return -EINVAL;
- + err = verify_area (VERIFY_WRITE, (long *)arg, sizeof (long));
- + if (err)
- + return err;
- + put_user (adfsimg_parts[MINOR(inode->i_rdev)].nr_sects, (long *)arg);
- + return 0;
- + case BLKFLSBUF:
- + if (!suser ()) return -EACCES;
- + fsync_dev (inode->i_rdev);
- + invalidate_buffers (inode->i_rdev);
- + return 0;
- +
- + case BLKRRPART:
- + return adfsimg_revalidate_disk (inode->i_rdev, 1);
- +#if 0
- + case IMGFNGET:
- + return -EINVAL;
- +
- + case IMGFNSET:
- + if (!suser ()) return -EACCES;
- + return -EINVAL;
- +#endif
- +
- + RO_IOCTLS(inode->i_rdev, arg);
- + default:
- + return -EINVAL;
- + }
- +}
- +
- +/*
- + * Open an mapped device. Check for disk changes etc, and open
- + * the real device. This means that if that device wants to, it
- + * can do things like locking the drive door. We force ourself
- + * to be busy while opening the real device.
- + */
- +static int adfsimg_open (struct inode *inode, struct file *filp)
- +{
- + struct adfsimage *img;
- + unsigned int target;
- + int retval = 0;
- +
- + target = TARGETMAP(inode->i_rdev);
- +
- + if (target >= ADFSIMG_NRDEV)
- + return -ENODEV;
- +
- + img = adfsimg_images + target;
- +
- + if (MAJOR(img->mapdev) == UNNAMED_MAJOR)
- + return -ENODEV;
- +#if 1
- + if (filp->f_mode & 2)
- + return -EROFS;
- +#endif
- + check_disk_change (inode->i_rdev);
- +
- + /*
- + * If the device is still changed, then it means that we haven't
- + * been able to revalidate it yet.
- + */
- + if (img->changed)
- + return -ENXIO;
- +
- + adfsimg_lockdev (img);
- +
- + if (img->accesscount == 0) {
- + /* was this the first open? */
- + struct file *d_filp;
- + struct inode *d_inode;
- + int retval;
- +
- + d_filp = &img->filp;
- + d_inode = &img->inode;
- +
- + memset (d_filp, 0, sizeof (*d_filp));
- + memset (d_inode, 0, sizeof (*d_inode));
- +
- + d_inode->i_rdev = img->mapdev;
- + d_filp->f_inode = d_inode;
- + d_filp->f_mode = filp->f_mode;
- +
- + retval = blkdev_open (d_inode, d_filp);
- + }
- +
- + if (retval == 0)
- + img->accesscount ++;
- +
- + adfsimg_unlockdev (img);
- +
- + return retval;
- +}
- +
- +/*
- + * Releasing a block device means we sync() it, so that it can safely
- + * be forgotten about... Close the real device as well if this is the
- + * last release.
- + */
- +static void adfsimg_release (struct inode *inode, struct file *filp)
- +{
- + struct adfsimage *img;
- + unsigned int target;
- +
- + /*
- + * Make this image file busy while we unmount the device (we may sleep).
- + */
- +
- + sync_dev (inode->i_rdev);
- + target = TARGETMAP(inode->i_rdev);
- +
- + if (target >= ADFSIMG_NRDEV) {
- + printk ("im: releasing invalid device %d\n", target);
- + return;
- + }
- +
- + img = adfsimg_images + target;
- +
- + adfsimg_lockdev (img);
- +
- + img->accesscount --;
- + if (img->accesscount == 0 && img->filp.f_op && img->filp.f_op->release)
- + img->filp.f_op->release (&img->inode, NULL);
- +
- + /*
- + * Don't forget to wake up anything on the busy queues.
- + */
- + adfsimg_unlockdev (img);
- +}
- +
- +static void adfsimg_geninit (struct gendisk *dev);
- +
- +static int adfsimg_sizes[256] = { 0, };
- +static int adfsimg_blocksizes[256] = { 0, };
- +static int adfsimg_hardsizes[256] = { 0, };
- +
- +static struct gendisk adfsimg_gendisk = {
- + MAJOR_NR, /* major number */
- + "im", /* major name */
- + ADFSIMG_SHIFT, /* bits to shift to get real from partition */
- + 1 << ADFSIMG_SHIFT, /* number of partitions per real */
- + ADFSIMG_NRDEV, /* maximum number of reals */
- + adfsimg_geninit, /* init function */
- + adfsimg_parts, /* adfsimg_struct */
- + adfsimg_sizes, /* block sizes */
- + 0, /* number */
- + (void *)NULL, /* internal */
- + NULL /* next */
- +};
- +
- +/*
- + * This is the ADFS Image file init entry point
- + */
- +static void adfsimg_geninit (struct gendisk *dev)
- +{
- + int i;
- +
- + adfsimg_gendisk.nr_real = ADFSIMG_NRDEV;
- +
- + for (i = 0; i < 256; i++) {
- + adfsimg_blocksizes[i] = 1024;
- + adfsimg_hardsizes[i] = 512;
- + }
- +
- + blksize_size[MAJOR_NR] = adfsimg_blocksizes;
- + hardsect_size[MAJOR_NR] = adfsimg_hardsizes;
- +
- + printk (" im :");
- + for (i = 0; i < ADFSIMG_NRDEV; i++) {
- + int retval;
- +
- + retval = adfsimg_init_onedisk (i, 1);
- + if (retval > 0) {
- + adfsimg_gendisk.part[i << ADFSIMG_SHIFT].nr_sects = retval;
- + adfsimg_images[i].capacity = retval;
- + adfsimg_images[i].changed = 0;
- + } else {
- + adfsimg_gendisk.part[i << ADFSIMG_SHIFT].start_sect = -1;
- + adfsimg_gendisk.part[i << ADFSIMG_SHIFT].nr_sects = 0;
- + adfsimg_images[i].capacity = retval;
- + adfsimg_images[i].changed = 1;
- + }
- + }
- + printk ("\n");
- +}
- +
- +/*
- + * Check to see if our media that we are mapping has changed, or
- + * the mapped file has been altered. I do wish that there were
- + * upcalls...
- + *
- + * This will always return 1 until:
- + * a) ALL opens on the device have been closed.
- + * b) The device is re-opened.
- + */
- +static int adfsimg_checkmediachange (kdev_t dev)
- +{
- + struct adfsimage *img;
- + int target;
- +
- + target = TARGETMAP(dev);
- +
- + if (target >= ADFSIMG_NRDEV) {
- + printk ("im: adfsimg_checkmediachange: invalid device\n");
- + return 0;
- + }
- +
- + img = adfsimg_images + target;
- +
- + if (MAJOR(img->mapdev) == UNNAMED_MAJOR)
- + return 0;
- +
- + /*
- + * Check our mapped device first. This way, our mapped device
- + * gets invalidated, and we can tell our device that its changed.
- + */
- + if (check_disk_change(img->mapdev)) {
- + /* its changed */
- + int i;
- + for (i = 0; i < ADFSIMG_NRDEV; i++) {
- + if (img->mapdev == adfsimg_images[i].mapdev) {
- + adfsimg_images[i].changed = 1;
- +#if DEBUG & DEBUG_CHANGED
- + printk ("im: checkdiskchange: host device changed [target = %d]\n", i);
- +#endif
- + }
- + }
- + }
- + if (img->changed)
- + return 1;
- + return 0;
- +}
- +
- +/*
- + * Revalidate our partitions. We do not revalidate the device that
- + * we are mapping, since that should have already been done. However,
- + * we do call it the disk change just to make sure.
- + *
- + * This means that we don't re-read our mapping information, and
- + * we keep our size info on the raw image file.
- + */
- +static int adfsimg_revalidate_disk (kdev_t dev, int maxusage)
- +{
- + int target, max_p, start, i;
- + struct adfsimage *img;
- + unsigned long flags;
- +
- + target = TARGETMAP(dev);
- +
- + if (target >= ADFSIMG_NRDEV) {
- + printk ("im: adfsimg_revalidate: invalid device\n");
- + return 0;
- + }
- +#if DEBUG & DEBUG_CHANGED
- +printk ("im: adfsimg_revalidate: target %d\n", target);
- +#endif
- + img = adfsimg_images + target;
- +
- + if (MAJOR(img->mapdev) == UNNAMED_MAJOR)
- + return 0;
- +
- + save_flags_cli (flags);
- + if (img->busy || img->accesscount > maxusage) {
- + restore_flags (flags);
- + return -EBUSY;
- + }
- + img->busy = 1;
- + restore_flags (flags);
- +
- + start = target << ADFSIMG_SHIFT;
- + max_p = adfsimg_gendisk.max_p;
- +
- + for (i = adfsimg_gendisk.max_p - 1; i >= 0; i--) {
- + int minor = start + i;
- + if (adfsimg_gendisk.part[minor].nr_sects > 0) {
- + kdev_t devi = MKDEV(MAJOR_NR, minor);
- + sync_dev (devi);
- + invalidate_inodes (devi);
- + invalidate_buffers (devi);
- + }
- + adfsimg_gendisk.part[minor].start_sect = 0;
- + adfsimg_gendisk.part[minor].nr_sects = 0;
- + blksize_size[MAJOR_NR][minor] = 1024;
- + }
- +
- + if (!check_disk_change (img->mapdev)) {
- + img->changed = 0;
- +
- + adfsimg_gendisk.part[start].nr_sects = img->capacity;
- + resetup_one_dev (&adfsimg_gendisk, target);
- + }
- +
- + adfsimg_unlockdev (img);
- + return 0;
- +}
- +
- +static int adfsimg_revalidate (kdev_t dev)
- +{
- + return adfsimg_revalidate_disk (dev, 0);
- +}
- +
- +static struct file_operations adfsimg_fops = {
- + NULL, /* lseek - default */
- + block_read, /* read - general block-dev read */
- + block_write, /* write - general block-dev write */
- + NULL, /* readdir - bad */
- + NULL, /* select */
- + adfsimg_ioctl, /* ioctl */
- + NULL, /* mmap */
- + adfsimg_open, /* open */
- + adfsimg_release, /* release */
- + block_fsync, /* fsync */
- + NULL, /* fasync */
- + adfsimg_checkmediachange, /* disk change */
- + adfsimg_revalidate /* revalidate */
- +};
- +
- +int adfsimg_init (void)
- +{
- + struct gendisk **gdp;
- +
- + if (register_blkdev (MAJOR_NR, "im", &adfsimg_fops)) {
- + printk ("adfsimg: unable to get major %d for image file system\n", MAJOR_NR);
- + return -1;
- + }
- + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- + read_ahead[MAJOR_NR] = 0; /* we rely on the hardware reading ahead */
- +
- + for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next));
- + *gdp = &adfsimg_gendisk; /* link onto tail of list */
- + return 0;
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/adfspart.c linux/arch/arm/drivers/block/adfspart.c
- --- linux.orig/arch/arm/drivers/block/adfspart.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/adfspart.c Mon Jul 15 21:32:56 1996
- @@ -0,0 +1,364 @@
- +/*
- + * linux/arch/arm/drivers/block/adfspart.c
- + *
- + * Copyright (c) 1996 Russell King.
- + *
- + * Scan ADFS partitions on hard disk drives.
- + */
- +
- +#include <linux/config.h>
- +#include <linux/kernel.h>
- +#include <linux/kdev_t.h>
- +#include <linux/major.h>
- +#include <linux/string.h>
- +#include <linux/genhd.h>
- +#include <linux/fs.h>
- +
- +#include "adfs.h"
- +
- +#undef CONFIG_WRITE_PROTECT_ADFS
- +#define CONFIG_ICS
- +#define CONFIG_ADFS
- +#undef CONFIG_CUMANA
- +
- +/*
- + * All partition types below here have been disabled.
- + */
- +#define PARTITION_LINUX 16
- +extern void add_partition (struct gendisk *hd, int minor, int start, int size);
- +
- +static void adfspart_setgeometry (kdev_t dev, int secspertrack, int heads, unsigned long totalblocks)
- +{
- + extern void hd_set_geometry (kdev_t dev, unsigned char, unsigned char, unsigned long, unsigned int);
- + extern void xd_set_geometry (kdev_t dev, unsigned char, unsigned char, unsigned long, unsigned int);
- +
- + switch (MAJOR(dev)) {
- +#ifdef CONFIG_BLK_DEV_HD
- + /*
- + * It would actually look like the HD driver doesn't actually need this,
- + * since it asks the drive about its the geometry... However, the 'BIOS'
- + * information is not updated...
- + */
- + case HD_MAJOR:
- + hd_set_geometry (dev, secspertrack, heads, totalblocks, 1);
- + break;
- +#endif
- +#ifdef CONFIG_BLK_DEV_IDE
- +#ifndef CONFIG_BLK_DEV_HD
- + case IDE0_MAJOR:
- +#endif
- + case IDE1_MAJOR:
- + case IDE2_MAJOR:
- + case IDE3_MAJOR:
- + break;
- +#endif
- +#ifdef CONFIG_BLK_DEV_XD
- + case XT_DISK_MAJOR:
- + xd_set_geometry (dev, secspertrack, heads, totalblocks, 1);
- + break;
- +#endif
- + default:
- + printk ("%s: don't know how to set geometry\n", kdevname (dev));
- + break;
- + }
- +}
- +
- +#ifdef CONFIG_CUMANA
- +static int adfspart_check_CUMANA (struct gendisk *hd, kdev_t dev,
- + unsigned long first_sector, int minor)
- +{
- + struct buffer_head *bh;
- + struct disc_record *dr;
- + unsigned long start_sect = 0, nr_sects;
- + int first = 1, mask = (1 << hd->minor_shift) - 1;
- +
- + /*
- + * Try Cumana style partitions - sector 3 contains ADFS boot block with pointer
- + * to next 'drive'.
- + *
- + * There are unknowns in this code - is the 'cylinder number' of the next
- + * partition relative to the start of this one - I'm assuming it is.
- + *
- + * Also, which ID did Cumana use?
- + *
- + * This is totally unfinished, and will require more work to get it going.
- + * Hence it is totally untested.
- + */
- +
- + while (1) {
- + unsigned long end_sector;
- +
- + if (!(bh = bread (dev, first_sector + 3, 1024)))
- + return -1;
- +
- + if (adfspart_checkbblk (bh->b_data)) {
- + brelse (bh);
- + return first ? 0 : 1;
- + }
- +
- + dr = (struct disc_record *)(bh->b_data + 0x1c0);
- +
- + if (dr->disc_size == 0) {
- + brelse (bh);
- + return first ? 0 : 1;
- + }
- +
- + end_sector = (bh->b_data[0x1fd] + (bh->b_data[0x1fe] << 8)) *
- + (dr->heads + (dr->lowsector & 0x40 ? 1 : 0)) * dr->secspertrack;
- +
- + switch (bh->b_data[0x1fc] & 15) {
- + case 0: /* No partition / ADFS? */
- + if (end_cylinder == 0) {
- + /*
- + * No partition
- + */
- + nr_sects = dr->disc_size >> bb->log2secsize;
- + add_partition (hd, minor, first_sector, nr_sects);
- + return 1;
- + }
- + break;
- +
- + case 1: /* RiscIX - we don't know how to find the next one. */
- + add_partition (hd, minor, first_sector, end_sector);
- + first_sector += end_sector;
- + break;
- + }
- + brelse (bh);
- + }
- +}
- +#endif
- +
- +#ifdef CONFIG_ADFS
- +/*
- + * Purpose: allocate ADFS partitions.
- + *
- + * Params : hd - pointer to gendisk structure to store partition info.
- + * dev - device number to access.
- + * first_sector- first readable sector on the device.
- + * minor - first available minor on device.
- + *
- + * Returns: -1 on error, 0 for no ADFS boot sector, 1 for ok.
- + *
- + * Alloc : hda = whole drive
- + * hda1 = ADFS partition on first drive.
- + * hda2 = non-ADFS partition.
- + */
- +static int adfspart_check_ADFS (struct gendisk *hd, kdev_t dev,
- + unsigned long first_sector, int minor)
- +{
- + struct buffer_head *bh;
- + unsigned long start_sect, nr_sects;
- + struct disc_record *dr;
- + int sectors_per_track, heads;
- +
- + if (!(bh = bread (dev, first_sector + 3, 1024)))
- + return -1;
- +
- + if (adfs_checkbblk (bh->b_data)) {
- + brelse (bh);
- + return 0;
- + }
- +
- + dr = (struct disc_record *)(bh->b_data + 0x1c0);
- +
- + if (dr->disc_size == 0) {
- + brelse (bh);
- + return 0;
- + }
- +
- + printk (" [ADFS]");
- +#ifdef CONFIG_WRITE_PROTECT_ADFS
- + write_protect (hd, MINOR(dev));
- +#endif
- +
- + sectors_per_track = dr->secspertrack;
- + heads = dr->heads + (dr->lowsector & 0x40 ? 1 : 0);
- +
- + nr_sects = dr->disc_size >> dr->log2secsize;
- + /*
- + * insert ADFS drive partition details
- + */
- + add_partition (hd, minor, first_sector, nr_sects);
- +#ifdef CONFIG_WRITE_PROTECT_ADFS
- + write_protect (hd, minor);
- +#endif
- + adfspart_setgeometry (dev, sectors_per_track, heads, nr_sects);
- +
- + /*
- + * Work out start of non-adfs partition.
- + */
- + start_sect = ((bh->b_data[0x1fe] << 8) + bh->b_data[0x1fd]) * sectors_per_track * heads;
- +
- + if (start_sect) {
- + /*
- + * we now have a problem - how to set the origional disk size if the
- + * disk doesn't report it, since there is no standard way of getting
- + * that info.
- + */
- + switch (bh->b_data[0x1fc] & 15) {
- + case PARTITION_RISCIX:
- + printk (" [RiscIX]");
- + nr_sects = 1; /* need to do something about that... */
- +
- + adfspart_setgeometry (dev, sectors_per_track, heads, start_sect + nr_sects);
- + add_partition (hd, minor, first_sector + start_sect, nr_sects);
- + break;
- +
- + case PARTITION_LINUX:
- + printk (" [Linux]");
- + nr_sects = 1; /* need to do something about that... */
- +
- + adfspart_setgeometry (dev, sectors_per_track, heads, start_sect + nr_sects);
- + break;
- + }
- + }
- + return 1;
- +}
- +#endif
- +
- +#ifdef CONFIG_ICS
- +/*
- + * Purpose: allocate ICS partitions.
- + *
- + * Params : hd - pointer to gendisk structure to store partition info.
- + * dev - device number to access.
- + * first_sector- first readable sector on the device.
- + * minor - first available minor on device.
- + *
- + * Returns: -1 on error, 0 for no ICS table, 1 for partitions ok.
- + *
- + * Alloc : hda = whole drive
- + * hda1 = ADFS partition 0 on first drive.
- + * hda2 = optional 'adfs' style partition after ADFS partition 0.
- + * hda3 = ADFS partition 1 on first drive.
- + * hda4 = optional 'adfs' style partition after ADFS partition 1.
- + * ..etc..
- + */
- +static int adfspart_check_ICS (struct gendisk *hd, kdev_t dev,
- + unsigned long first_sector, int minor)
- +{
- + unsigned long sum, start_sect, nr_sects, last_sect = 0, *p;
- + int i, mask = (1 << hd->minor_shift) - 1;
- + int sectors_per_track, heads;
- + struct buffer_head *bh, *bh2;
- + struct disc_record *dr;
- +
- + /*
- + * Try ICS style partitions - sector 0 contains partition info.
- + */
- +
- + if (!(bh = bread (dev, first_sector, 1024)))
- + return -1;
- +
- + /*
- + * The fax is a bit unclear here - it says:
- + * 'The checksum is caluclated as the 32-bit sum of all the bytes in the sector
- + * from (0 to 507) + &50617274, and is stored at 508 - 511.' Does this mean
- + * a step of 4 bytes, or one byte?
- + */
- + for (i = 0, sum = 0x50617274; i < 507; i++)
- + sum += bh->b_data[i];
- +
- + if (sum != *(unsigned long *)(&bh->b_data[507])) {
- + brelse (bh);
- + return 0; /* not ICS partition table */
- + }
- +
- + /*
- + * Now get the disc record from the boot sectors (the first partition MUST be ADFS)
- + */
- + if (!(bh2 = bread (dev, first_sector + 3, 1024))) {
- + brelse (bh);
- + return -1;
- + }
- +
- + if (adfs_checkbblk (bh2->b_data)) {
- + brelse (bh2);
- + brelse (bh);
- + return 0;
- + }
- +
- + dr = (struct disc_record *)(bh->b_data + 0x1c0);
- +
- + if (dr->disc_size == 0) {
- + brelse (bh2);
- + brelse (bh);
- + return 0;
- + }
- +
- + sectors_per_track = dr->secspertrack;
- + heads = dr->heads + (dr->lowsector & 0x40 ? 1 : 0);
- +
- + brelse (bh2);
- +
- + printk (" [ICS]");
- +#ifdef CONFIG_WRITE_PROTECT_ADFS
- + write_protect (hd, MINOR(dev));
- +#endif
- +
- + p = (unsigned long *)bh->b_data;
- + while (1) {
- + if ((minor & mask) == 0)
- + break;
- +
- + start_sect = *p++;
- + nr_sects = *p++;
- + if (!start_sect || !nr_sects)
- + break;
- +
- + add_partition (hd, minor, first_sector + start_sect, nr_sects);
- + last_sect = first_sector + start_sect + nr_sects;
- +#ifdef CONFIG_WRITE_PROTECT_ADFS
- + write_protect (hd, minor);
- +#endif
- + /*
- + * We have detected a partition. It could be followed by another non-ICS
- + * partition specified in the boot block. [currently not implemented].
- + */
- + minor += 2;
- + }
- +
- + brelse (bh);
- +
- + adfspart_setgeometry (dev, sectors_per_track, heads, last_sect);
- +
- + return 1;
- +}
- +#endif
- +
- +/*
- + * Purpose: initialise all the partitions on an ADFS drive.
- + * These may be other ADFS partitions or a Linux/RiscBSD/RiscIX
- + * partition.
- + *
- + * Params : hd - pointer to gendisk structure to store devices partitions.
- + * dev - device number to access
- + * first_sector - first available sector on the disk.
- + * minor - first available minor on this device.
- + *
- + * Returns: -1 on error, 0 if not ADFS format, 1 if ok.
- + */
- +int adfspart_initdev (struct gendisk *hd, kdev_t dev,
- + unsigned long first_sector, int minor)
- +{
- + int r = 0;
- +
- +#ifdef CONFIG_ICS
- + if (r == 0)
- + r = adfspart_check_ICS (hd, dev, first_sector, minor);
- +#endif
- +#ifdef CONFIG_CUMANA
- + if (r == 0)
- + r = adfspart_check_CUMANA (hd, dev, first_sector, minor);
- +#endif
- +#ifdef CONFIG_ADFS
- + if (r == 0)
- + r = adfspart_check_ADFS (hd, dev, first_sector, minor);
- +#endif
- + if (r < 0)
- + printk (" unable to read boot sectors / partition sectors");
- + else
- + if (r)
- + printk ("\n");
- + return r;
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/blk.h linux/arch/arm/drivers/block/blk.h
- --- linux.orig/arch/arm/drivers/block/blk.h Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/blk.h Fri Sep 6 23:58:51 1996
- @@ -0,0 +1,446 @@
- +#ifndef _BLK_H
- +#define _BLK_H
- +
- +#include <linux/blkdev.h>
- +#include <linux/locks.h>
- +#include <linux/config.h>
- +
- +/*
- + * NR_REQUEST is the number of entries in the request-queue.
- + * NOTE that writes may use only the low 2/3 of these: reads
- + * take precedence.
- + */
- +#define NR_REQUEST 64
- +
- +/*
- + * This is used in the elevator algorithm. We don't prioritise reads
- + * over writes any more --- although reads are more time-critical than
- + * writes, by treating them equally we increase filesystem throughput.
- + * This turns out to give better overall performance. -- sct
- + */
- +#define IN_ORDER(s1,s2) \
- +((s1)->rq_dev < (s2)->rq_dev || (((s1)->rq_dev == (s2)->rq_dev && \
- +(s1)->sector < (s2)->sector)))
- +
- +/*
- + * These will have to be changed to be aware of different buffer
- + * sizes etc.. It actually needs a major cleanup.
- + */
- +#if defined(IDE_DRIVER) || defined(MD_DRIVER)
- +#define SECTOR_MASK ((BLOCK_SIZE >> 9) - 1)
- +#else
- +#define SECTOR_MASK (blksize_size[MAJOR_NR] && \
- + blksize_size[MAJOR_NR][MINOR(CURRENT->rq_dev)] ? \
- + ((blksize_size[MAJOR_NR][MINOR(CURRENT->rq_dev)] >> 9) - 1) : \
- + ((BLOCK_SIZE >> 9) - 1))
- +#endif /* IDE_DRIVER */
- +
- +#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
- +
- +#ifdef CONFIG_CDU31A
- +extern int cdu31a_init(void);
- +#endif CONFIG_CDU31A
- +#ifdef CONFIG_MCD
- +extern int mcd_init(void);
- +#endif CONFIG_MCD
- +#ifdef CONFIG_MCDX
- +extern int mcdx_init(void);
- +#endif CONFIG_MCDX
- +#ifdef CONFIG_SBPCD
- +extern int sbpcd_init(void);
- +#endif CONFIG_SBPCD
- +#ifdef CONFIG_AZTCD
- +extern int aztcd_init(void);
- +#endif CONFIG_AZTCD
- +#ifdef CONFIG_CDU535
- +extern int sony535_init(void);
- +#endif CONFIG_CDU535
- +#ifdef CONFIG_GSCD
- +extern int gscd_init(void);
- +#endif CONFIG_GSCD
- +#ifdef CONFIG_CM206
- +extern int cm206_init(void);
- +#endif CONFIG_CM206
- +#ifdef CONFIG_OPTCD
- +extern int optcd_init(void);
- +#endif CONFIG_OPTCD
- +#ifdef CONFIG_SJCD
- +extern int sjcd_init(void);
- +#endif CONFIG_SJCD
- +#ifdef CONFIG_BLK_DEV_HD
- +extern int hd_init(void);
- +#endif
- +#ifdef CONFIG_BLK_DEV_IDE
- +extern int ide_init(void);
- +#endif
- +#ifdef CONFIG_BLK_DEV_XD
- +extern int xd_init(void);
- +#endif
- +#ifdef CONFIG_BLK_DEV_LOOP
- +extern int loop_init(void);
- +#endif
- +#ifdef CONFIG_BLK_DEV_MD
- +extern int md_init(void);
- +#endif CONFIG_BLK_DEV_MD
- +
- +extern void set_device_ro(kdev_t dev,int flag);
- +void add_blkdev_randomness(int major);
- +
- +extern int floppy_init(void);
- +extern void rd_load(void);
- +extern int rd_init(void);
- +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */
- +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
- +extern int rd_image_start; /* starting block # of image */
- +
- +#ifdef CONFIG_BLK_DEV_INITRD
- +
- +#define INITRD_MINOR 250 /* shouldn't collide with /dev/ram* too soon ... */
- +
- +extern unsigned long initrd_start,initrd_end;
- +extern int mount_initrd; /* zero if initrd should not be mounted */
- +void initrd_init(void);
- +
- +#endif
- +
- +#define RO_IOCTLS(dev,where) \
- + case BLKROSET: { int __err; if (!suser()) return -EACCES; \
- + __err = verify_area(VERIFY_READ, (void *) (where), sizeof(long)); \
- + if (!__err) set_device_ro((dev),get_fs_long((long *) (where))); return __err; } \
- + case BLKROGET: { int __err = verify_area(VERIFY_WRITE, (void *) (where), sizeof(long)); \
- + if (!__err) put_fs_long(0!=is_read_only(dev),(long *) (where)); return __err; }
- +
- +#if defined(MAJOR_NR) || defined(IDE_DRIVER)
- +
- +/*
- + * Add entries as needed.
- + */
- +
- +#ifdef IDE_DRIVER
- +
- +#define DEVICE_NR(device) (MINOR(device) >> PARTN_BITS)
- +#define DEVICE_ON(device) /* nothing */
- +#define DEVICE_OFF(device) /* nothing */
- +
- +#elif (MAJOR_NR == RAMDISK_MAJOR)
- +
- +/* ram disk */
- +#define DEVICE_NAME "ramdisk"
- +#define DEVICE_REQUEST rd_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +#define DEVICE_NO_RANDOM
- +
- +#elif (MAJOR_NR == FLOPPY_MAJOR)
- +
- +static void floppy_off(unsigned int nr);
- +
- +#define DEVICE_NAME "floppy"
- +#define DEVICE_INTR do_floppy
- +#define DEVICE_REQUEST do_fd_request
- +#define DEVICE_NR(device) ( (MINOR(device) & 3) | ((MINOR(device) & 0x80 ) >> 5 ))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
- +
- +#elif (MAJOR_NR == HD_MAJOR)
- +
- +/* harddisk: timeout is 6 seconds.. */
- +#define DEVICE_NAME "harddisk"
- +#define DEVICE_INTR do_hd
- +#define DEVICE_TIMEOUT HD_TIMER
- +#define TIMEOUT_VALUE (6*HZ)
- +#define DEVICE_REQUEST do_hd_request
- +#define DEVICE_NR(device) (MINOR(device)>>6)
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == SCSI_DISK_MAJOR)
- +
- +#define DEVICE_NAME "scsidisk"
- +#define DEVICE_INTR do_sd
- +#define TIMEOUT_VALUE (2*HZ)
- +#define DEVICE_REQUEST do_sd_request
- +#define DEVICE_NR(device) (MINOR(device) >> 4)
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +/* Kludge to use the same number for both char and block major numbers */
- +#elif (MAJOR_NR == MD_MAJOR) && defined(MD_DRIVER)
- +
- +#define DEVICE_NAME "Multiple devices driver"
- +#define DEVICE_REQUEST do_md_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == SCSI_TAPE_MAJOR)
- +
- +#define DEVICE_NAME "scsitape"
- +#define DEVICE_INTR do_st
- +#define DEVICE_NR(device) (MINOR(device) & 0x7f)
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == SCSI_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "CD-ROM"
- +#define DEVICE_INTR do_sr
- +#define DEVICE_REQUEST do_sr_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == XT_DISK_MAJOR)
- +
- +#define DEVICE_NAME "xt disk"
- +#define DEVICE_REQUEST do_xd_request
- +#define DEVICE_NR(device) (MINOR(device) >> 6)
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif defined(MFM_DISK_MAJOR) && (MAJOR_NR == MFM_DISK_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 == CDU31A_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "CDU31A"
- +#define DEVICE_REQUEST do_cdu31a_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "Mitsumi CD-ROM"
- +/* #define DEVICE_INTR do_mcd */
- +#define DEVICE_REQUEST do_mcd_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == MITSUMI_X_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "Mitsumi CD-ROM"
- +/* #define DEVICE_INTR do_mcdx */
- +#define DEVICE_REQUEST do_mcdx_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "Matsushita CD-ROM controller #1"
- +#define DEVICE_REQUEST do_sbpcd_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == MATSUSHITA_CDROM2_MAJOR)
- +
- +#define DEVICE_NAME "Matsushita CD-ROM controller #2"
- +#define DEVICE_REQUEST do_sbpcd2_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == MATSUSHITA_CDROM3_MAJOR)
- +
- +#define DEVICE_NAME "Matsushita CD-ROM controller #3"
- +#define DEVICE_REQUEST do_sbpcd3_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == MATSUSHITA_CDROM4_MAJOR)
- +
- +#define DEVICE_NAME "Matsushita CD-ROM controller #4"
- +#define DEVICE_REQUEST do_sbpcd4_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == AZTECH_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "Aztech CD-ROM"
- +#define DEVICE_REQUEST do_aztcd_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == CDU535_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "SONY-CDU535"
- +#define DEVICE_INTR do_cdu535
- +#define DEVICE_REQUEST do_cdu535_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == GOLDSTAR_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "Goldstar R420"
- +#define DEVICE_REQUEST do_gscd_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == CM206_CDROM_MAJOR)
- +#define DEVICE_NAME "Philips/LMS cd-rom cm206"
- +#define DEVICE_REQUEST do_cm206_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == OPTICS_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "DOLPHIN 8000AT CD-ROM"
- +#define DEVICE_REQUEST do_optcd_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == SANYO_CDROM_MAJOR)
- +
- +#define DEVICE_NAME "Sanyo H94A CD-ROM"
- +#define DEVICE_REQUEST do_sjcd_request
- +#define DEVICE_NR(device) (MINOR(device))
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#elif (MAJOR_NR == ADFSIMG_MAJOR)
- +
- +#define DEVICE_NAME "im"
- +#define DEVICE_REQUEST adfsimg_request
- +#define DEVICE_NR(device) (MINOR(device) >> ADFSIMG_SHIFT)
- +#define DEVICE_ON(device)
- +#define DEVICE_OFF(device)
- +
- +#endif /* MAJOR_NR == whatever */
- +
- +#if (MAJOR_NR != SCSI_TAPE_MAJOR)
- +#if !defined(IDE_DRIVER)
- +
- +#ifndef CURRENT
- +#define CURRENT (blk_dev[MAJOR_NR].current_request)
- +#endif
- +
- +#define CURRENT_DEV DEVICE_NR(CURRENT->rq_dev)
- +
- +#ifdef DEVICE_INTR
- +static void (*DEVICE_INTR)(void) = NULL;
- +#endif
- +#ifdef DEVICE_TIMEOUT
- +
- +#define SET_TIMER \
- +((timer_table[DEVICE_TIMEOUT].expires = jiffies + TIMEOUT_VALUE), \
- +(timer_active |= 1<<DEVICE_TIMEOUT))
- +
- +#define CLEAR_TIMER \
- +timer_active &= ~(1<<DEVICE_TIMEOUT)
- +
- +#define SET_INTR(x) \
- +if ((DEVICE_INTR = (x)) != NULL) \
- + SET_TIMER; \
- +else \
- + CLEAR_TIMER;
- +
- +#else
- +
- +#define SET_INTR(x) (DEVICE_INTR = (x))
- +
- +#endif /* DEVICE_TIMEOUT */
- +
- +static void (DEVICE_REQUEST)(void);
- +
- +#ifdef DEVICE_INTR
- +#define CLEAR_INTR SET_INTR(NULL)
- +#else
- +#define CLEAR_INTR
- +#endif
- +
- +#define INIT_REQUEST \
- + if (!CURRENT) {\
- + CLEAR_INTR; \
- + return; \
- + } \
- + if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) \
- + panic(DEVICE_NAME ": request list destroyed"); \
- + if (CURRENT->bh) { \
- + if (!buffer_locked(CURRENT->bh)) \
- + panic(DEVICE_NAME ": block not locked"); \
- + }
- +
- +#endif /* !defined(IDE_DRIVER) */
- +
- +/* end_request() - SCSI devices have their own version */
- +/* - IDE drivers have their own copy too */
- +
- +#if ! SCSI_BLK_MAJOR(MAJOR_NR)
- +
- +#if defined(IDE_DRIVER) && !defined(_IDE_C) /* shared copy for IDE modules */
- +void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup);
- +#else
- +
- +#ifdef IDE_DRIVER
- +void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup) {
- + struct request *req = hwgroup->rq;
- +#else
- +static void end_request(int uptodate) {
- + struct request *req = CURRENT;
- +#endif /* IDE_DRIVER */
- + struct buffer_head * bh;
- +
- + req->errors = 0;
- + if (!uptodate) {
- + printk("end_request: I/O error, dev %s, sector %lu\n",
- + kdevname(req->rq_dev), req->sector);
- + req->nr_sectors--;
- + req->nr_sectors &= ~SECTOR_MASK;
- + req->sector += (BLOCK_SIZE / 512);
- + req->sector &= ~SECTOR_MASK;
- + }
- +
- + if ((bh = req->bh) != NULL) {
- + req->bh = bh->b_reqnext;
- + bh->b_reqnext = NULL;
- + mark_buffer_uptodate(bh, uptodate);
- + unlock_buffer(bh);
- + if ((bh = req->bh) != NULL) {
- + req->current_nr_sectors = bh->b_size >> 9;
- + if (req->nr_sectors < req->current_nr_sectors) {
- + req->nr_sectors = req->current_nr_sectors;
- + printk("end_request: buffer-list destroyed\n");
- + }
- + req->buffer = bh->b_data;
- + return;
- + }
- + }
- +#ifndef DEVICE_NO_RANDOM
- + add_blkdev_randomness(MAJOR(req->rq_dev));
- +#endif
- +#ifdef IDE_DRIVER
- + blk_dev[MAJOR(req->rq_dev)].current_request = req->next;
- + hwgroup->rq = NULL;
- +#else
- + DEVICE_OFF(req->rq_dev);
- + CURRENT = req->next;
- +#endif /* IDE_DRIVER */
- + if (req->sem != NULL)
- + up(req->sem);
- + req->rq_status = RQ_INACTIVE;
- + wake_up(&wait_for_request);
- +}
- +#endif /* defined(IDE_DRIVER) && !defined(_IDE_C) */
- +#endif /* ! SCSI_BLK_MAJOR(MAJOR_NR) */
- +#endif /* (MAJOR_NR != SCSI_TAPE_MAJOR) */
- +
- +#endif /* defined(MAJOR_NR) || defined(IDE_DRIVER) */
- +
- +#endif /* _BLK_H */
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/fd1772.c linux/arch/arm/drivers/block/fd1772.c
- --- linux.orig/arch/arm/drivers/block/fd1772.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/fd1772.c Sat Sep 7 21:09:40 1996
- @@ -0,0 +1,1638 @@
- +/*
- + * linux/kernel/arch/arm/drivers/block/fd1772.c
- + * Based on ataflop.c in the m68k Linux
- + * Copyright (C) 1993 Greg Harp
- + * Atari Support by Bjoern Brauel, Roman Hodek
- + * Archimedes Support by Dave Gilbert (gilbertd@cs.man.ac.uk)
- + *
- + * Big cleanup Sep 11..14 1994 Roman Hodek:
- + * - Driver now works interrupt driven
- + * - Support for two drives; should work, but I cannot test that :-(
- + * - Reading is done in whole tracks and buffered to speed up things
- + * - Disk change detection and drive deselecting after motor-off
- + * similar to TOS
- + * - Autodetection of disk format (DD/HD); untested yet, because I
- + * don't have an HD drive :-(
- + *
- + * Fixes Nov 13 1994 Martin Schaller:
- + * - Autodetection works now
- + * - Support for 5 1/4" disks
- + * - Removed drive type (unknown on atari)
- + * - Do seeks with 8 Mhz
- + *
- + * Changes by Andreas Schwab:
- + * - After errors in multiple read mode try again reading single sectors
- + * (Feb 1995):
- + * - Clean up error handling
- + * - Set blk_size for proper size checking
- + * - Initialize track register when testing presence of floppy
- + * - Implement some ioctl's
- + *
- + * Changes by Torsten Lang:
- + * - When probing the floppies we should add the FDC1772CMDADD_H flag since
- + * the FDC1772 will otherwise wait forever when no disk is inserted...
- + *
- + * Things left to do:
- + * - Formatting
- + * - Maybe a better strategy for disk change detection (does anyone
- + * know one?)
- + * - There are some strange problems left: The strangest one is
- + * that, at least on my TT (4+4MB), the first 2 Bytes of the last
- + * page of the TT-Ram (!) change their contents (some bits get
- + * set) while a floppy DMA is going on. But there are no accesses
- + * to these memory locations from the kernel... (I tested that by
- + * making the page read-only). I cannot explain what's going on...
- + * - Sometimes the drive-change-detection stops to work. The
- + * function is still called, but the WP bit always reads as 0...
- + * Maybe a problem with the status reg mode or a timing problem.
- + * Note 10/12/94: The change detection now seems to work reliably.
- + * There is no proof, but I've seen no hang for a long time...
- + *
- + * ARCHIMEDES changes: (gilbertd@cs.man.ac.uk)
- + * 26/12/95 - Changed all names starting with FDC to FDC1772
- + * Removed all references to clock speed of FDC - we're stuck with 8MHz
- + * Modified disk_type structure to remove HD formats
- + *
- + * 7/ 1/96 - Wrote FIQ code, removed most remaining atariisms
- + *
- + * 13/ 1/96 - Well I think its read a single sector; but there is a problem
- + * fd_rwsec_done which is called in FIQ mode starts another transfer
- + * off (in fd_rwsec) while still in FIQ mode. Because its still in
- + * FIQ mode it can't service the DMA and loses data. So need to
- + * heavily restructure.
- + * 14/ 1/96 - Found that the definitions of the register numbers of the
- + * FDC were multiplied by 2 in the header for the 16bit words
- + * of the atari so half the writes were going in the wrong place.
- + * Also realised that the FIQ entry didn't make any attempt to
- + * preserve registers or return correctly; now in assembler.
- + *
- + * 11/ 2/96 - Hmm - doesn't work on real machine. Auto detect doesn't
- + * and hacking that past seems to wait forever - check motor
- + * being turned on.
- + *
- + * 17/ 2/96 - still having problems - forcing track to -1 when selecting
- + * new drives seems to allow it to read first few sectors
- + * but then we get solid hangs at apparently random places
- + * which change depending what is happening.
- + *
- + * 9/ 3/96 - Fiddled a lot of stuff around to move to kernel 1.3.35
- + * A lot of fiddling in DMA stuff. Having problems with it
- + * constnatly thinking its timeing out. Ah - its timeout
- + * was set to (6*HZ) rather than jiffies+(6*HZ). Now giving
- + * duff data!
- + *
- + * 5/ 4/96 - Made it use the new IOC_ macros rather than *ioc
- + * Hmm - giving unexpected FIQ and then timeouts
- + * 18/ 8/96 - Ran through indent -kr -i8
- + * Some changes to disc change detect; don't know how well it
- + * works.
- + * 24/ 8/96 - Put all the track buffering code back in from the atari
- + * code - I wonder if it will still work... No :-)
- + * Still works if I turn off track buffering.
- + * 25/ 8/96 - Changed the timer expires that I'd added back to be
- + * jiffies + ....; and it all sprang to life! Got 2.8K/sec
- + * off a cp -r of a 679K disc (showed 94% cpu usage!)
- + * (PC gets 14.3K/sec - 0% CPU!) Hmm - hard drive corrupt!
- + * Also perhaps that compile was with cache off.
- + * changed cli in fd_readtrack_check to cliIF
- + * changed vmallocs to kmalloc (whats the difference!!)
- + * Removed the busy wait loop in do_fd_request and replaced
- + * by a routine on tq_immediate; only 11% cpu on a dd off the
- + * raw disc - but the speed is the same.
- + */
- +
- +#include <linux/config.h>
- +#include <linux/sched.h>
- +#include <linux/fs.h>
- +#include <linux/fcntl.h>
- +#include <linux/kernel.h>
- +#include <linux/interrupt.h>
- +#include <linux/timer.h>
- +#include <linux/tqueue.h>
- +#include <linux/fd.h>
- +#include <linux/fd1772.h>
- +#include <linux/errno.h>
- +#include <linux/types.h>
- +#include <linux/delay.h>
- +#include <linux/mm.h>
- +
- +#include <asm/arch/oldlatches.h>
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +#include <asm/dma.h>
- +#include <asm/hardware.h>
- +#include <asm/io.h>
- +#include <asm/irq.h>
- +#include <asm/irq-no.h>
- +#include <asm/pgtable.h>
- +
- +#define MAJOR_NR FLOPPY_MAJOR
- +#define FLOPPY_DMA 0
- +#include "blk.h"
- +
- +/* 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
- + * some other code depends on the number of floppies... (It is defined
- + * in a public header!)
- + */
- +#if 0
- +#undef FD_MAX_UNITS
- +#define FD_MAX_UNITS 2
- +#endif
- +
- +/* Ditto worries for Arc - DAG */
- +#define FD_MAX_UNITS 4
- +#define TRACKBUFFER 0
- +/*#define DEBUG*/
- +
- +#ifdef DEBUG
- +#define DPRINT(a) printk a
- +#else
- +#define DPRINT(a)
- +#endif
- +
- +/* Disk types: DD */
- +static struct archy_disk_type {
- + const char *name;
- + unsigned spt; /* sectors per track */
- + unsigned blocks; /* total number of blocks */
- + unsigned stretch; /* track doubling ? */
- +} disk_type[] = {
- +
- + { "d360", 9, 720, 0 }, /* 360kB diskette */
- + { "D360", 9, 720, 1 }, /* 360kb in 720kb drive */
- + { "D720", 9, 1440, 0 }, /* 720kb diskette (DD) */
- + /*{ "D820", 10,1640, 0}, *//* DD disk with 82 tracks/10 sectors
- + - DAG - can't see how type detect can distinguish this
- + from 720K until it reads block 4 by which time its too late! */
- +};
- +
- +#define NUM_DISK_TYPES (sizeof(disk_type)/sizeof(*disk_type))
- +
- +/*
- + * Maximum disk size (in kilobytes). This default is used whenever the
- + * current disk size is unknown.
- + */
- +#define MAX_DISK_SIZE 720
- +
- +static int floppy_sizes[256];
- +static int floppy_blocksizes[256] = {0,};
- +
- +/* current info on each unit */
- +static struct archy_floppy_struct {
- + int connected; /* !=0 : drive is connected */
- + int autoprobe; /* !=0 : do autoprobe */
- +
- + struct archy_disk_type *disktype; /* current type of disk */
- +
- + int track; /* current head position or -1
- + * if unknown */
- + unsigned int steprate; /* steprate setting */
- + unsigned int wpstat; /* current state of WP signal
- + * (for disk change detection) */
- +} unit[FD_MAX_UNITS];
- +
- +/* DAG: On Arc we spin on a flag being cleared by fdc1772_comendhandler which
- + is an assembler routine */
- +extern void fdc1772_comendhandler(void); /* Actually doens't have these parameters - see fd1772.S */
- +extern volatile int fdc1772_comendstatus;
- +extern volatile int fdc1772_fdc_int_done;
- +
- +#define FDC1772BASE (0x3210000>>2)
- +
- +#define FDC1772_READ(reg) inb(FDC1772BASE+(reg/2))
- +
- +/* DAG: You wouldn't be silly to ask why FDC1772_WRITE is a function rather
- + than the #def below - well simple - the #def won't compile - and I
- + don't understand why (__outwc not defined) */
- +/* NOTE: Reg is 0,2,4,6 as opposed to 0,1,2,3 or 0,4,8,12 to keep compatibility
- + with the ST version of fd1772.h */
- +/*#define FDC1772_WRITE(reg,val) outw(val,(reg+FDC1772BASE)); */
- +void FDC1772_WRITE(int reg, unsigned char val)
- +{
- + if (reg == FDC1772REG_CMD) {
- + DPRINT(("FDC1772_WRITE new command 0x%x\n", val));
- + if (fdc1772_fdc_int_done) {
- + DPRINT(("FDC1772_WRITE: Hmm fdc1772_fdc_int_done true - resetting\n"));
- + fdc1772_fdc_int_done = 0;
- + };
- + };
- + outb(val, (reg / 2) + FDC1772BASE);
- +};
- +
- +#define MAX_SECTORS 22
- +
- +unsigned char *DMABuffer; /* buffer for writes */
- +/*static unsigned long PhysDMABuffer; *//* physical address */
- +/* DAG: On Arc we just go straight for the DMA buffer */
- +#define PhysDMABuffer DMABuffer
- +
- +#ifdef TRACKBUFFER
- +unsigned char *TrackBuffer; /* buffer for reads */
- +#define PhysTrackBuffer TrackBuffer /* physical address */
- +static int BufferDrive, BufferSide, BufferTrack;
- +static int read_track; /* non-zero if we are reading whole tracks */
- +
- +#define SECTOR_BUFFER(sec) (TrackBuffer + ((sec)-1)*512)
- +#define IS_BUFFERED(drive,side,track) \
- + (BufferDrive == (drive) && BufferSide == (side) && BufferTrack == (track))
- +#endif
- +
- +/*
- + * These are global variables, as that's the easiest way to give
- + * information to interrupts. They are the data used for the current
- + * request.
- + */
- +static int SelectedDrive = 0;
- +static int ReqCmd, ReqBlock;
- +static int ReqSide, ReqTrack, ReqSector, ReqCnt;
- +static int HeadSettleFlag = 0;
- +static unsigned char *ReqData, *ReqBuffer;
- +static int MotorOn = 0, MotorOffTrys;
- +
- +/* Synchronization of FDC1772 access. */
- +static volatile int fdc_busy = 0;
- +static struct wait_queue *fdc_wait = NULL;
- +
- +
- +static unsigned int changed_floppies = 0xff, fake_change = 0;
- +#define CHECK_CHANGE_DELAY HZ/2
- +
- +/* DAG - increased to 30*HZ - not sure if this is the correct thing to do */
- +#define FD_MOTOR_OFF_DELAY (30*HZ)
- +#define FD_MOTOR_OFF_MAXTRY (10*20)
- +
- +#define FLOPPY_TIMEOUT (6*HZ)
- +#define RECALIBRATE_ERRORS 4 /* After this many errors the drive
- + * will be recalibrated. */
- +#define MAX_ERRORS 8 /* After this many errors the driver
- + * will give up. */
- +
- +
- +#define START_MOTOR_OFF_TIMER(delay) \
- + do { \
- + motor_off_timer.expires = jiffies + (delay); \
- + add_timer( &motor_off_timer ); \
- + MotorOffTrys = 0; \
- + } while(0)
- +
- +#define START_CHECK_CHANGE_TIMER(delay) \
- + do { \
- + timer_table[FLOPPY_TIMER].expires = jiffies + (delay); \
- + timer_active |= (1 << FLOPPY_TIMER); \
- + } while(0)
- +
- +#define START_TIMEOUT() \
- + do { \
- + del_timer( &timeout_timer ); \
- + timeout_timer.expires = jiffies + FLOPPY_TIMEOUT; \
- + add_timer( &timeout_timer ); \
- + } while(0)
- +
- +#define STOP_TIMEOUT() \
- + do { \
- + del_timer( &timeout_timer ); \
- + } while(0)
- +
- +#define ENABLE_IRQ() enable_irq(FIQ_FD1772+16);
- +
- +#define DISABLE_IRQ() disable_irq(FIQ_FD1772+16);
- +
- +static void fd1772_checkint(void);
- +
- +struct tq_struct fd1772_tq =
- +{ 0,0, (void *)fd1772_checkint, 0 };
- +/*
- + * The driver is trying to determine the correct media format
- + * while Probing is set. fd_rwsec_done() clears it after a
- + * successful access.
- + */
- +static int Probing = 0;
- +
- +/* This flag is set when a dummy seek is necesary to make the WP
- + * status bit accessible.
- + */
- +static int NeedSeek = 0;
- +
- +
- +/***************************** Prototypes *****************************/
- +
- +static void fd_select_side(int side);
- +static void fd_select_drive(int drive);
- +static void fd_deselect(void);
- +static void fd_motor_off_timer(unsigned long dummy);
- +static void check_change(void);
- +static __inline__ void set_head_settle_flag(void);
- +static __inline__ int get_head_settle_flag(void);
- +static void floppy_irqconsequencehandler(void);
- +static void fd_error(void);
- +static void do_fd_action(int drive);
- +static void fd_calibrate(void);
- +static void fd_calibrate_done(int status);
- +static void fd_seek(void);
- +static void fd_seek_done(int status);
- +static void fd_rwsec(void);
- +#ifdef TRACKBUFFER
- +static void fd_readtrack_check( unsigned long dummy );
- +#endif
- +static void fd_rwsec_done(int status);
- +static void fd_times_out(unsigned long dummy);
- +static void finish_fdc(void);
- +static void finish_fdc_done(int dummy);
- +static void floppy_off(unsigned int nr);
- +static __inline__ void copy_buffer(void *from, void *to);
- +static void setup_req_params(int drive);
- +static void redo_fd_request(void);
- +static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int
- + cmd, unsigned long param);
- +static void fd_probe(int drive);
- +static int fd_test_drive_present(int drive);
- +static void config_types(void);
- +static int floppy_open(struct inode *inode, struct file *filp);
- +static void floppy_release(struct inode *inode, struct file *filp);
- +
- +/************************* End of Prototypes **************************/
- +
- +static struct timer_list motor_off_timer =
- +{NULL, NULL, 0, 0, fd_motor_off_timer};
- +#ifdef TRACKBUFFER
- +static struct timer_list readtrack_timer =
- + { NULL, NULL, 0, 0, fd_readtrack_check };
- +#endif
- +static struct timer_list timeout_timer =
- +{NULL, NULL, 0, 0, fd_times_out};
- +
- +/* DAG: Haven't got a clue what this is? */
- +int stdma_islocked(void)
- +{
- + return 0;
- +};
- +
- +/* Select the side to use. */
- +
- +static void fd_select_side(int side)
- +{
- + unsigned long flags;
- +
- + save_flags(flags);
- + cli();
- +
- + oldlatch_aupdate(LATCHA_SIDESEL, side ? 0 : LATCHA_SIDESEL);
- + restore_flags(flags);
- +}
- +
- +
- +/* Select a drive, update the FDC1772's track register
- + */
- +
- +static void fd_select_drive(int drive)
- +{
- + unsigned long flags;
- +
- +#ifdef DEBUG
- + printk("fd_select_drive:%d\n", drive);
- +#endif
- + /* Hmm - nowhere do we seem to turn the motor on - I'm going to do it here! */
- + oldlatch_aupdate(LATCHA_MOTOR | LATCHA_INUSE, 0);
- +
- + if (drive == SelectedDrive)
- + return;
- +
- + save_flags(flags);
- + cli();
- + oldlatch_aupdate(LATCHA_FDSELALL, 0xf - (1 << drive));
- + restore_flags(flags);
- +
- + /* restore track register to saved value */
- + FDC1772_WRITE(FDC1772REG_TRACK, unit[drive].track);
- + udelay(25);
- +
- + SelectedDrive = drive;
- +}
- +
- +
- +/* Deselect both drives. */
- +
- +static void fd_deselect(void)
- +{
- + unsigned long flags;
- +
- + DPRINT(("fd_deselect\n"));
- +
- + save_flags(flags);
- + cli();
- + oldlatch_aupdate(LATCHA_FDSELALL | LATCHA_MOTOR | LATCHA_INUSE, 0xf | LATCHA_MOTOR | LATCHA_INUSE);
- + restore_flags(flags);
- +
- + SelectedDrive = -1;
- +}
- +
- +
- +/* This timer function deselects the drives when the FDC1772 switched the
- + * motor off. The deselection cannot happen earlier because the FDC1772
- + * counts the index signals, which arrive only if one drive is selected.
- + */
- +
- +static void fd_motor_off_timer(unsigned long dummy)
- +{
- + unsigned long flags;
- + unsigned char status;
- + int delay;
- +
- + del_timer(&motor_off_timer);
- +
- + if (SelectedDrive < 0)
- + /* no drive selected, needn't deselect anyone */
- + return;
- +
- + save_flags(flags);
- + cli();
- +
- + if (fdc_busy) /* was stdma_islocked */
- + goto retry;
- +
- + status = FDC1772_READ(FDC1772REG_STATUS);
- +
- + if (!(status & 0x80)) {
- + /* motor already turned off by FDC1772 -> deselect drives */
- + DPRINT(("fdc1772: deselecting in fd_motor_off_timer\n"));
- + fd_deselect();
- + restore_flags(flags);
- + MotorOn = 0;
- + return;
- + }
- + /* not yet off, try again */
- +
- + retry:
- + restore_flags(flags);
- + /* Test again later; if tested too often, it seems there is no disk
- + * in the drive and the FDC1772 will leave the motor on forever (or,
- + * at least until a disk is inserted). So we'll test only twice
- + * per second from then on...
- + */
- + delay = (MotorOffTrys < FD_MOTOR_OFF_MAXTRY) ?
- + (++MotorOffTrys, HZ / 20) : HZ / 2;
- + START_MOTOR_OFF_TIMER(delay);
- +}
- +
- +
- +/* This function is repeatedly called to detect disk changes (as good
- + * as possible) and keep track of the current state of the write protection.
- + */
- +
- +static void check_change(void)
- +{
- + static int drive = 0;
- +
- + unsigned long flags;
- + int stat;
- +
- + if (fdc_busy)
- + return; /* Don't start poking about if the fdc is busy */
- +
- + return; /* lets just forget it for the mo DAG */
- +
- + if (++drive > 1 || !unit[drive].connected)
- + drive = 0;
- +
- + save_flags(flags);
- + cli();
- +
- + if (!stdma_islocked()) {
- + stat = !!(FDC1772_READ(FDC1772REG_STATUS) & FDC1772STAT_WPROT);
- +
- + /* The idea here is that if the write protect line has changed then
- + the disc must have changed */
- + if (stat != unit[drive].wpstat) {
- + DPRINT(("wpstat[%d] = %d\n", drive, stat));
- + unit[drive].wpstat = stat;
- + set_bit(drive, &changed_floppies);
- + }
- + }
- + restore_flags(flags);
- +
- + START_CHECK_CHANGE_TIMER(CHECK_CHANGE_DELAY);
- +}
- +
- +
- +/* Handling of the Head Settling Flag: This flag should be set after each
- + * seek operation, because we dont't use seeks with verify.
- + */
- +
- +static __inline__ void set_head_settle_flag(void)
- +{
- + HeadSettleFlag = FDC1772CMDADD_E;
- +}
- +
- +static __inline__ int get_head_settle_flag(void)
- +{
- + int tmp = HeadSettleFlag;
- + HeadSettleFlag = 0;
- + return (tmp);
- +}
- +
- +
- +
- +
- +/* General Interrupt Handling */
- +
- +static void (*FloppyIRQHandler) (int status) = NULL;
- +
- +static void floppy_irqconsequencehandler(void)
- +{
- + unsigned char status;
- + void (*handler) (int);
- +
- + fdc1772_fdc_int_done = 0;
- +
- + handler = FloppyIRQHandler;
- + FloppyIRQHandler = NULL;
- +
- + if (handler) {
- + nop();
- + status = (unsigned char) fdc1772_comendstatus;
- + DPRINT(("FDC1772 irq, status = %02x handler = %08lx\n", (unsigned int) status, (unsigned long) handler));
- + handler(status);
- + } else {
- + DPRINT(("FDC1772 irq, no handler status=%02x\n", fdc1772_comendstatus));
- + }
- + DPRINT(("FDC1772 irq: end of floppy_irq\n"));
- +}
- +
- +
- +/* Error handling: If some error happened, retry some times, then
- + * recalibrate, then try again, and fail after MAX_ERRORS.
- + */
- +
- +static void fd_error(void)
- +{
- + printk("FDC1772: fd_error\n");
- + /*panic("fd1772: fd_error"); *//* DAG tmp */
- + if (!CURRENT)
- + return;
- + CURRENT->errors++;
- + if (CURRENT->errors >= MAX_ERRORS) {
- + printk("fd%d: too many errors.\n", SelectedDrive);
- + end_request(0);
- + } else if (CURRENT->errors == RECALIBRATE_ERRORS) {
- + printk("fd%d: recalibrating\n", SelectedDrive);
- + if (SelectedDrive != -1)
- + unit[SelectedDrive].track = -1;
- + }
- + redo_fd_request();
- +}
- +
- +
- +
- +#define SET_IRQ_HANDLER(proc) do { FloppyIRQHandler = (proc); } while(0)
- +
- +
- +/* do_fd_action() is the general procedure for a fd request: All
- + * required parameter settings (drive select, side select, track
- + * position) are checked and set if needed. For each of these
- + * parameters and the actual reading or writing exist two functions:
- + * one that starts the setting (or skips it if possible) and one
- + * callback for the "done" interrupt. Each done func calls the next
- + * set function to propagate the request down to fd_rwsec_done().
- + */
- +
- +static void do_fd_action(int drive)
- +{
- + DPRINT(("do_fd_action unit[drive].track=%d\n", unit[drive].track));
- +
- +#ifdef TRACKBUFFER
- + repeat:
- +
- + if (IS_BUFFERED( drive, ReqSide, ReqTrack )) {
- + if (ReqCmd == READ) {
- + copy_buffer( SECTOR_BUFFER(ReqSector), ReqData );
- + if (++ReqCnt < CURRENT->current_nr_sectors) {
- + /* read next sector */
- + setup_req_params( drive );
- + goto repeat;
- + }
- + else {
- + /* all sectors finished */
- + CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
- + CURRENT->sector += CURRENT->current_nr_sectors;
- + end_request( 1 );
- + redo_fd_request();
- + return;
- + }
- + }
- + else {
- + /* cmd == WRITE, pay attention to track buffer
- + * consistency! */
- + copy_buffer( ReqData, SECTOR_BUFFER(ReqSector) );
- + }
- + }
- +#endif
- +
- + if (SelectedDrive != drive) {
- + /*unit[drive].track = -1; DAG */
- + fd_select_drive(drive);
- + };
- +
- +
- + if (unit[drive].track == -1)
- + fd_calibrate();
- + else if (unit[drive].track != ReqTrack << unit[drive].disktype->stretch)
- + fd_seek();
- + else
- + fd_rwsec();
- +}
- +
- +
- +/* Seek to track 0 if the current track is unknown */
- +
- +static void fd_calibrate(void)
- +{
- + DPRINT(("fd_calibrate\n"));
- + if (unit[SelectedDrive].track >= 0) {
- + fd_calibrate_done(0);
- + return;
- + }
- + DPRINT(("fd_calibrate (after track compare)\n"));
- + SET_IRQ_HANDLER(fd_calibrate_done);
- + /* we can't verify, since the speed may be incorrect */
- + FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_RESTORE | unit[SelectedDrive].steprate);
- +
- + NeedSeek = 1;
- + MotorOn = 1;
- + START_TIMEOUT();
- + /* wait for IRQ */
- +}
- +
- +
- +static void fd_calibrate_done(int status)
- +{
- + printk("fd_calibrate_done()\n");
- + STOP_TIMEOUT();
- +
- + /* set the correct speed now */
- + if (status & FDC1772STAT_RECNF) {
- + printk("fd%d: restore failed\n", SelectedDrive);
- + fd_error();
- + } else {
- + unit[SelectedDrive].track = 0;
- + fd_seek();
- + }
- +}
- +
- +
- +/* Seek the drive to the requested track. The drive must have been
- + * calibrated at some point before this.
- + */
- +
- +static void fd_seek(void)
- +{
- + DPRINT(("fd_seek() to track %d (unit[SelectedDrive].track=%d)\n", ReqTrack,
- + unit[SelectedDrive].track));
- + if (unit[SelectedDrive].track == ReqTrack <<
- + unit[SelectedDrive].disktype->stretch) {
- + fd_seek_done(0);
- + return;
- + }
- + FDC1772_WRITE(FDC1772REG_DATA, ReqTrack <<
- + unit[SelectedDrive].disktype->stretch);
- + udelay(25);
- + SET_IRQ_HANDLER(fd_seek_done);
- + FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK | unit[SelectedDrive].steprate);
- +
- + MotorOn = 1;
- + set_head_settle_flag();
- + START_TIMEOUT();
- + /* wait for IRQ */
- +}
- +
- +
- +static void fd_seek_done(int status)
- +{
- + DPRINT(("fd_seek_done()\n"));
- + STOP_TIMEOUT();
- +
- + /* set the correct speed */
- + if (status & FDC1772STAT_RECNF) {
- + printk("fd%d: seek error (to track %d)\n",
- + SelectedDrive, ReqTrack);
- + /* we don't know exactly which track we are on now! */
- + unit[SelectedDrive].track = -1;
- + fd_error();
- + } else {
- + unit[SelectedDrive].track = ReqTrack <<
- + unit[SelectedDrive].disktype->stretch;
- + NeedSeek = 0;
- + fd_rwsec();
- + }
- +}
- +
- +
- +/* This does the actual reading/writing after positioning the head
- + * over the correct track.
- + */
- +
- +#ifdef TRACKBUFFER
- +static int MultReadInProgress = 0;
- +#endif
- +
- +
- +static void fd_rwsec(void)
- +{
- + unsigned long paddr, flags;
- + unsigned int rwflag, old_motoron;
- + unsigned int track;
- +
- + DPRINT(("fd_rwsec(), Sec=%d, Access=%c\n", ReqSector, ReqCmd == WRITE ? 'w' : 'r'));
- + if (ReqCmd == WRITE) {
- + /*cache_push( (unsigned long)ReqData, 512 ); */
- + paddr = (unsigned long) ReqData;
- + rwflag = 0x100;
- + } else {
- +#ifdef TRACKBUFFER
- + if (read_track)
- + paddr = (unsigned long)PhysTrackBuffer;
- + else
- + paddr =(unsigned long)PhysDMABuffer;
- +#else
- + paddr = (unsigned long)PhysDMABuffer;
- +#endif
- + rwflag = 0;
- + }
- +
- + DPRINT(("fd_rwsec() before sidesel rwflag=%d sec=%d trk=%d\n", rwflag,
- + ReqSector, FDC1772_READ(FDC1772REG_TRACK)));
- + fd_select_side(ReqSide);
- +
- + /*DPRINT(("fd_rwsec() before start sector \n")); */
- + /* Start sector of this operation */
- +#ifdef TRACKBUFFER
- + FDC1772_WRITE( FDC1772REG_SECTOR, !read_track ? ReqSector : 1 );
- +#else
- + FDC1772_WRITE( FDC1772REG_SECTOR, ReqSector );
- +#endif
- +
- + /* Cheat for track if stretch != 0 */
- + if (unit[SelectedDrive].disktype->stretch) {
- + track = FDC1772_READ(FDC1772REG_TRACK);
- + FDC1772_WRITE(FDC1772REG_TRACK, track >>
- + unit[SelectedDrive].disktype->stretch);
- + }
- + udelay(25);
- +
- + DPRINT(("fd_rwsec() before setup DMA \n"));
- + /* Setup DMA - Heavily modified by DAG */
- + save_flags(flags);
- + cliIF();
- + disable_dma(FLOPPY_DMA);
- + set_dma_mode(FLOPPY_DMA, rwflag ? DMA_MODE_WRITE : DMA_MODE_READ);
- + set_dma_addr(FLOPPY_DMA, (long) paddr); /* DAG - changed from Atari specific */
- +#ifdef TRACKBUFFER
- + set_dma_count(FLOPPY_DMA,(!read_track ? 1 : unit[SelectedDrive].disktype->spt)*512);
- +#else
- + set_dma_count(FLOPPY_DMA, 512); /* Block/sector size - going to have to change */
- +#endif
- + SET_IRQ_HANDLER(fd_rwsec_done);
- + /* Turn on dma int */
- + enable_dma(FLOPPY_DMA);
- + restore_flags(flags);
- + /* Now give it something to do */
- + FDC1772_WRITE(FDC1772REG_CMD, (rwflag ? (FDC1772CMD_WRSEC | FDC1772CMDADD_P) :
- +#ifdef TRACKBUFFER
- + (FDC1772CMD_RDSEC | (read_track ? FDC1772CMDADD_M : 0))
- +#else
- + FDC1772CMD_RDSEC
- +#endif
- + ));
- +
- + DPRINT(("fd_rwsec() after DMA setup flags=0x%08x\n", flags));
- + /*sti(); *//* DAG - Hmm */
- + /* Hmm - should do something DAG */
- + old_motoron = MotorOn;
- + MotorOn = 1;
- + NeedSeek = 1;
- +
- + /* wait for interrupt */
- +
- +#ifdef TRACKBUFFER
- + if (read_track) {
- + /* If reading a whole track, wait about one disk rotation and
- + * then check if all sectors are read. The FDC will even
- + * search for the first non-existant sector and need 1 sec to
- + * recognise that it isn't present :-(
- + */
- + readtrack_timer.expires = jiffies + HZ/5 + (old_motoron ? 0 : HZ);
- + /* 1 rot. + 5 rot.s if motor was off */
- + add_timer( &readtrack_timer );
- + MultReadInProgress = 1;
- + }
- +#endif
- +
- + /*DPRINT(("fd_rwsec() before START_TIMEOUT \n")); */
- + START_TIMEOUT();
- + /*DPRINT(("fd_rwsec() after START_TIMEOUT \n")); */
- +}
- +
- +
- +#ifdef TRACKBUFFER
- +
- +static void fd_readtrack_check( unsigned long dummy )
- +
- +{ unsigned long flags, addr;
- + extern unsigned char *fdc1772_dataaddr;
- +
- + save_flags(flags);
- + cliIF();
- +
- + del_timer( &readtrack_timer );
- +
- + if (!MultReadInProgress) {
- + /* This prevents a race condition that could arise if the
- + * interrupt is triggered while the calling of this timer
- + * callback function takes place. The IRQ function then has
- + * already cleared 'MultReadInProgress' when control flow
- + * gets here.
- + */
- + restore_flags(flags);
- + return;
- + }
- +
- + /* get the current DMA address */
- + addr=*fdc1772_dataaddr; /* DAG - ? */
- +
- + if (addr >= PhysTrackBuffer + unit[SelectedDrive].disktype->spt*512) {
- + /* already read enough data, force an FDC interrupt to stop
- + * the read operation
- + */
- + SET_IRQ_HANDLER( NULL );
- + restore_flags(flags);
- + DPRINT(("fd_readtrack_check(): done\n"));
- + FDC1772_WRITE( FDC1772REG_CMD, FDC1772CMD_FORCI );
- + udelay(25);
- +
- + /* No error until now -- the FDC would have interrupted
- + * otherwise!
- + */
- + fd_rwsec_done( 0 );
- + }
- + else {
- + /* not yet finished, wait another tenth rotation */
- + restore_flags(flags);
- + DPRINT(("fd_readtrack_check(): not yet finished\n"));
- + readtrack_timer.expires = jiffies + HZ/5/10;
- + add_timer( &readtrack_timer );
- + }
- +}
- +
- +#endif
- +
- +static void fd_rwsec_done(int status)
- +{
- + unsigned int track;
- +
- + DPRINT(("fd_rwsec_done() status=%d\n", status));
- +
- +#ifdef TRACKBUFFER
- + if (read_track && !MultReadInProgress) return;
- + MultReadInProgress = 0;
- +
- + STOP_TIMEOUT();
- +
- + if (read_track)
- + del_timer( &readtrack_timer );
- +#endif
- +
- +
- + /* Correct the track if stretch != 0 */
- + if (unit[SelectedDrive].disktype->stretch) {
- + track = FDC1772_READ(FDC1772REG_TRACK);
- + FDC1772_WRITE(FDC1772REG_TRACK, track <<
- + unit[SelectedDrive].disktype->stretch);
- + }
- + if (ReqCmd == WRITE && (status & FDC1772STAT_WPROT)) {
- + printk("fd%d: is write protected\n", SelectedDrive);
- + goto err_end;
- + }
- + if ((status & FDC1772STAT_RECNF)
- +#ifdef TRACKBUFFER
- + /* RECNF is no error after a multiple read when the FDC
- + * searched for a non-existant sector!
- + */
- + && !(read_track &&
- + FDC1772_READ(FDC1772REG_SECTOR) > unit[SelectedDrive].disktype->spt)
- +#endif
- + ) {
- + if (Probing) {
- + if (unit[SelectedDrive].disktype > disk_type) {
- + /* try another disk type */
- + unit[SelectedDrive].disktype--;
- + floppy_sizes[SelectedDrive]
- + = unit[SelectedDrive].disktype->blocks >> 1;
- + } else
- + Probing = 0;
- + } else {
- + /* record not found, but not probing. Maybe stretch wrong ? Restart probing */
- + if (unit[SelectedDrive].autoprobe) {
- + unit[SelectedDrive].disktype = disk_type + NUM_DISK_TYPES - 1;
- + floppy_sizes[SelectedDrive]
- + = unit[SelectedDrive].disktype->blocks >> 1;
- + Probing = 1;
- + }
- + }
- + if (Probing) {
- + setup_req_params(SelectedDrive);
- +#ifdef TRACKBUFFER
- + BufferDrive = -1;
- +#endif
- + do_fd_action(SelectedDrive);
- + return;
- + }
- + printk("fd%d: sector %d not found (side %d, track %d)\n",
- + SelectedDrive, FDC1772_READ(FDC1772REG_SECTOR), ReqSide, ReqTrack);
- + goto err_end;
- + }
- + if (status & FDC1772STAT_CRC) {
- + printk("fd%d: CRC error (side %d, track %d, sector %d)\n",
- + SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR));
- + goto err_end;
- + }
- + if (status & FDC1772STAT_LOST) {
- + printk("fd%d: lost data (side %d, track %d, sector %d)\n",
- + SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR));
- + goto err_end;
- + }
- + Probing = 0;
- +
- + if (ReqCmd == READ) {
- +#ifdef TRACKBUFFER
- + if (!read_track)
- + {
- + /*cache_clear (PhysDMABuffer, 512);*/
- + copy_buffer (DMABuffer, ReqData);
- + }
- + else
- + {
- + /*cache_clear (PhysTrackBuffer, MAX_SECTORS * 512);*/
- + BufferDrive = SelectedDrive;
- + BufferSide = ReqSide;
- + BufferTrack = ReqTrack;
- + copy_buffer (SECTOR_BUFFER (ReqSector), ReqData);
- + }
- +#else
- + /*cache_clear( PhysDMABuffer, 512 ); */
- + copy_buffer(DMABuffer, ReqData);
- +#endif
- + }
- + if (++ReqCnt < CURRENT->current_nr_sectors) {
- + /* read next sector */
- + setup_req_params(SelectedDrive);
- + do_fd_action(SelectedDrive);
- + } else {
- + /* all sectors finished */
- + CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
- + CURRENT->sector += CURRENT->current_nr_sectors;
- + end_request(1);
- + redo_fd_request();
- + }
- + return;
- +
- + err_end:
- +#ifdef TRACKBUFFER
- + BufferDrive = -1;
- +#endif
- +
- + fd_error();
- +}
- +
- +
- +static void fd_times_out(unsigned long dummy)
- +{
- + SET_IRQ_HANDLER(NULL);
- + /* If the timeout occured while the readtrack_check timer was
- + * active, we need to cancel it, else bad things will happen */
- + del_timer( &readtrack_timer );
- + FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI);
- + udelay(25);
- +
- + printk("floppy timeout\n");
- + STOP_TIMEOUT(); /* hmm - should we do this ? */
- + fd_error();
- +}
- +
- +
- +/* The (noop) seek operation here is needed to make the WP bit in the
- + * FDC1772 status register accessible for check_change. If the last disk
- + * operation would have been a RDSEC, this bit would always read as 0
- + * no matter what :-( To save time, the seek goes to the track we're
- + * already on.
- + */
- +
- +static void finish_fdc(void)
- +{
- + /* DAG - just try without this dummy seek! */
- + finish_fdc_done(0);
- + return;
- +
- + if (!NeedSeek) {
- + finish_fdc_done(0);
- + } else {
- + DPRINT(("finish_fdc: dummy seek started\n"));
- + FDC1772_WRITE(FDC1772REG_DATA, unit[SelectedDrive].track);
- + SET_IRQ_HANDLER(finish_fdc_done);
- + FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK);
- + MotorOn = 1;
- + START_TIMEOUT();
- + /* we must wait for the IRQ here, because the ST-DMA is
- + * released immediatly afterwards and the interrupt may be
- + * delivered to the wrong driver.
- + */
- + }
- +}
- +
- +
- +static void finish_fdc_done(int dummy)
- +{
- + unsigned long flags;
- +
- + DPRINT(("finish_fdc_done entered\n"));
- + STOP_TIMEOUT();
- + NeedSeek = 0;
- +
- + if ((timer_active & (1 << FLOPPY_TIMER)) &&
- + timer_table[FLOPPY_TIMER].expires < jiffies + 5)
- + /* If the check for a disk change is done too early after this
- + * last seek command, the WP bit still reads wrong :-((
- + */
- + timer_table[FLOPPY_TIMER].expires = jiffies + 5;
- + else {
- + /* START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY ); */
- + };
- + del_timer(&motor_off_timer);
- + START_MOTOR_OFF_TIMER(FD_MOTOR_OFF_DELAY);
- +
- + save_flags(flags);
- + cli();
- + /* stdma_release(); - not sure if I should do something DAG */
- + fdc_busy = 0;
- + wake_up(&fdc_wait);
- + restore_flags(flags);
- +
- + DPRINT(("finish_fdc() finished\n"));
- +}
- +
- +
- +/* Prevent "aliased" accesses. */
- +static fd_ref[4] =
- +{0, 0, 0, 0};
- +static fd_device[4] =
- +{0, 0, 0, 0};
- +
- +/*
- + * Current device number. Taken either from the block header or from the
- + * format request descriptor.
- + */
- +#define CURRENT_DEVICE (CURRENT->rq_dev)
- +
- +/* Current error count. */
- +#define CURRENT_ERRORS (CURRENT->errors)
- +
- +
- +/* dummy for blk.h */
- +static void floppy_off(unsigned int nr)
- +{
- +}
- +
- +
- +/* On the old arcs write protect depends on the particular model
- + of machine. On the A310, R140, and A440 there is a disc changed
- + detect, however on the A4x0/1 range there is not. There
- + is nothing to tell you which machine your on.
- + At the moment I'm just marking changed always. I've
- + left the Atari's 'change on write protect change' code in this
- + part (but nothing sets it).
- + RiscOS apparently checks the disc serial number etc. to detect changes
- + - but if it sees a disc change line go high (?) it flips to using
- + it. Well maybe I'll add that in the future (!?)
- +*/
- +static int check_floppy_change(dev_t dev)
- +{
- + unsigned int drive = (dev & 0x03);
- +
- + if (MAJOR(dev) != MAJOR_NR) {
- + printk("floppy_changed: not a floppy\n");
- + return 0;
- + }
- + if (test_bit(drive, &fake_change)) {
- + /* simulated change (e.g. after formatting) */
- + return 1;
- + }
- + if (test_bit(drive, &changed_floppies)) {
- + /* surely changed (the WP signal changed at least once) */
- + return 1;
- + }
- + if (unit[drive].wpstat) {
- + /* WP is on -> could be changed: to be sure, buffers should be
- + * invalidated...
- + */
- + return 1;
- + }
- + return 1; /* DAG - was 0 */
- +}
- +
- +static int floppy_revalidate(dev_t dev)
- +{
- + int drive = dev & 3;
- +
- + if (test_bit(drive, &changed_floppies) || test_bit(drive, &fake_change)
- + || unit[drive].disktype == 0) {
- +#ifdef TRACKBUFFER
- + BufferDrive = -1;
- +#endif
- + clear_bit(drive, &fake_change);
- + clear_bit(drive, &changed_floppies);
- + unit[drive].disktype = 0;
- + }
- + return 0;
- +}
- +
- +static __inline__ void copy_buffer(void *from, void *to)
- +{
- + ulong *p1 = (ulong *) from, *p2 = (ulong *) to;
- + int cnt;
- +
- + for (cnt = 512 / 4; cnt; cnt--)
- + *p2++ = *p1++;
- +}
- +
- +
- +/* This sets up the global variables describing the current request. */
- +
- +static void setup_req_params(int drive)
- +{
- + int block = ReqBlock + ReqCnt;
- +
- + ReqTrack = block / unit[drive].disktype->spt;
- + ReqSector = block - ReqTrack * unit[drive].disktype->spt + 1;
- + ReqSide = ReqTrack & 1;
- + ReqTrack >>= 1;
- + ReqData = ReqBuffer + 512 * ReqCnt;
- +
- +#ifdef TRACKBUFFER
- + read_track = (ReqCmd == READ && CURRENT_ERRORS == 0);
- +#endif
- +
- + DPRINT(("Request params: Si=%d Tr=%d Se=%d Data=%08lx\n", ReqSide,
- + ReqTrack, ReqSector, (unsigned long) ReqData));
- +}
- +
- +
- +static void redo_fd_request(void)
- +{
- + int device, drive, type;
- + struct archy_floppy_struct *floppy;
- +
- + DPRINT(("redo_fd_request: CURRENT=%08lx CURRENT->rq_dev=%04x CURRENT->sector=%ld\n",
- + (unsigned long) CURRENT, CURRENT ? CURRENT->rq_dev : 0,
- + CURRENT ? CURRENT->sector : 0));
- +
- + if (CURRENT && CURRENT->rq_dev < 0)
- + goto the_end;
- +
- + repeat:
- +
- + if (!CURRENT)
- + goto the_end;
- +
- + if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
- + panic(DEVICE_NAME ": request list destroyed");
- +
- + if (CURRENT->bh) {
- + if (!buffer_locked(CURRENT->bh))
- + panic(DEVICE_NAME ": block not locked");
- + }
- + device = MINOR(CURRENT_DEVICE);
- + drive = device & 3;
- + type = device >> 2;
- + floppy = &unit[drive];
- +
- + if (!floppy->connected) {
- + /* drive not connected */
- + printk("Unknown Device: fd%d\n", drive);
- + end_request(0);
- + goto repeat;
- + }
- + if (type == 0) {
- + if (!floppy->disktype) {
- + Probing = 1;
- + floppy->disktype = disk_type + NUM_DISK_TYPES - 1;
- + floppy_sizes[drive] = floppy->disktype->blocks >> 1;
- + floppy->autoprobe = 1;
- + }
- + } else {
- + /* user supplied disk type */
- + --type;
- + if (type >= NUM_DISK_TYPES) {
- + printk("fd%d: invalid disk format", drive);
- + end_request(0);
- + goto repeat;
- + }
- + floppy->disktype = &disk_type[type];
- + floppy_sizes[drive] = disk_type[type].blocks >> 1;
- + floppy->autoprobe = 0;
- + }
- +
- + if (CURRENT->sector + 1 > floppy->disktype->blocks) {
- + end_request(0);
- + goto repeat;
- + }
- + /* stop deselect timer */
- + del_timer(&motor_off_timer);
- +
- + ReqCnt = 0;
- + ReqCmd = CURRENT->cmd;
- + ReqBlock = CURRENT->sector;
- + ReqBuffer = CURRENT->buffer;
- + setup_req_params(drive);
- + do_fd_action(drive);
- +
- + return;
- +
- + the_end:
- + finish_fdc();
- +}
- +
- +static void fd1772_checkint(void)
- +{
- + /*printk("fd1772_checkint %d\n",fdc1772_fdc_int_done);*/
- + if (fdc1772_fdc_int_done)
- + floppy_irqconsequencehandler();
- + if (fdc_busy) {
- + queue_task(&fd1772_tq,&tq_immediate);
- + mark_bh(IMMEDIATE_BH);
- + };
- +};
- +
- +void do_fd_request(void)
- +{
- + unsigned long flags;
- +
- + DPRINT(("do_fd_request for pid %d\n", current->pid));
- + if (fdc_busy) return;
- + save_flags(flags);
- + cli();
- + while (fdc_busy)
- + sleep_on(&fdc_wait);
- + fdc_busy = 1;
- + ENABLE_IRQ();
- + restore_flags(flags);
- +
- + fdc1772_fdc_int_done = 0;
- +
- + redo_fd_request();
- +
- + queue_task(&fd1772_tq,&tq_immediate);
- + mark_bh(IMMEDIATE_BH);
- +}
- +
- +
- +static int invalidate_drive(int rdev)
- +{
- + /* invalidate the buffer track to force a reread */
- +#ifdef TRACKBUFFER
- + BufferDrive = -1;
- +#endif
- +
- + set_bit(rdev & 3, &fake_change);
- + check_disk_change(rdev);
- + return 0;
- +}
- +
- +static int fd_ioctl(struct inode *inode, struct file *filp,
- + unsigned int cmd, unsigned long param)
- +{
- +#define IOCTL_MODE_BIT 8
- +#define OPEN_WRITE_BIT 16
- +#define IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
- +
- + int drive, device;
- +
- + device = inode->i_rdev;
- + switch (cmd) {
- + RO_IOCTLS(inode->i_rdev, param);
- + }
- + drive = MINOR(device);
- + if (!IOCTL_ALLOWED)
- + return -EPERM;
- + switch (cmd) {
- + case FDFMTBEG:
- + return 0;
- + /* case FDC1772LRPRM: ??? DAG what does this do??
- + unit[drive].disktype = NULL;
- + floppy_sizes[drive] = MAX_DISK_SIZE;
- + return invalidate_drive (device); */
- + case FDFMTEND:
- + case FDFLUSH:
- + return invalidate_drive(drive);
- + }
- + if (!suser())
- + return -EPERM;
- + if (drive < 0 || drive > 3)
- + return -EINVAL;
- + switch (cmd) {
- + default:
- + return -EINVAL;
- + }
- + return 0;
- +}
- +
- +
- +/* Initialize the 'unit' variable for drive 'drive' */
- +
- +static void fd_probe(int drive)
- +{
- + unit[drive].connected = 0;
- + unit[drive].disktype = NULL;
- +
- + if (!fd_test_drive_present(drive))
- + return;
- +
- + unit[drive].connected = 1;
- + unit[drive].track = -1; /* If we put the auto detect back in this can go to 0 */
- + unit[drive].steprate = FDC1772STEP_6;
- + MotorOn = 1; /* from probe restore operation! */
- +}
- +
- +
- +/* This function tests the physical presence of a floppy drive (not
- + * whether a disk is inserted). This is done by issuing a restore
- + * command, waiting max. 2 seconds (that should be enough to move the
- + * head across the whole disk) and looking at the state of the "TR00"
- + * signal. This should now be raised if there is a drive connected
- + * (and there is no hardware failure :-) Otherwise, the drive is
- + * declared absent.
- + */
- +
- +static int fd_test_drive_present(int drive)
- +{
- + unsigned long timeout;
- + unsigned char status;
- + int ok;
- +
- + printk("fd_test_drive_present %d\n", drive);
- + if (drive > 1)
- + return (0);
- + return (1); /* Simple hack for the moment - the autodetect doesn't seem to work on arc */
- + fd_select_drive(drive);
- +
- + /* disable interrupt temporarily */
- + DISABLE_IRQ();
- + FDC1772_WRITE(FDC1772REG_TRACK, 0x00); /* was ff00 why? */
- + FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_RESTORE | FDC1772CMDADD_H | FDC1772STEP_6);
- +
- + /*printk("fd_test_drive_present: Going into timeout loop\n"); */
- + for (ok = 0, timeout = jiffies + 2 * HZ + HZ / 2; jiffies < timeout;) {
- + /* What does this piece of atariism do? - query for an interrupt? */
- + /* if (!(mfp.par_dt_reg & 0x20))
- + break; */
- + /* Well this is my nearest guess - quit when we get an FDC interrupt */
- + if (IOC_FIQSTAT & 2)
- + break;
- + }
- +
- + /*printk("fd_test_drive_present: Coming out of timeout loop\n"); */
- + status = FDC1772_READ(FDC1772REG_STATUS);
- + ok = (status & FDC1772STAT_TR00) != 0;
- +
- + /*printk("fd_test_drive_present: ok=%d\n",ok); */
- + /* force interrupt to abort restore operation (FDC1772 would try
- + * about 50 seconds!) */
- + FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI);
- + udelay(500);
- + status = FDC1772_READ(FDC1772REG_STATUS);
- + udelay(20);
- + /*printk("fd_test_drive_present: just before OK code %d\n",ok); */
- +
- + if (ok) {
- + /* dummy seek command to make WP bit accessible */
- + FDC1772_WRITE(FDC1772REG_DATA, 0);
- + FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK);
- + printk("fd_test_drive_present: just before wait for int\n");
- + /* DAG: Guess means wait for interrupt */
- + while (!(IOC_FIQSTAT & 2));
- + printk("fd_test_drive_present: just after wait for int\n");
- + status = FDC1772_READ(FDC1772REG_STATUS);
- + }
- + printk("fd_test_drive_present: just before ENABLE_IRQ\n");
- + ENABLE_IRQ();
- + printk("fd_test_drive_present: about to return\n");
- + return (ok);
- +}
- +
- +
- +/* Look how many and which kind of drives are connected. If there are
- + * floppies, additionally start the disk-change and motor-off timers.
- + */
- +
- +static void config_types(void)
- +{
- + int drive, cnt = 0;
- +
- + printk("Probing floppy drive(s):\n");
- + for (drive = 0; drive < FD_MAX_UNITS; drive++) {
- + fd_probe(drive);
- + if (unit[drive].connected) {
- + printk("fd%d\n", drive);
- + ++cnt;
- + }
- + }
- +
- + if (FDC1772_READ(FDC1772REG_STATUS) & FDC1772STAT_BUSY) {
- + /* If FDC1772 is still busy from probing, give it another FORCI
- + * command to abort the operation. If this isn't done, the FDC1772
- + * will interrupt later and its IRQ line stays low, because
- + * the status register isn't read. And this will block any
- + * interrupts on this IRQ line :-(
- + */
- + FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI);
- + udelay(500);
- + FDC1772_READ(FDC1772REG_STATUS);
- + udelay(20);
- + }
- + if (cnt > 0) {
- + START_MOTOR_OFF_TIMER(FD_MOTOR_OFF_DELAY);
- + if (cnt == 1)
- + fd_select_drive(0);
- + /*START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY ); */
- + }
- +}
- +
- +/*
- + * floppy_open check for aliasing (/dev/fd0 can be the same as
- + * /dev/PS0 etc), and disallows simultaneous access to the same
- + * drive with different device numbers.
- + */
- +
- +static int floppy_open(struct inode *inode, struct file *filp)
- +{
- + int drive;
- + int old_dev;
- +
- + if (!filp) {
- + DPRINT(("Weird, open called with filp=0\n"));
- + return -EIO;
- + }
- + drive = MINOR(inode->i_rdev) & 3;
- + if ((MINOR(inode->i_rdev) >> 2) > NUM_DISK_TYPES)
- + return -ENXIO;
- +
- + old_dev = fd_device[drive];
- +
- + if (fd_ref[drive])
- + if (old_dev != inode->i_rdev)
- + return -EBUSY;
- +
- + if (fd_ref[drive] == -1 || (fd_ref[drive] && filp->f_flags & O_EXCL))
- + return -EBUSY;
- +
- + if (filp->f_flags & O_EXCL)
- + fd_ref[drive] = -1;
- + else
- + fd_ref[drive]++;
- +
- + fd_device[drive] = inode->i_rdev;
- +
- + if (old_dev && old_dev != inode->i_rdev)
- + invalidate_buffers(old_dev);
- +
- + /* Allow ioctls if we have write-permissions even if read-only open */
- + if (filp->f_mode & 2 || permission(inode, 2) == 0)
- + filp->f_mode |= IOCTL_MODE_BIT;
- + if (filp->f_mode & 2)
- + filp->f_mode |= OPEN_WRITE_BIT;
- +
- + if (filp->f_flags & O_NDELAY)
- + return 0;
- +
- + if (filp->f_mode & 3) {
- + check_disk_change(inode->i_rdev);
- + if (filp->f_mode & 2) {
- + if (unit[drive].wpstat) {
- + floppy_release(inode, filp);
- + return -EROFS;
- + }
- + }
- + }
- + return 0;
- +}
- +
- +
- +static void floppy_release(struct inode *inode, struct file *filp)
- +{
- + int drive;
- +
- + drive = inode->i_rdev & 3;
- +
- + if (!filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
- + /* if the file is mounted OR (writable now AND writable at open
- + time) Linus: Does this cover all cases? */
- + block_fsync(inode, filp);
- +
- + if (fd_ref[drive] < 0)
- + fd_ref[drive] = 0;
- + else if (!fd_ref[drive]--) {
- + printk("floppy_release with fd_ref == 0");
- + fd_ref[drive] = 0;
- + }
- +}
- +
- +static struct file_operations floppy_fops =
- +{
- + NULL, /* lseek - default */
- + block_read, /* read - general block-dev read */
- + block_write, /* write - general block-dev write */
- + NULL, /* readdir - bad */
- + NULL, /* select */
- + fd_ioctl, /* ioctl */
- + NULL, /* mmap */
- + floppy_open, /* open */
- + floppy_release, /* release */
- + block_fsync, /* fsync */
- + NULL, /* fasync */
- + check_floppy_change, /* media_change */
- + floppy_revalidate, /* revalidate */
- +};
- +
- +
- +int floppy_init(void)
- +{
- + int i;
- +
- + if (register_blkdev(MAJOR_NR, "fd", &floppy_fops)) {
- + printk("Unable to get major %d for floppy\n", MAJOR_NR);
- + return;
- + }
- + /* Actually this does almost nothing - we actually do this by starting DMA
- + on channel 1 */
- + /*if (request_irq(FIQ_FD1772+16,fdc1772_comendhandler,SA_INTERRUPT,"fd1772")) {
- + printk("Unable to grab IRQ%d for the floppy (1772) driver\n",FIQ_FD1772+16);
- + return ;
- + }; */
- +
- + if (request_dma(FLOPPY_DMA, "fd1772")) {
- + printk("Unable to grab DMA%d for the floppy (1772) driver\n", FLOPPY_DMA);
- + free_irq(FIQ_FD1772 + 16);
- + return;
- + };
- +
- + if (request_dma(FIQ_FD1772, "fd1772 end")) {
- + printk("Unable to grab DMA%d for the floppy (1772) driver\n", FIQ_FD1772);
- + free_irq(FIQ_FD1772 + 16);
- + return;
- + };
- + enable_dma(FIQ_FD1772); /* This inserts a call to our command end routine */
- +
- + /* initialize variables */
- + SelectedDrive = -1;
- +#ifdef TRACKBUFFER
- + BufferDrive = -1;
- +#endif
- +
- + /* initialize check_change timer */
- + timer_table[FLOPPY_TIMER].fn = check_change;
- + timer_active &= ~(1 << FLOPPY_TIMER);
- +
- +
- +#ifdef TRACKBUFFER
- + DMABuffer = (char *)kmalloc((MAX_SECTORS+1)*512); /* Atari uses 512 - I want to eventually cope with 1K sectors */
- + TrackBuffer = DMABuffer + 512;
- +#else
- + /* Allocate memory for the DMAbuffer - on the Atari this takes it
- + out of some special memory... */
- + DMABuffer = (char *) kmalloc(2048); /* Copes with pretty large sectors */
- +#endif
- +#ifdef TRACKBUFFER
- + BufferDrive = BufferSide = BufferTrack = -1;
- +#endif
- +
- + for (i = 0; i < FD_MAX_UNITS; i++) {
- + unit[i].track = -1;
- + }
- +
- + for (i = 0; i < 256; i++)
- + if ((i >> 2) > 0 && (i >> 2) <= NUM_DISK_TYPES)
- + floppy_sizes[i] = disk_type[(i >> 2) - 1].blocks >> 1;
- + else
- + floppy_sizes[i] = MAX_DISK_SIZE;
- +
- + blk_size[MAJOR_NR] = floppy_sizes;
- + blksize_size[MAJOR_NR] = floppy_blocksizes;
- + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- +
- + config_types();
- +
- + return 0;
- +}
- +
- +/* Just a dummy at the moment */
- +void floppy_setup(char *str, int *ints)
- +{
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/fd1772dma.S linux/arch/arm/drivers/block/fd1772dma.S
- --- linux.orig/arch/arm/drivers/block/fd1772dma.S Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/fd1772dma.S Sat Sep 7 21:09:51 1996
- @@ -0,0 +1,100 @@
- +#include <asm/hardware.h>
- +
- +@ Code for DMA with the 1772 fdc
- +.text
- +
- +
- + .global _fdc1772_dataaddr
- +_fdc1772_fiqdata:
- +@ Number of bytes left to DMA
- + .global _fdc1772_bytestogo
- +_fdc1772_bytestogo:
- + .word 0
- +@ Place to put/get data from in DMA
- + .global _fdc1772_dataaddr
- +_fdc1772_dataaddr:
- + .word 0
- +
- + .global _fdc1772_fdc_int_done
- +_fdc1772_fdc_int_done:
- + .word 0
- + .global _fdc1772_comendstatus
- +_fdc1772_comendstatus:
- + .word 0
- +
- +@ We hang this off DMA channel 1
- + .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
- + 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:
- + mov r8,#IOC_BASE
- + ldrb r9,[r8,#0x34] @ IOC FIQ status
- + tst r9,#1
- + 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}
- + subs r8,r8,#1 @ One less byte to go
- + @ If there was somewhere for this data to go then store it and update pointers
- + 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:
- + @ 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:
- +
- + .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
- + orr r8,r8,#0x10000 @ FDC base
- + ldmia r11,{r9,r10}
- + subs r9,r9,#1 @ One less byte to go
- + @ If there really is some data then get it, store it and update count
- + ldrplb r12,[r10],#1
- + 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:
- + @ Cant branch because this code has been copied down to the FIQ vector
- + ldr pc,[pc,#-4]
- + .word _fdc1772_comendhandler
- +
- + .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:
- + @ The big job is flipping in and out of FIQ mode
- + 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
- + mov r0,r0 @ NOP
- + mov r11,r2
- + teqp r3,#0 @ Normal mode
- + mov r0,r0 @ NOP
- + mov pc,r14
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/floppy.c linux/arch/arm/drivers/block/floppy.c
- --- linux.orig/arch/arm/drivers/block/floppy.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/floppy.c Fri Sep 6 21:08:31 1996
- @@ -0,0 +1,4318 @@
- +/*
- + * linux/arch/arm/drivers/block/floppy.c
- + * [ was linux/drivers/block/floppy.c ]
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + * Copyright (C) 1993, 1994 Alain Knaff
- + * Modifications Copyright (C) 1995 Russell King
- + */
- +/*
- + * 02.12.91 - Changed to static variables to indicate need for reset
- + * and recalibrate. This makes some things easier (output_byte reset
- + * checking etc), and means less interrupt jumping in case of errors,
- + * so the code is hopefully easier to understand.
- + */
- +
- +/*
- + * This file is certainly a mess. I've tried my best to get it working,
- + * but I don't like programming floppies, and I have only one anyway.
- + * Urgel. I should check for more errors, and do more graceful error
- + * recovery. Seems there are problems with several drives. I've tried to
- + * correct them. No promises.
- + */
- +
- +/*
- + * As with hd.c, all routines within this file can (and will) be called
- + * by interrupts, so extreme caution is needed. A hardware interrupt
- + * handler may not sleep, or a kernel panic will happen. Thus I cannot
- + * call "floppy-on" directly, but have to set a special timer interrupt
- + * etc.
- + */
- +
- +/*
- + * 28.02.92 - made track-buffering routines, based on the routines written
- + * by entropy@wintermute.wpi.edu (Lawrence Foard). Linus.
- + */
- +
- +/*
- + * Automatic floppy-detection and formatting written by Werner Almesberger
- + * (almesber@nessie.cs.id.ethz.ch), who also corrected some problems with
- + * the floppy-change signal detection.
- + */
- +
- +/*
- + * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed
- + * FDC data overrun bug, added some preliminary stuff for vertical
- + * recording support.
- + *
- + * 1992/9/17: Added DMA allocation & DMA functions. -- hhb.
- + *
- + * TODO: Errors are still not counted properly.
- + */
- +
- +/* 1992/9/20
- + * Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl)
- + * modeled after the freeware MS-DOS program fdformat/88 V1.8 by
- + * Christoph H. Hochst\"atter.
- + * I have fixed the shift values to the ones I always use. Maybe a new
- + * ioctl() should be created to be able to modify them.
- + * There is a bug in the driver that makes it impossible to format a
- + * floppy as the first thing after bootup.
- + */
- +
- +/*
- + * 1993/4/29 -- Linus -- cleaned up the timer handling in the kernel, and
- + * this helped the floppy driver as well. Much cleaner, and still seems to
- + * work.
- + */
- +
- +/* 1994/6/24 --bbroad-- added the floppy table entries and made
- + * minor modifications to allow 2.88 floppies to be run.
- + */
- +
- +/* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more
- + * disk types.
- + */
- +
- +/*
- + * 1994/8/8 -- Alain Knaff -- Switched to fdpatch driver: Support for bigger
- + * format bug fixes, but unfortunately some new bugs too...
- + */
- +
- +/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
- + * errors to allow safe writing by specialized programs.
- + */
- +
- +/* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks
- + * by defining bit 1 of the "stretch" parameter to mean put sectors on the
- + * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's
- + * drives are "upside-down").
- + */
- +
- +/*
- + * 1995/8/26 -- Andreas Busse -- added Mips support.
- + */
- +
- +/*
- + * 1995/8/16 -- Russell King -- altered method for turning floppy drives on
- + * for the arm.
- + */
- +
- +/*
- + * 1995/10/18 -- Ralf Baechle -- Portability cleanup; move machine dependent
- + * features to asm/floppy.h.
- + */
- +
- +
- +#define FLOPPY_SANITY_CHECK
- +#undef FLOPPY_SILENT_DCL_CLEAR
- +
- +#define DEBUGT 2
- +#define DCL_DEBUG /* debug disk change line */
- +
- +/* do print messages for unexpected interrupts */
- +static int print_unex=1;
- +#include <linux/utsname.h>
- +#include <linux/module.h>
- +
- +/* the following is the mask of allowed drives. By default units 2 and
- + * 3 of both floppy controllers are disabled, because switching on the
- + * motor of these drives causes system hangs on some PCI computers. drive
- + * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
- + * a drive is allowed. */
- +static int allowed_drive_mask=0x33;
- +
- +#define FLOPPY_IRQ 12
- +#define FLOPPY_DMA 2
- +#define FDC1 0x3f0
- +#define N_DRIVE 8
- +#define N_FDC 1
- +static int FDC2=-1;
- +#define FLOPPY0_TYPE 4
- +#define FLOPPY1_TYPE 4
- +
- +#include <linux/sched.h>
- +#include <linux/fs.h>
- +#include <linux/kernel.h>
- +#include <linux/timer.h>
- +#include <linux/tqueue.h>
- +#define FDPATCHES
- +#include <linux/fdreg.h>
- +
- +
- +#include <linux/fd.h>
- +
- +
- +#define OLDFDRAWCMD 0x020d /* send a raw command to the FDC */
- +
- +struct old_floppy_raw_cmd {
- + void *data;
- + long length;
- +
- + unsigned char rate;
- + unsigned char flags;
- + unsigned char cmd_count;
- + unsigned char cmd[9];
- + unsigned char reply_count;
- + unsigned char reply[7];
- + int track;
- +};
- +
- +#include <linux/errno.h>
- +#include <linux/malloc.h>
- +#include <linux/mm.h>
- +#include <linux/string.h>
- +#include <linux/fcntl.h>
- +#include <linux/delay.h>
- +
- +#include <linux/ioport.h>
- +#include <linux/interrupt.h>
- +
- +#include <asm/dma.h>
- +#include <asm/irq.h>
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <asm/segment.h>
- +
- +static int use_virtual_dma=0; /* virtual DMA for Intel */
- +static unsigned short virtual_dma_port=0x3f0;
- +void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs);
- +static int set_dor(int fdc, char mask, char data);
- +static inline int __get_order(unsigned long size);
- +#include <asm/floppy.h>
- +
- +
- +#define MAJOR_NR FLOPPY_MAJOR
- +
- +#include <linux/blk.h>
- +#include <linux/cdrom.h> /* for the compatibility eject ioctl */
- +
- +
- +#ifndef FLOPPY_MOTOR_MASK
- +#define FLOPPY_MOTOR_MASK 0xf0
- +#endif
- +
- +#ifndef fd_get_dma_residue
- +#define fd_get_dma_residue() get_dma_residue(FLOPPY_DMA)
- +#endif
- +
- +/* Dma Memory related stuff */
- +
- +/* Pure 2^n version of get_order */
- +static inline int __get_order(unsigned long size)
- +{
- + int order;
- +
- + size = (size-1) >> (PAGE_SHIFT-1);
- + order = -1;
- + do {
- + size >>= 1;
- + order++;
- + } while (size);
- + return order;
- +}
- +
- +#ifndef fd_dma_mem_free
- +#define fd_dma_mem_free(addr, size) free_pages(addr, __get_order(size))
- +#endif
- +
- +#ifndef fd_dma_mem_alloc
- +#define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,__get_order(size))
- +#endif
- +
- +/* End dma memory related stuff */
- +
- +static unsigned int fake_change = 0;
- +static int initialising=1;
- +
- +static inline int TYPE(kdev_t x) {
- + return (MINOR(x)>>2) & 0x1f;
- +}
- +static inline int DRIVE(kdev_t x) {
- + return (MINOR(x)&0x03) | ((MINOR(x)&0x80) >> 5);
- +}
- +#define ITYPE(x) (((x)>>2) & 0x1f)
- +#define TOMINOR(x) ((x & 3) | ((x & 4) << 5))
- +#define UNIT(x) ((x) & 0x03) /* drive on fdc */
- +#define FDC(x) (((x) & 0x04) >> 2) /* fdc of drive */
- +#define REVDRIVE(fdc, unit) ((unit) + ((fdc) << 2))
- + /* reverse mapping from unit and fdc to drive */
- +#define DP (&drive_params[current_drive])
- +#define DRS (&drive_state[current_drive])
- +#define DRWE (&write_errors[current_drive])
- +#define FDCS (&fdc_state[fdc])
- +#define CLEARF(x) (clear_bit(x##_BIT, &DRS->flags))
- +#define SETF(x) (set_bit(x##_BIT, &DRS->flags))
- +#define TESTF(x) (test_bit(x##_BIT, &DRS->flags))
- +
- +#define UDP (&drive_params[drive])
- +#define UDRS (&drive_state[drive])
- +#define UDRWE (&write_errors[drive])
- +#define UFDCS (&fdc_state[FDC(drive)])
- +#define UCLEARF(x) (clear_bit(x##_BIT, &UDRS->flags))
- +#define USETF(x) (set_bit(x##_BIT, &UDRS->flags))
- +#define UTESTF(x) (test_bit(x##_BIT, &UDRS->flags))
- +
- +#define DPRINT(format, args...) printk(DEVICE_NAME "%d: " format, current_drive , ## args)
- +
- +#define PH_HEAD(floppy,head) (((((floppy)->stretch & 2) >>1) ^ head) << 2)
- +#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH)
- +
- +#define CLEARSTRUCT(x) memset((x), 0, sizeof(*(x)))
- +
- +#define INT_OFF save_flags(flags); cli()
- +#define INT_ON restore_flags(flags)
- +
- +/* read/write */
- +#define COMMAND raw_cmd->cmd[0]
- +#define DR_SELECT raw_cmd->cmd[1]
- +#define TRACK raw_cmd->cmd[2]
- +#define HEAD raw_cmd->cmd[3]
- +#define SECTOR raw_cmd->cmd[4]
- +#define SIZECODE raw_cmd->cmd[5]
- +#define SECT_PER_TRACK raw_cmd->cmd[6]
- +#define GAP raw_cmd->cmd[7]
- +#define SIZECODE2 raw_cmd->cmd[8]
- +#define NR_RW 9
- +
- +/* format */
- +#define F_SIZECODE raw_cmd->cmd[2]
- +#define F_SECT_PER_TRACK raw_cmd->cmd[3]
- +#define F_GAP raw_cmd->cmd[4]
- +#define F_FILL raw_cmd->cmd[5]
- +#define NR_F 6
- +
- +/*
- + * Maximum disk size (in kilobytes). This default is used whenever the
- + * current disk size is unknown.
- + * [Now it is rather a minimum]
- + */
- +#define MAX_DISK_SIZE 2 /* 3984*/
- +
- +#define K_64 0x10000 /* 64kB */
- +
- +/*
- + * globals used by 'result()'
- + */
- +#define MAX_REPLIES 16
- +static unsigned char reply_buffer[MAX_REPLIES];
- +static int inr; /* size of reply buffer, when called from interrupt */
- +#define ST0 (reply_buffer[0])
- +#define ST1 (reply_buffer[1])
- +#define ST2 (reply_buffer[2])
- +#define ST3 (reply_buffer[0]) /* result of GETSTATUS */
- +#define R_TRACK (reply_buffer[3])
- +#define R_HEAD (reply_buffer[4])
- +#define R_SECTOR (reply_buffer[5])
- +#define R_SIZECODE (reply_buffer[6])
- +
- +#define SEL_DLY (2*HZ/100)
- +
- +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
- +/*
- + * this struct defines the different floppy drive types.
- + */
- +static struct {
- + struct floppy_drive_params params;
- + const char *name; /* name printed while booting */
- +} default_drive_params[]= {
- +/* NOTE: the time values in jiffies should be in msec!
- + CMOS drive type
- + | Maximum data rate supported by drive type
- + | | Head load time, msec
- + | | | Head unload time, msec (not used)
- + | | | | Step rate interval, usec
- + | | | | | Time needed for spinup time (jiffies)
- + | | | | | | Timeout for spinning down (jiffies)
- + | | | | | | | Spindown offset (where disk stops)
- + | | | | | | | | Select delay
- + | | | | | | | | | RPS
- + | | | | | | | | | | Max number of tracks
- + | | | | | | | | | | | Interrupt timeout
- + | | | | | | | | | | | | Max nonintlv. sectors
- + | | | | | | | | | | | | | -Max Errors- flags */
- +{{0, 500, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
- + 0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" },
- +
- +{{1, 300, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0,
- + 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
- +
- +{{2, 500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0,
- + 0, { 2, 5, 6,23,10,20,11, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
- +
- +{{3, 250, 16, 16, 3000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
- + 0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/
- +#if 0
- +{{4, 500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
- + 0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
- +#else
- +{{4, 500, 16, 16, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,0}, FTD_MSG,
- + 0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
- +#endif
- +{{5, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
- + 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
- +
- +{{6, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
- + 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/
- +/* | --autodetected formats--- | | |
- + * read_track | | Name printed when booting
- + * | Native format
- + * Frequency of disk change checks */
- +};
- +
- +static struct floppy_drive_params drive_params[N_DRIVE];
- +static struct floppy_drive_struct drive_state[N_DRIVE];
- +static struct floppy_write_errors write_errors[N_DRIVE];
- +static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
- +
- +/*
- + * This struct defines the different floppy types.
- + *
- + * Bit 0 of 'stretch' tells if the tracks need to be doubled for some
- + * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch'
- + * tells if the disk is in Commodore 1581 format, which means side 0 sectors
- + * are located on side 1 of the disk but with a side 0 ID, and vice-versa.
- + * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
- + * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
- + * side 0 is on physical side 0 (but with the misnamed sector IDs).
- + * 'stretch' should probably be renamed to something more general, like
- + * 'options'. Other parameters should be self-explanatory (see also
- + * setfdprm(8)).
- + */
- +/*
- + Size
- + | Sectors per track
- + | | Head
- + | | | Tracks
- + | | | | Stretch
- + | | | | | Gap 1 size
- + | | | | | | Data rate, | 0x40 for perp
- + | | | | | | | Spec1 (stepping rate, head unload
- + | | | | | | | | /fmt gap (gap2) */
- +static struct floppy_struct floppy_type[32] = {
- + { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */
- + { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360" }, /* 1 360KB PC */
- + { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" }, /* 2 1.2MB AT */
- + { 720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360" }, /* 3 360KB SS 3.5" */
- + { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720" }, /* 4 720KB 3.5" */
- + { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" }, /* 5 360KB AT */
- + { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */
- + { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */
- + { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */
- + { 6240,39,2,80,0,0x1B,0x43,0xAF,0x28,"E3120" }, /* 9 3.12MB 3.5" */
- +
- + { 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25" */
- + { 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5" */
- + { 820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410" }, /* 12 410KB 5.25" */
- + { 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820" }, /* 13 820KB 3.5" */
- + { 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" }, /* 14 1.48MB 5.25" */
- + { 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" }, /* 15 1.72MB 3.5" */
- + { 840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420" }, /* 16 420KB 5.25" */
- + { 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830" }, /* 17 830KB 3.5" */
- + { 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" }, /* 18 1.49MB 5.25" */
- + { 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5" */
- +
- + { 1760,11,2,80,0,0x1C,0x09,0xCF,0x00,"h880" }, /* 20 880KB 5.25" */
- + { 2080,13,2,80,0,0x1C,0x01,0xCF,0x00,"D1040" }, /* 21 1.04MB 3.5" */
- + { 2240,14,2,80,0,0x1C,0x19,0xCF,0x00,"D1120" }, /* 22 1.12MB 3.5" */
- + { 3200,20,2,80,0,0x1C,0x20,0xCF,0x2C,"h1600" }, /* 23 1.6MB 5.25" */
- + { 3520,22,2,80,0,0x1C,0x08,0xCF,0x2e,"H1760" }, /* 24 1.76MB 3.5" */
- + { 3840,24,2,80,0,0x1C,0x20,0xCF,0x00,"H1920" }, /* 25 1.92MB 3.5" */
- + { 6400,40,2,80,0,0x25,0x5B,0xCF,0x00,"E3200" }, /* 26 3.20MB 3.5" */
- + { 7040,44,2,80,0,0x25,0x5B,0xCF,0x00,"E3520" }, /* 27 3.52MB 3.5" */
- + { 7680,48,2,80,0,0x25,0x63,0xCF,0x00,"E3840" }, /* 28 3.84MB 3.5" */
- +
- + { 3680,23,2,80,0,0x1C,0x10,0xCF,0x00,"H1840" }, /* 29 1.84MB 3.5" */
- + { 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800" }, /* 30 800KB 3.5" */
- + { 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5" */
- +};
- +
- +#define NUMBER(x) (sizeof(x) / sizeof(*(x)))
- +#define SECTSIZE (_FD_SECTSIZE(*floppy))
- +
- +/* Auto-detection: Disk type used until the next media change occurs. */
- +static struct floppy_struct *current_type[N_DRIVE] = {
- + NULL, NULL, NULL, NULL,
- + NULL, NULL, NULL, NULL
- +};
- +
- +/*
- + * User-provided type information. current_type points to
- + * the respective entry of this array.
- + */
- +static struct floppy_struct user_params[N_DRIVE];
- +
- +static int floppy_sizes[256];
- +static int floppy_blocksizes[256] = { 0, };
- +
- +/*
- + * The driver is trying to determine the correct media format
- + * while probing is set. rw_interrupt() clears it after a
- + * successful access.
- + */
- +static int probing = 0;
- +
- +/* Synchronization of FDC access. */
- +#define FD_COMMAND_NONE -1
- +#define FD_COMMAND_ERROR 2
- +#define FD_COMMAND_OKAY 3
- +
- +static volatile int command_status = FD_COMMAND_NONE, fdc_busy = 0;
- +static struct wait_queue *fdc_wait = NULL, *command_done = NULL;
- +#define NO_SIGNAL (!(current->signal & ~current->blocked) || !interruptible)
- +#define CALL(x) if ((x) == -EINTR) return -EINTR
- +#define ECALL(x) if ((ret = (x))) return ret;
- +#define _WAIT(x,i) CALL(ret=wait_til_done((x),i))
- +#define WAIT(x) _WAIT((x),interruptible)
- +#define IWAIT(x) _WAIT((x),1)
- +
- +/* Errors during formatting are counted here. */
- +static int format_errors;
- +
- +/* Format request descriptor. */
- +static struct format_descr format_req;
- +
- +/*
- + * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps
- + * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
- + * H is head unload time (1=16ms, 2=32ms, etc)
- + */
- +
- +/*
- + * Track buffer
- + * Because these are written to by the DMA controller, they must
- + * not contain a 64k byte boundary crossing, or data will be
- + * corrupted/lost.
- + */
- +static char *floppy_track_buffer=NULL;
- +static int max_buffer_sectors=0;
- +
- +static int *errors;
- +typedef void (*done_f)(int);
- +static struct cont_t {
- + void (*interrupt)(void); /* this is called after the interrupt of the
- + * main command */
- + void (*redo)(void); /* this is called to retry the operation */
- + void (*error)(void); /* this is called to tally an error */
- + done_f done; /* this is called to say if the operation has
- + * succeeded/failed */
- +} *cont=NULL;
- +
- +static void floppy_ready(void);
- +static void floppy_start(void);
- +static void process_fd_request(void);
- +static void recalibrate_floppy(void);
- +static void floppy_shutdown(void);
- +
- +static int floppy_grab_irq_and_dma(void);
- +static void floppy_release_irq_and_dma(void);
- +
- +/*
- + * The "reset" variable should be tested whenever an interrupt is scheduled,
- + * after the commands have been sent. This is to ensure that the driver doesn't
- + * get wedged when the interrupt doesn't come because of a failed command.
- + * reset doesn't need to be tested before sending commands, because
- + * output_byte is automatically disabled when reset is set.
- + */
- +#define CHECK_RESET { if ( FDCS->reset ){ reset_fdc(); return ; } }
- +static void reset_fdc(void);
- +
- +/*
- + * These are global variables, as that's the easiest way to give
- + * information to interrupts. They are the data used for the current
- + * request.
- + */
- +#define NO_TRACK -1
- +#define NEED_1_RECAL -2
- +#define NEED_2_RECAL -3
- +
- +/* */
- +static int usage_count = 0;
- +
- +
- +/* buffer related variables */
- +static int buffer_track = -1;
- +static int buffer_drive = -1;
- +static int buffer_min = -1;
- +static int buffer_max = -1;
- +
- +/* fdc related variables, should end up in a struct */
- +static struct floppy_fdc_state fdc_state[N_FDC];
- +static int fdc; /* current fdc */
- +
- +static struct floppy_struct *_floppy = floppy_type;
- +static unsigned char current_drive = 0;
- +static long current_count_sectors = 0;
- +static unsigned char sector_t; /* sector in track */
- +
- +#ifndef fd_eject
- +#define fd_eject(x) -EINVAL
- +#endif
- +
- +/*
- + * Floppy_selects1 is the list of DOR's to select a drive n
- + * Floppy_selects2 is the list of DOR's to select drive fd
- + * On initialisation, the floppy list is scanned, and the drives allocated
- + * in the order that they are found. This is done by seeking the drive
- + * to a non-zero track, and then restoring it to track 0. If an error occurs,
- + * then there is no floppy drive present.
- + */
- +/*extern*/ int no_floppies;
- +unsigned char floppy_selects1[]={ 0x10, 0x21, 0x23, 0x33 };
- +unsigned char floppy_selects2[]={ 0 , 0 , 0 , 0 };
- +
- +#define fd_disable_dma() disable_dma(FLOPPY_DMA)
- +#define fd_clear_dma_ff() clear_dma_ff(FLOPPY_DMA)
- +#define fd_set_dma_mode(mode) set_dma_mode(FLOPPY_DMA, (mode))
- +#define fd_set_dma_addr(addr) set_dma_addr(FLOPPY_DMA, (addr))
- +#define fd_set_dma_count(len) set_dma_count(FLOPPY_DMA, (len))
- +#define fd_enable_dma() enable_dma(FLOPPY_DMA)
- +#define fd_cacheflush(addr, sz)
- +#define fd_outb(val,port) outb_p((val),(port))
- +#define fd_inb(port) inb_p((port))
- +#define fd_request_irq() request_irq(FLOPPY_IRQ, floppy_interrupt,SA_INTERRUPT|SA_SAMPLE_RANDOM,"floppy",NULL)
- +#define fd_free_irq() free_irq(FLOPPY_IRQ,NULL)
- +#define fd_request_dma() request_dma(FLOPPY_DMA,"floppy")
- +#define fd_free_dma() free_dma(FLOPPY_DMA)
- +#define fd_enable_irq() enable_irq(FLOPPY_IRQ)
- +#define fd_disable_irq() disable_irq(FLOPPY_IRQ)
- +
- +#ifdef DEBUGT
- +static long unsigned debugtimer;
- +#endif
- +
- +/*
- + * Debugging
- + * =========
- + */
- +static inline void set_debugt(void)
- +{
- +#ifdef DEBUGT
- + debugtimer = jiffies;
- +#endif
- +}
- +
- +static inline void debugt(const char *message)
- +{
- +#ifdef DEBUGT
- + if ( DP->flags & DEBUGT )
- + printk("%s dtime=%lu\n", message, jiffies-debugtimer );
- +#endif
- +}
- +
- +typedef void (*timeout_fn)(unsigned long);
- +static struct timer_list fd_timeout ={ NULL, NULL, 0, 0,
- + (timeout_fn) floppy_shutdown };
- +
- +static const char *timeout_message;
- +
- +#ifdef FLOPPY_SANITY_CHECK
- +static void is_alive(const char *message)
- +{
- + /* this routine checks whether the floppy driver is "alive" */
- + if (fdc_busy && command_status < 2 && !fd_timeout.prev){
- + DPRINT("timeout handler died: %s\n",message);
- + }
- +}
- +#endif
- +
- +#ifdef FLOPPY_SANITY_CHECK
- +
- +#define OLOGSIZE 20
- +
- +static void (*lasthandler)(void) = NULL;
- +static int interruptjiffies=0;
- +static int resultjiffies=0;
- +static int resultsize=0;
- +static int lastredo=0;
- +
- +static struct output_log {
- + unsigned char data;
- + unsigned char status;
- + unsigned long jiffies;
- +} output_log[OLOGSIZE];
- +
- +static int output_log_pos=0;
- +#endif
- +
- +#define CURRENTD -1
- +#define MAXTIMEOUT -2
- +
- +static void reschedule_timeout(int drive, const char *message, int marg)
- +{
- + if (drive == CURRENTD )
- + drive = current_drive;
- + del_timer(&fd_timeout);
- + if (drive < 0 || drive > N_DRIVE) {
- + fd_timeout.expires = jiffies + 20*HZ;
- + drive = 0;
- + } else
- + fd_timeout.expires = jiffies + UDP->timeout;
- + add_timer(&fd_timeout);
- + if (UDP->flags & FD_DEBUG) {
- + DPRINT("reschedule timeout ");
- + printk(message, marg);
- + printk("\n");
- + }
- + timeout_message = message;
- +}
- +
- +static int maximum(int a, int b)
- +{
- + if(a > b)
- + return a;
- + else
- + return b;
- +}
- +#define INFBOUND(a,b) (a)=maximum((a),(b));
- +
- +static int minimum(int a, int b)
- +{
- + if(a < b)
- + return a;
- + else
- + return b;
- +}
- +#define SUPBOUND(a,b) (a)=minimum((a),(b));
- +
- +
- +/*
- + * Bottom half floppy driver.
- + * ==========================
- + *
- + * This part of the file contains the code talking directly to the hardware,
- + * and also the main service loop (seek-configure-spinup-command)
- + */
- +
- +/*
- + * disk change.
- + * This routine is responsible for maintaining the FD_DISK_CHANGE flag,
- + * and the last_checked date.
- + *
- + * last_checked is the date of the last check which showed 'no disk change'
- + * FD_DISK_CHANGE is set under two conditions:
- + * 1. The floppy has been changed after some i/o to that floppy already
- + * took place.
- + * 2. No floppy disk is in the drive. This is done in order to ensure that
- + * requests are quickly flushed in case there is no disk in the drive. It
- + * follows that FD_DISK_CHANGE can only be cleared if there is a disk in
- + * the drive.
- + *
- + * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet.
- + * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on
- + * each seek. If a disk is present, the disk change line should also be
- + * cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk
- + * change line is set, this means either that no disk is in the drive, or
- + * that it has been removed since the last seek.
- + *
- + * This means that we really have a third possibility too:
- + * The floppy has been changed after the last seek.
- + */
- +
- +static int disk_change(int drive)
- +{
- + int fdc=FDC(drive);
- +#ifdef FLOPPY_SANITY_CHECK
- + if(jiffies - UDRS->select_date < UDP->select_delay)
- + DPRINT("WARNING disk change called early\n");
- + if(! (FDCS->dor & (0x10 << UNIT(drive))) ||
- + (FDCS->dor & 3) != UNIT(drive) ||
- + fdc != FDC(drive)){
- + DPRINT("probing disk change on unselected drive\n");
- + DPRINT("drive=%d fdc=%d dor=%x\n",drive, FDC(drive),
- + (unsigned int)FDCS->dor);
- + }
- +#endif
- +
- +#ifdef DCL_DEBUG
- + if (UDP->flags & FD_DEBUG){
- + DPRINT("checking disk change line for drive %d\n",drive);
- + DPRINT("jiffies=%ld\n", jiffies);
- + DPRINT("disk change line=%x\n", fd_inb(FD_DIR)&0x80);
- + DPRINT("flags=%x\n",UDRS->flags);
- + }
- +#endif
- + if (UDP->flags & FD_BROKEN_DCL)
- + return UTESTF(FD_DISK_CHANGED);
- + if ((fd_inb(FD_DIR) ^ UDP->flags) & 0x80){
- + USETF(FD_VERIFY); /* verify write protection */
- + if(UDRS->maxblock){
- + /* mark it changed */
- + USETF(FD_DISK_CHANGED);
- + }
- +
- + /* invalidate its geometry */
- + if (UDRS->keep_data >= 0) {
- + if ((UDP->flags & FTD_MSG) &&
- + current_type[drive] != NULL)
- + DPRINT("Disk type is undefined after "
- + "disk change\n");
- + current_type[drive] = NULL;
- + floppy_sizes[TOMINOR(current_drive)] = MAX_DISK_SIZE;
- + }
- +
- + /*USETF(FD_DISK_NEWCHANGE);*/
- + return 1;
- + } else {
- + UDRS->last_checked=jiffies;
- + UCLEARF(FD_DISK_NEWCHANGE);
- + }
- + return 0;
- +}
- +
- +static inline int is_selected(int dor, int unit)
- +{
- + return ( (dor & (0x10 << unit)) && (dor &3) == unit);
- +}
- +
- +static inline void arm_set_dor(int dor)
- +{
- + if(dor & 0xf0)
- + fd_outb((dor & 0x0c) | floppy_selects1[dor & 3], FD_DOR);
- + else
- + fd_outb((dor & 0x0c), FD_DOR);
- +}
- +
- +static int set_dor(int fdc, char mask, char data)
- +{
- + register unsigned char drive, unit, newdor,olddor;
- +
- + if (FDCS->address == -1)
- + return -1;
- +
- + olddor = FDCS->dor;
- + newdor = (olddor & mask) | data;
- + if ( newdor != olddor ){
- + unit = olddor & 0x3;
- + if(is_selected(olddor, unit) && !is_selected(newdor, unit)){
- + drive = REVDRIVE(fdc,unit);
- +#ifdef DCL_DEBUG
- + if (UDP->flags & FD_DEBUG){
- + DPRINT("calling disk change from set_dor\n");
- + }
- +#endif
- + disk_change(drive);
- + }
- + FDCS->dor = newdor;
- + arm_set_dor(newdor);
- +
- + unit = newdor & 0x3;
- + if(!is_selected(olddor, unit) && is_selected(newdor, unit)){
- + drive = REVDRIVE(fdc,unit);
- + UDRS->select_date = jiffies;
- + }
- + }
- + if (newdor & FLOPPY_MOTOR_MASK)
- + floppy_grab_irq_and_dma();
- + if (olddor & FLOPPY_MOTOR_MASK)
- + floppy_release_irq_and_dma();
- + return olddor;
- +}
- +
- +static void twaddle(void)
- +{
- + if (DP->select_delay)
- + return;
- + arm_set_dor(FDCS->dor & ~(0x10<<UNIT(current_drive)));
- + arm_set_dor(FDCS->dor);
- + DRS->select_date = jiffies;
- +}
- +
- +/* reset all driver information about the current fdc. This is needed after
- + * a reset, and after a raw command. */
- +static void reset_fdc_info(int mode)
- +{
- + int drive;
- +
- + FDCS->spec1 = FDCS->spec2 = -1;
- + FDCS->need_configure = 1;
- + FDCS->perp_mode = 1;
- + FDCS->rawcmd = 0;
- + for ( drive = 0; drive < N_DRIVE; drive++)
- + if (FDC(drive) == fdc &&
- + ( mode || UDRS->track != NEED_1_RECAL))
- + UDRS->track = NEED_2_RECAL;
- +}
- +
- +/* selects the fdc and drive, and enables the fdc's input/dma. */
- +static void set_fdc(int drive)
- +{
- + if ( drive >= 0 && drive < N_DRIVE){
- + fdc = FDC(drive);
- + current_drive = drive;
- + }
- + if (fdc != 1 && fdc != 0) {
- + printk("bad fdc value\n");
- + return;
- + }
- + set_dor(fdc,~0,8);
- +#if N_FDC > 1
- + set_dor(1-fdc, ~8, 0);
- +#endif
- + if (FDCS->rawcmd == 2)
- + reset_fdc_info(1);
- + if (fd_inb(FD_STATUS) != STATUS_READY)
- + FDCS->reset = 1;
- +}
- +
- +/* locks the driver */
- +static int lock_fdc(int drive, int interruptible)
- +{
- + unsigned long flags;
- +
- + if(!usage_count){
- + printk("trying to lock fdc while usage count=0\n");
- + return -1;
- + }
- + floppy_grab_irq_and_dma();
- + INT_OFF;
- + while (fdc_busy && NO_SIGNAL)
- + interruptible_sleep_on(&fdc_wait);
- + if(fdc_busy){
- + INT_ON;
- + return -EINTR;
- + }
- + fdc_busy = 1;
- + INT_ON;
- + command_status = FD_COMMAND_NONE;
- + reschedule_timeout(drive, "lock fdc", 0);
- + set_fdc(drive);
- + return 0;
- +}
- +
- +#define LOCK_FDC(drive,interruptible) \
- +if(lock_fdc(drive,interruptible)) return -EINTR;
- +
- +
- +/* unlocks the driver */
- +static inline void unlock_fdc(void)
- +{
- + raw_cmd = 0;
- + if (!fdc_busy)
- + DPRINT("FDC access conflict!\n");
- +
- + if ( DEVICE_INTR )
- + DPRINT("device interrupt still active at FDC release: %p!\n",
- + DEVICE_INTR);
- + command_status = FD_COMMAND_NONE;
- + del_timer(&fd_timeout);
- + cont = NULL;
- + fdc_busy = 0;
- + floppy_release_irq_and_dma();
- + wake_up(&fdc_wait);
- +}
- +
- +/* switches the motor off after a given timeout */
- +static void motor_off_callback(unsigned long nr)
- +{
- + unsigned char mask = ~(0x10 << UNIT(nr));
- +
- + set_dor( FDC(nr), mask, 0 );
- +}
- +
- +static struct timer_list motor_off_timer[N_DRIVE] = {
- + { NULL, NULL, 0, 0, motor_off_callback },
- + { NULL, NULL, 0, 1, motor_off_callback },
- + { NULL, NULL, 0, 2, motor_off_callback },
- + { NULL, NULL, 0, 3, motor_off_callback },
- + { NULL, NULL, 0, 4, motor_off_callback },
- + { NULL, NULL, 0, 5, motor_off_callback },
- + { NULL, NULL, 0, 6, motor_off_callback },
- + { NULL, NULL, 0, 7, motor_off_callback }
- +};
- +
- +/* schedules motor off */
- +static void floppy_off(unsigned int drive)
- +{
- + unsigned long volatile delta;
- + register int fdc=FDC(drive);
- +
- + if( !(FDCS->dor & ( 0x10 << UNIT(drive))))
- + return;
- +
- + del_timer(motor_off_timer+drive);
- +
- + /* make spindle stop in a position which minimizes spinup time
- + * next time */
- + if ( UDP->rps ){
- + delta = jiffies - UDRS->first_read_date + HZ -
- + UDP->spindown_offset;
- + delta = (( delta * UDP->rps) % HZ ) / UDP->rps;
- + motor_off_timer[drive].expires = jiffies + UDP->spindown - delta;
- + }
- + add_timer(motor_off_timer+drive);
- +}
- +
- +/*
- + * cycle through all N_DRIVE floppy drives, for disk change testing.
- + * stopping at current drive. This is done before any long operation, to
- + * be sure to have up to date disk change information.
- + */
- +static void scandrives(void)
- +{
- + int i, drive, saved_drive;
- +
- + if (DP->select_delay)
- + return;
- +
- + saved_drive = current_drive;
- + for(i=0; i< N_DRIVE; i++){
- + drive = (saved_drive + i + 1 ) % N_DRIVE;
- + if ( UDRS->fd_ref == 0 || UDP->select_delay != 0)
- + continue; /* skip closed drives */
- + set_fdc(drive);
- + if(! (set_dor( fdc, ~3, UNIT(drive) | ( 0x10 << UNIT(drive))) &
- + (0x10 << UNIT(drive))))
- + /* switch the motor off again, if it was off to
- + * begin with */
- + set_dor( fdc, ~( 0x10 << UNIT(drive) ), 0 );
- + }
- + set_fdc(saved_drive);
- +}
- +
- +static void empty(void)
- +{
- +}
- +
- +static struct tq_struct floppy_tq =
- +{ 0, 0, 0, 0 };
- +
- +static struct timer_list fd_timer ={ NULL, NULL, 0, 0, 0 };
- +
- +static void cancel_activity(void)
- +{
- + CLEAR_INTR;
- + floppy_tq.routine = (void *)(void *) empty;
- + del_timer(&fd_timer);
- +}
- +
- +/* this function makes sure that the disk stays in the drive during the
- + * transfer */
- +static void fd_watchdog(void)
- +{
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("calling disk change from watchdog\n");
- + }
- +#endif
- +
- + if ( disk_change(current_drive) ){
- + DPRINT("disk removed during i/o\n");
- + cancel_activity();
- + cont->done(0);
- + reset_fdc();
- + } else {
- + del_timer(&fd_timer);
- + fd_timer.function = (timeout_fn) fd_watchdog;
- + fd_timer.expires = jiffies + HZ / 10;
- + add_timer(&fd_timer);
- + }
- +}
- +
- +static void main_command_interrupt(void)
- +{
- + del_timer(&fd_timer);
- + cont->interrupt();
- +}
- +
- +/* waits for a delay (spinup or select) to pass */
- +static int wait_for_completion(int delay, timeout_fn function)
- +{
- + if ( FDCS->reset ){
- + reset_fdc(); /* do the reset during sleep to win time
- + * if we don't need to sleep, it's a good
- + * occasion anyways */
- + return 1;
- + }
- +
- + if ((signed) (jiffies - delay) < 0) {
- + del_timer(&fd_timer);
- + fd_timer.function = function;
- + fd_timer.expires = delay;
- + add_timer(&fd_timer);
- + return 1;
- + }
- + return 0;
- +}
- +
- +static int hlt_disabled=0;
- +static void floppy_disable_hlt(void)
- +{
- + unsigned long flags;
- +
- + INT_OFF;
- + if(!hlt_disabled){
- + hlt_disabled=1;
- +#ifdef HAVE_DISABLE_HLT
- + disable_hlt();
- +#endif
- + }
- + INT_ON;
- +}
- +
- +static void floppy_enable_hlt(void)
- +{
- + unsigned long flags;
- +
- + INT_OFF;
- + if(hlt_disabled){
- + hlt_disabled=0;
- +#ifdef HAVE_DISABLE_HLT
- + enable_hlt();
- +#endif
- + }
- + INT_ON;
- +}
- +
- +
- +static void setup_DMA(void)
- +{
- + unsigned long flags;
- +
- +#ifdef FLOPPY_SANITY_CHECK
- + if (raw_cmd->length == 0){
- + int i;
- +
- + printk("zero dma transfer size:");
- + for(i=0; i< raw_cmd->cmd_count; i++)
- + printk("%x,", raw_cmd->cmd[i]);
- + printk("\n");
- + cont->done(0);
- + FDCS->reset = 1;
- + return;
- + }
- +#if 0
- + if ((long) raw_cmd->kernel_data % 512 ){
- + printk("non aligned address: %p\n", raw_cmd->kernel_data );
- + cont->done(0);
- + FDCS->reset=1;
- + return;
- + }
- + if (CROSS_64KB(raw_cmd->kernel_data, raw_cmd->length)) {
- + printk("DMA crossing 64-K boundary %p-%p\n",
- + raw_cmd->kernel_data,
- + raw_cmd->kernel_data + raw_cmd->length);
- + cont->done(0);
- + FDCS->reset=1;
- + return;
- + }
- +#endif
- +#endif
- + INT_OFF;
- + fd_disable_dma();
- + fd_clear_dma_ff();
- + fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length);
- + fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ)?
- + DMA_MODE_READ : DMA_MODE_WRITE);
- + fd_set_dma_addr(virt_to_bus(raw_cmd->kernel_data));
- + fd_set_dma_count(raw_cmd->length);
- + virtual_dma_port = FDCS->address;
- + fd_enable_dma();
- + INT_ON;
- + floppy_disable_hlt();
- +}
- +
- +void show_floppy(void);
- +
- +/* waits until the fdc becomes ready */
- +static int wait_til_ready(void)
- +{
- + int counter, status;
- + if(FDCS->reset)
- + return -1;
- + for (counter = 0; counter < 10000; counter++) {
- + status = fd_inb(FD_STATUS);
- + if (status & STATUS_READY)
- + return status;
- + }
- + if (!initialising) {
- + DPRINT("Getstatus times out (%x) on fdc %d\n",
- + status, fdc);
- + show_floppy();
- + }
- + FDCS->reset = 1;
- + return -1;
- +}
- +
- +/* sends a command byte to the fdc */
- +static int output_byte(char byte)
- +{
- + int status;
- +
- + if ((status = wait_til_ready()) < 0)
- + return -1;
- + if ((status & (STATUS_READY|STATUS_DIR|STATUS_DMA)) == STATUS_READY){
- + fd_outb(byte,FD_DATA);
- +#ifdef FLOPPY_SANITY_CHECK
- + output_log[output_log_pos].data = byte;
- + output_log[output_log_pos].status = status;
- + output_log[output_log_pos].jiffies = jiffies;
- + output_log_pos = (output_log_pos + 1) % OLOGSIZE;
- +#endif
- + return 0;
- + }
- + FDCS->reset = 1;
- + if (!initialising) {
- + DPRINT("Unable to send byte %x to FDC. Fdc=%x Status=%x\n",
- + byte, fdc, status);
- + show_floppy();
- + }
- + return -1;
- +}
- +#define LAST_OUT(x) if (output_byte(x)<0){ reset_fdc();return;}
- +
- +/* gets the response from the fdc */
- +static int result(void)
- +{
- + int i, status;
- +
- + for(i=0; i < MAX_REPLIES; i++) {
- + if ((status = wait_til_ready()) < 0)
- + break;
- + status &= STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA;
- + if ((status & ~STATUS_BUSY) == STATUS_READY){
- +#ifdef FLOPPY_SANITY_CHECK
- + resultjiffies = jiffies;
- + resultsize = i;
- +#endif
- + return i;
- + }
- + if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY))
- + reply_buffer[i] = fd_inb(FD_DATA);
- + else
- + break;
- + }
- + if (!initialising) {
- + DPRINT("get result error. Fdc=%d Last status=%x Read bytes=%d\n",
- + fdc, status, i);
- + show_floppy();
- + }
- + FDCS->reset = 1;
- + return -1;
- +}
- +
- +#define MORE_OUTPUT -2
- +/* does the fdc need more output? */
- +static int need_more_output(void)
- +{
- + int status;
- + if ((status = wait_til_ready()) < 0)
- + return -1;
- + if ((status & (STATUS_READY|STATUS_DIR|STATUS_DMA)) == STATUS_READY)
- + return MORE_OUTPUT;
- + return result();
- +}
- +
- +/* Set perpendicular mode as required, based on data rate, if supported.
- + * 82077 Now tested. 1Mbps data rate only possible with 82077-1.
- + */
- +static inline void perpendicular_mode(void)
- +{
- + unsigned char perp_mode;
- +
- + if (raw_cmd->rate & 0x40){
- + switch(raw_cmd->rate & 3){
- + case 0:
- + perp_mode=2;
- + break;
- + case 3:
- + perp_mode=3;
- + break;
- + default:
- + DPRINT("Invalid data rate for perpendicular mode!\n");
- + cont->done(0);
- + FDCS->reset = 1; /* convenient way to return to
- + * redo without to much hassle (deep
- + * stack et al. */
- + return;
- + }
- + } else
- + perp_mode = 0;
- +
- + if ( FDCS->perp_mode == perp_mode )
- + return;
- + if (FDCS->version >= FDC_82077_ORIG) {
- + output_byte(FD_PERPENDICULAR);
- + output_byte(perp_mode);
- + FDCS->perp_mode = perp_mode;
- + } else if (perp_mode) {
- + DPRINT("perpendicular mode not supported by this FDC.\n");
- + }
- +} /* perpendicular_mode */
- +
- +static int fifo_depth = 0xa;
- +static int no_fifo = 0;
- +
- +static int fdc_configure(void)
- +{
- + /* Turn on FIFO */
- + output_byte(FD_CONFIGURE);
- + if(need_more_output() != MORE_OUTPUT)
- + return 0;
- + output_byte(0);
- + output_byte(0x10 | (no_fifo & 0x20) | (fifo_depth & 0xf));
- + output_byte(0); /* pre-compensation from track
- + 0 upwards */
- + return 1;
- +}
- +
- +#define NOMINAL_DTR 500
- +
- +/* Issue a "SPECIFY" command to set the step rate time, head unload time,
- + * head load time, and DMA disable flag to values needed by floppy.
- + *
- + * The value "dtr" is the data transfer rate in Kbps. It is needed
- + * to account for the data rate-based scaling done by the 82072 and 82077
- + * FDC types. This parameter is ignored for other types of FDCs (i.e.
- + * 8272a).
- + *
- + * Note that changing the data transfer rate has a (probably deleterious)
- + * effect on the parameters subject to scaling for 82072/82077 FDCs, so
- + * fdc_specify is called again after each data transfer rate
- + * change.
- + *
- + * srt: 1000 to 16000 in microseconds
- + * hut: 16 to 240 milliseconds
- + * hlt: 2 to 254 milliseconds
- + *
- + * These values are rounded up to the next highest available delay time.
- + */
- +static void fdc_specify(void)
- +{
- + unsigned char spec1, spec2;
- + int srt, hlt, hut;
- + unsigned long dtr = NOMINAL_DTR;
- + unsigned long scale_dtr = NOMINAL_DTR;
- + int hlt_max_code = 0x7f;
- + int hut_max_code = 0xf;
- +
- + if (FDCS->need_configure && FDCS->version >= FDC_82072A) {
- + fdc_configure();
- + FDCS->need_configure = 0;
- + /*DPRINT("FIFO enabled\n");*/
- + }
- +
- + switch (raw_cmd->rate & 0x03) {
- + case 3:
- + dtr = 1000;
- + break;
- + case 1:
- + dtr = 300;
- + if (FDCS->version >= FDC_82078) {
- + /* chose the default rate table, not the one
- + * where 1 = 2 Mbps */
- + output_byte(FD_DRIVESPEC);
- + if(need_more_output() == MORE_OUTPUT) {
- + output_byte(UNIT(current_drive));
- + output_byte(0xc0);
- + }
- + }
- + break;
- + case 2:
- + dtr = 250;
- + break;
- + }
- +
- + if (FDCS->version >= FDC_82072) {
- + scale_dtr = dtr;
- + hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */
- + hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */
- + }
- +
- + /* Convert step rate from microseconds to milliseconds and 4 bits */
- + srt = 16 - (DP->srt*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR;
- + SUPBOUND(srt, 0xf);
- + INFBOUND(srt, 0);
- +
- + hlt = (DP->hlt*scale_dtr/2 + NOMINAL_DTR - 1)/NOMINAL_DTR;
- + if (hlt < 0x01)
- + hlt = 0x01;
- + else if (hlt > 0x7f)
- + hlt = hlt_max_code;
- +
- + hut = (DP->hut*scale_dtr/16 + NOMINAL_DTR - 1)/NOMINAL_DTR;
- + if (hut < 0x1)
- + hut = 0x1;
- + else if (hut > 0xf)
- + hut = hut_max_code;
- +
- + spec1 = (srt << 4) | hut;
- + spec2 = (hlt << 1) | (use_virtual_dma & 1);
- +
- + /* If these parameters did not change, just return with success */
- + if (FDCS->spec1 != spec1 || FDCS->spec2 != spec2) {
- + /* Go ahead and set spec1 and spec2 */
- + output_byte(FD_SPECIFY);
- + output_byte(FDCS->spec1 = spec1);
- + output_byte(FDCS->spec2 = spec2);
- + }
- +} /* fdc_specify */
- +
- +/* Set the FDC's data transfer rate on behalf of the specified drive.
- + * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
- + * of the specify command (i.e. using the fdc_specify function).
- + */
- +static int fdc_dtr(void)
- +{
- + /* If data rate not already set to desired value, set it. */
- + if ((raw_cmd->rate & 3) == FDCS->dtr)
- + return 0;
- +
- + /* Set dtr */
- + fd_outb(raw_cmd->rate & 3, FD_DCR);
- +
- + /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
- + * need a stabilization period of several milliseconds to be
- + * enforced after data rate changes before R/W operations.
- + * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
- + */
- + FDCS->dtr = raw_cmd->rate & 3;
- + return(wait_for_completion(jiffies+2*HZ/100,
- + (timeout_fn) floppy_ready));
- +} /* fdc_dtr */
- +
- +static void tell_sector(void)
- +{
- + printk(": track %d, head %d, sector %d, size %d",
- + R_TRACK, R_HEAD, R_SECTOR, R_SIZECODE);
- +} /* tell_sector */
- +
- +
- +/*
- + * OK, this error interpreting routine is called after a
- + * DMA read/write has succeeded
- + * or failed, so we check the results, and copy any buffers.
- + * hhb: Added better error reporting.
- + * ak: Made this into a separate routine.
- + */
- +static int interpret_errors(void)
- +{
- + char bad;
- +int res = get_dma_residue(FLOPPY_DMA);
- +if(res) {printk("\n-- DMA residue (%d)",res); tell_sector(); printk("\n");}
- + if (inr!=7) {
- + DPRINT("-- FDC reply error");
- + FDCS->reset = 1;
- + return 1;
- + }
- +
- + /* check IC to find cause of interrupt */
- + switch (ST0 & ST0_INTR) {
- + case 0x40: /* error occurred during command execution */
- + if (ST1 & ST1_EOC)
- + return 0; /* occurs with pseudo-DMA */
- + bad = 1;
- + if (ST1 & ST1_WP) {
- + DPRINT("Drive is write protected\n");
- + CLEARF(FD_DISK_WRITABLE);
- + cont->done(0);
- + bad = 2;
- + } else if (ST1 & ST1_ND) {
- + SETF(FD_NEED_TWADDLE);
- + } else if (ST1 & ST1_OR) {
- + if (DP->flags & FTD_MSG )
- + DPRINT("Over/Underrun - retrying\n");
- + bad = 0;
- + }else if(*errors >= DP->max_errors.reporting){
- + DPRINT("");
- + if (ST0 & ST0_ECE) {
- + printk("Recalibrate failed!");
- + } else if (ST2 & ST2_CRC) {
- + printk("data CRC error");
- + tell_sector();
- + } else if (ST1 & ST1_CRC) {
- + printk("CRC error");
- + tell_sector();
- + } else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) {
- + if (!probing) {
- + printk("sector not found");
- + tell_sector();
- + } else
- + printk("probe failed...");
- + } else if (ST2 & ST2_WC) { /* seek error */
- + printk("wrong cylinder");
- + } else if (ST2 & ST2_BC) { /* cylinder marked as bad */
- + printk("bad cylinder");
- + } else {
- + printk("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x", ST0, ST1, ST2);
- + tell_sector();
- + }
- + printk("\n");
- +
- + }
- + if ( ST2 & ST2_WC || ST2 & ST2_BC)
- + /* wrong cylinder => recal */
- + DRS->track = NEED_2_RECAL;
- + return bad;
- + case 0x80: /* invalid command given */
- + DPRINT("Invalid FDC command given!\n");
- + cont->done(0);
- + return 2;
- + case 0xc0:
- + DPRINT("Abnormal termination caused by polling\n");
- + cont->error();
- + return 2;
- + default: /* (0) Normal command termination */
- + return 0;
- + }
- +}
- +
- +/*
- + * This routine is called when everything should be correctly set up
- + * for the transfer (i.e. floppy motor is on, the correct floppy is
- + * selected, and the head is sitting on the right track).
- + */
- +static void setup_rw_floppy(void)
- +{
- + int i,ready_date,r, flags,dflags;
- + timeout_fn function;
- +
- + flags = raw_cmd->flags;
- + if ( flags & ( FD_RAW_READ | FD_RAW_WRITE))
- + flags |= FD_RAW_INTR;
- +
- + if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)){
- + ready_date = DRS->spinup_date + DP->spinup;
- + /* If spinup will take a long time, rerun scandrives
- + * again just before spinup completion. Beware that
- + * after scandrives, we must again wait for selection.
- + */
- + if ((signed) (ready_date - jiffies) > DP->select_delay){
- + ready_date -= DP->select_delay;
- + function = (timeout_fn) floppy_start;
- + } else
- + function = (timeout_fn) setup_rw_floppy;
- +
- + /* wait until the floppy is spinning fast enough */
- + if (wait_for_completion(ready_date,function))
- + return;
- + }
- + dflags = DRS->flags;
- +
- + if ( (flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
- + setup_DMA();
- +
- + if ( flags & FD_RAW_INTR )
- + SET_INTR(main_command_interrupt);
- +
- + r=0;
- + for(i=0; i< raw_cmd->cmd_count; i++)
- + r|=output_byte( raw_cmd->cmd[i] );
- +
- +#ifdef DEBUGT
- + debugt("rw_command: ");
- +#endif
- + if (r) {
- + cont->error();
- + reset_fdc();
- + return;
- + }
- +
- + if ( ! ( flags & FD_RAW_INTR )){
- + inr = result();
- + cont->interrupt();
- + } else if ( flags & FD_RAW_NEED_DISK )
- + fd_watchdog();
- +}
- +
- +static int blind_seek;
- +
- +/*
- + * This is the routine called after every seek (or recalibrate) interrupt
- + * from the floppy controller.
- + */
- +static void seek_interrupt(void)
- +{
- +#ifdef DEBUGT
- + debugt("seek interrupt:");
- +#endif
- + if (inr != 2 || (ST0 & 0xF8) != 0x20 ) {
- + DPRINT("seek failed\n");
- + DRS->track = NEED_2_RECAL;
- + cont->error();
- + cont->redo();
- + return;
- + }
- + if (DRS->track >= 0 && DRS->track != ST1 && !blind_seek){
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("clearing NEWCHANGE flag because of effective seek\n");
- + DPRINT("jiffies=%ld\n", jiffies);
- + }
- +#endif
- + CLEARF(FD_DISK_NEWCHANGE); /* effective seek */
- + DRS->select_date = jiffies;
- + }
- + DRS->track = ST1;
- + floppy_ready();
- +}
- +
- +static void check_wp(void)
- +{
- + if (TESTF(FD_VERIFY)) {
- + /* check write protection */
- + output_byte( FD_GETSTATUS );
- + output_byte( UNIT(current_drive) );
- + if ( result() != 1 ){
- + FDCS->reset = 1;
- + return;
- + }
- + CLEARF(FD_VERIFY);
- + CLEARF(FD_NEED_TWADDLE);
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("checking whether disk is write protected\n");
- + DPRINT("wp=%x\n",ST3 & 0x40);
- + }
- +#endif
- + if (!( ST3 & 0x40))
- + SETF(FD_DISK_WRITABLE);
- + else
- + CLEARF(FD_DISK_WRITABLE);
- + }
- +}
- +
- +static void seek_floppy(void)
- +{
- + int track;
- +
- + blind_seek=0;
- +
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("calling disk change from seek\n");
- + }
- +#endif
- +
- + if (!TESTF(FD_DISK_NEWCHANGE) &&
- + disk_change(current_drive) &&
- + (raw_cmd->flags & FD_RAW_NEED_DISK)){
- + /* the media changed flag should be cleared after the seek.
- + * If it isn't, this means that there is really no disk in
- + * the drive.
- + */
- + SETF(FD_DISK_CHANGED);
- + cont->done(0);
- + cont->redo();
- + return;
- + }
- + if ( DRS->track <= NEED_1_RECAL ){
- + recalibrate_floppy();
- + return;
- + } else if (TESTF(FD_DISK_NEWCHANGE) &&
- + (raw_cmd->flags & FD_RAW_NEED_DISK) &&
- + (DRS->track <= NO_TRACK || DRS->track == raw_cmd->track)) {
- + /* we seek to clear the media-changed condition. Does anybody
- + * know a more elegant way, which works on all drives? */
- + if ( raw_cmd->track )
- + track = raw_cmd->track - 1;
- + else {
- + if(DP->flags & FD_SILENT_DCL_CLEAR){
- + set_dor(fdc, ~ (0x10 << UNIT(current_drive)), 0);
- + blind_seek = 1;
- + raw_cmd->flags |= FD_RAW_NEED_SEEK;
- + }
- + track = 1;
- + }
- + } else {
- + check_wp();
- + if (raw_cmd->track != DRS->track &&
- + (raw_cmd->flags & FD_RAW_NEED_SEEK))
- + track = raw_cmd->track;
- + else {
- + setup_rw_floppy();
- + return;
- + }
- + }
- +
- + SET_INTR(seek_interrupt);
- + output_byte(FD_SEEK);
- + output_byte(UNIT(current_drive));
- + LAST_OUT(track);
- +#ifdef DEBUGT
- + debugt("seek command:");
- +#endif
- +}
- +
- +static void recal_interrupt(void)
- +{
- +#ifdef DEBUGT
- + debugt("recal interrupt:");
- +#endif
- + if (inr !=2 )
- + FDCS->reset = 1;
- + else if (ST0 & ST0_ECE) {
- + switch(DRS->track){
- + case NEED_1_RECAL:
- +#ifdef DEBUGT
- + debugt("recal interrupt need 1 recal:");
- +#endif
- + /* after a second recalibrate, we still haven't
- + * reached track 0. Probably no drive. Raise an
- + * error, as failing immediately might upset
- + * computers possessed by the Devil :-) */
- + cont->error();
- + cont->redo();
- + return;
- + case NEED_2_RECAL:
- +#ifdef DEBUGT
- + debugt("recal interrupt need 2 recal:");
- +#endif
- + /* If we already did a recalibrate,
- + * and we are not at track 0, this
- + * means we have moved. (The only way
- + * not to move at recalibration is to
- + * be already at track 0.) Clear the
- + * new change flag */
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("clearing NEWCHANGE flag because of second recalibrate\n");
- + }
- +#endif
- +
- + CLEARF(FD_DISK_NEWCHANGE);
- + DRS->select_date = jiffies;
- + /* fall through */
- + default:
- +#ifdef DEBUGT
- + debugt("recal interrupt default:");
- +#endif
- + /* Recalibrate moves the head by at
- + * most 80 steps. If after one
- + * recalibrate we don't have reached
- + * track 0, this might mean that we
- + * started beyond track 80. Try
- + * again. */
- + DRS->track = NEED_1_RECAL;
- + break;
- + }
- + } else
- + DRS->track = ST1;
- + floppy_ready();
- +}
- +
- +static void print_result(char *message, int inr)
- +{
- + int i;
- +
- + DPRINT("%s ", message);
- + if (inr >= 0)
- + for(i=0; i<inr; i++)
- + printk("repl[%d]=%x ", i, reply_buffer[i]);
- + printk("\n");
- +}
- +
- +/* interrupt handler */
- +void floppy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
- +{
- + void (*handler)(void) = DEVICE_INTR;
- + int do_print;
- +
- + lasthandler = handler;
- + interruptjiffies = jiffies;
- +
- + fd_disable_dma();
- + floppy_enable_hlt();
- + CLEAR_INTR;
- + if ( fdc >= N_FDC || FDCS->address == -1){
- + /* we don't even know which FDC is the culprit */
- + printk("DOR0=%x\n", (unsigned int)fdc_state[0].dor);
- + printk("floppy interrupt on bizarre fdc %d\n",fdc);
- + printk("handler=%p\n", handler);
- + is_alive("bizarre fdc");
- + return;
- + }
- +
- + FDCS->reset = 0;
- + /* We have to clear the reset flag here, because apparently on boxes
- + * with level triggered interrupts (PS/2, Sparc, ...), it is needed to
- + * emit SENSEI's to clear the interrupt line. And FDCS->reset blocks the
- + * emission of the SENSEI's.
- + * It is OK to emit floppy commands because we are in an interrupt
- + * handler here, and thus we have to fear no interference of other
- + * activity.
- + */
- +
- + do_print = !handler && print_unex && !initialising;
- +
- + inr = result();
- + if (do_print)
- + print_result("unexpected interrupt", inr);
- + if (inr == 0) {
- + do {
- + output_byte(FD_SENSEI);
- + inr = result();
- + if(do_print)
- + print_result("sensei", inr);
- + } while ( (ST0 & 0x83) != UNIT(current_drive) && inr == 2);
- + }
- + if (handler) {
- + if(intr_count >= 2) {
- + /* expected interrupt */
- + floppy_tq.routine = (void *)(void *) handler;
- + queue_task_irq(&floppy_tq, &tq_immediate);
- + mark_bh(IMMEDIATE_BH);
- + } else
- + handler();
- + } else
- + FDCS->reset = 1;
- + is_alive("normal interrupt end");
- +}
- +
- +static void recalibrate_floppy(void)
- +{
- +#ifdef DEBUGT
- + debugt("recalibrate floppy:");
- +#endif
- + SET_INTR(recal_interrupt);
- + output_byte(FD_RECALIBRATE);
- + LAST_OUT(UNIT(current_drive));
- +}
- +
- +/*
- + * Must do 4 FD_SENSEIs after reset because of ``drive polling''.
- + */
- +static void reset_interrupt(void)
- +{
- +#ifdef DEBUGT
- + debugt("reset interrupt:");
- +#endif
- + result(); /* get the status ready for set_fdc */
- + if (FDCS->reset) {
- + printk("reset set in interrupt, calling %p\n", cont->error);
- + cont->error(); /* a reset just after a reset. BAD! */
- + }
- + cont->redo();
- +}
- +
- +/*
- + * reset is done by pulling bit 2 of DOR low for a while (old FDCs),
- + * or by setting the self clearing bit 7 of STATUS (newer FDCs)
- + */
- +static void reset_fdc(void)
- +{
- + SET_INTR(reset_interrupt);
- + FDCS->reset = 0;
- + reset_fdc_info(0);
- +
- + /* Pseudo-DMA may intercept 'reset finished' interrupt. */
- + /* Irrelevant for systems with true DMA (i386). */
- + fd_disable_dma();
- +
- + if (FDCS->version >= FDC_82072A)
- + fd_outb(0x80 | (FDCS->dtr &3), FD_STATUS);
- + else {
- + arm_set_dor(FDCS->dor & ~0x04);
- + udelay(FD_RESET_DELAY);
- + arm_set_dor(FDCS->dor);
- + }
- +}
- +
- +void show_floppy(void)
- +{
- + int i;
- +
- + printk("\n");
- + printk("floppy driver state\n");
- + printk("-------------------\n");
- + printk("now=%ld last interrupt=%d last called handler=%p\n",
- + jiffies, interruptjiffies, lasthandler);
- +
- +
- +#ifdef FLOPPY_SANITY_CHECK
- + printk("timeout_message=%s\n", timeout_message);
- + printk("last output bytes:\n");
- + for(i=0; i < OLOGSIZE; i++)
- + printk("%2x %2x %ld\n",
- + output_log[(i+output_log_pos) % OLOGSIZE].data,
- + output_log[(i+output_log_pos) % OLOGSIZE].status,
- + output_log[(i+output_log_pos) % OLOGSIZE].jiffies);
- + printk("last result at %d\n", resultjiffies);
- + printk("last redo_fd_request at %d\n", lastredo);
- + for(i=0; i<resultsize; i++){
- + printk("%2x ", reply_buffer[i]);
- + }
- + printk("\n");
- +#endif
- +
- + printk("status=%x\n", fd_inb(FD_STATUS));
- + printk("fdc_busy=%d\n", fdc_busy);
- + if( DEVICE_INTR)
- + printk("DEVICE_INTR=%p\n", DEVICE_INTR);
- + if(floppy_tq.sync)
- + printk("floppy_tq.routine=%p\n", floppy_tq.routine);
- + if(fd_timer.prev)
- + printk("fd_timer.function=%p\n", fd_timer.function);
- + if(fd_timeout.prev){
- + printk("timer_table=%p\n",fd_timeout.function);
- + printk("expires=%ld\n",fd_timeout.expires-jiffies);
- + printk("now=%ld\n",jiffies);
- + }
- + printk("cont=%p\n", cont);
- + printk("CURRENT=%p\n", CURRENT);
- + printk("command_status=%d\n", command_status);
- + printk("\n");
- +}
- +
- +static void floppy_shutdown(void)
- +{
- + if (!initialising)
- + show_floppy();
- + cancel_activity();
- + sti();
- +
- + floppy_enable_hlt();
- + fd_disable_dma();
- + /* avoid dma going to a random drive after shutdown */
- +
- + if(!initialising)
- + DPRINT("floppy timeout called\n");
- + FDCS->reset = 1;
- + if (cont){
- + cont->done(0);
- + cont->redo(); /* this will recall reset when needed */
- + } else {
- + printk("no cont in shutdown!\n");
- + process_fd_request();
- + }
- + is_alive("floppy shutdown");
- +}
- +/*typedef void (*timeout_fn)(unsigned long);*/
- +
- +/* start motor, check media-changed condition and write protection */
- +static int start_motor( void (*function)(void) )
- +{
- + int mask, data;
- +
- + mask = 0xfc;
- + data = UNIT(current_drive);
- + if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)){
- + if(!(FDCS->dor & ( 0x10 << UNIT(current_drive) ) )){
- + set_debugt();
- + /* no read since this drive is running */
- + DRS->first_read_date = 0;
- + /* note motor start time if motor is not yet running */
- + DRS->spinup_date = jiffies;
- + data |= (0x10 << UNIT(current_drive));
- + }
- + } else
- + if (FDCS->dor & ( 0x10 << UNIT(current_drive) ) )
- + mask &= ~(0x10 << UNIT(current_drive));
- +
- + /* starts motor and selects floppy */
- + del_timer(motor_off_timer + current_drive);
- + set_dor( fdc, mask, data);
- +
- + /* wait_for_completion also schedules reset if needed. */
- + return(wait_for_completion(DRS->select_date+DP->select_delay,
- + (timeout_fn) function));
- +}
- +
- +static void floppy_ready(void)
- +{
- + CHECK_RESET;
- + if(start_motor(floppy_ready)) return;
- + if(fdc_dtr()) return;
- +
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("calling disk change from floppy_ready\n");
- + }
- +#endif
- +
- + if(!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
- + disk_change(current_drive) &&
- + !DP->select_delay)
- + twaddle(); /* this clears the dcl on certain drive/controller
- + * combinations */
- +
- + if ( raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){
- + perpendicular_mode();
- + fdc_specify(); /* must be done here because of hut, hlt ... */
- + seek_floppy();
- + } else
- + setup_rw_floppy();
- +}
- +
- +static void floppy_start(void)
- +{
- + reschedule_timeout(CURRENTD, "floppy start", 0);
- +
- + scandrives();
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("setting NEWCHANGE in floppy_start\n");
- + }
- +#endif
- + SETF(FD_DISK_NEWCHANGE);
- + floppy_ready();
- +}
- +
- +/*
- + * ========================================================================
- + * here ends the bottom half. Exported routines are:
- + * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
- + * start_motor, reset_fdc, reset_fdc_info, interpret_errors.
- + * Initialization also uses output_byte, result, set_dor, floppy_interrupt
- + * and set_dor.
- + * ========================================================================
- + */
- +/*
- + * General purpose continuations.
- + * ==============================
- + */
- +
- +static void do_wakeup(void)
- +{
- + reschedule_timeout(MAXTIMEOUT, "do wakeup", 0);
- + cont = 0;
- + command_status += 2;
- + wake_up(&command_done);
- +}
- +
- +static struct cont_t wakeup_cont={
- + empty,
- + do_wakeup,
- + empty,
- + (done_f)empty
- +};
- +
- +
- +static struct cont_t intr_cont={
- + empty,
- + process_fd_request,
- + empty,
- + (done_f) empty
- +};
- +
- +static int wait_til_done( void (*handler)(void ), int interruptible )
- +{
- + int ret;
- + unsigned long flags;
- +
- + floppy_tq.routine = (void *)(void *) handler;
- + queue_task(&floppy_tq, &tq_immediate);
- + mark_bh(IMMEDIATE_BH);
- + INT_OFF;
- + while(command_status < 2 && NO_SIGNAL){
- + is_alive("wait_til_done");
- + if (interruptible)
- + interruptible_sleep_on(&command_done);
- + else
- + sleep_on(&command_done);
- + }
- + if(command_status < 2){
- + cancel_activity();
- + cont = &intr_cont;
- + reset_fdc();
- + INT_ON;
- + return -EINTR;
- + }
- + INT_ON;
- +
- + if ( FDCS->reset )
- + command_status = FD_COMMAND_ERROR;
- + if ( command_status == FD_COMMAND_OKAY )
- + ret=0;
- + else
- + ret=-EIO;
- + command_status = FD_COMMAND_NONE;
- + return ret;
- +}
- +
- +static void generic_done(int result)
- +{
- + command_status = result;
- + cont = &wakeup_cont;
- +}
- +
- +static void generic_success(void)
- +{
- + cont->done(1);
- +}
- +
- +static void generic_failure(void)
- +{
- + cont->done(0);
- +}
- +
- +static void success_and_wakeup(void)
- +{
- + generic_success();
- + cont->redo();
- +}
- +
- +
- +/*
- + * formatting and rw support.
- + * ==========================
- + */
- +
- +static int next_valid_format(void)
- +{
- + int probed_format;
- +
- + probed_format = DRS->probed_format;
- + while(1){
- + if ( probed_format >= 8 ||
- + ! DP->autodetect[probed_format] ){
- + DRS->probed_format = 0;
- + return 1;
- + }
- + if ( floppy_type[DP->autodetect[probed_format]].sect ){
- + DRS->probed_format = probed_format;
- + return 0;
- + }
- + probed_format++;
- + }
- +}
- +
- +static void bad_flp_intr(void)
- +{
- + if ( probing ){
- + DRS->probed_format++;
- + if ( !next_valid_format())
- + return;
- + }
- + (*errors)++;
- + INFBOUND(DRWE->badness, *errors);
- + if (*errors > DP->max_errors.abort)
- + cont->done(0);
- + if (*errors > DP->max_errors.reset)
- + FDCS->reset = 1;
- + else if (*errors > DP->max_errors.recal)
- + DRS->track = NEED_2_RECAL;
- +}
- +
- +static void set_floppy(kdev_t device)
- +{
- + if (TYPE(device))
- + _floppy = TYPE(device) + floppy_type;
- + else
- + _floppy = current_type[ DRIVE(device) ];
- +}
- +
- +/*
- + * formatting support.
- + * ===================
- + */
- +static void format_interrupt(void)
- +{
- + switch (interpret_errors()){
- + case 1:
- + cont->error();
- + case 2:
- + break;
- + case 0:
- + cont->done(1);
- + }
- + cont->redo();
- +}
- +
- +#define CODE2SIZE (ssize = ( ( 1 << SIZECODE ) + 3 ) >> 2)
- +#define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80 ) >>1))
- +#define CT(x) ( (x) | 0x40 )
- +static void setup_format_params(int track)
- +{
- + struct fparm {
- + unsigned char track,head,sect,size;
- + } *here = (struct fparm *)floppy_track_buffer;
- + int il,n;
- + int count,head_shift,track_shift;
- +
- + raw_cmd = &default_raw_cmd;
- + raw_cmd->track = track;
- +
- + raw_cmd->flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN |
- + FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK;
- + raw_cmd->rate = _floppy->rate & 0x43;
- + raw_cmd->cmd_count = NR_F;
- + COMMAND = FM_MODE(_floppy,FD_FORMAT);
- + DR_SELECT = UNIT(current_drive) + PH_HEAD(_floppy,format_req.head);
- + F_SIZECODE = FD_SIZECODE(_floppy);
- + F_SECT_PER_TRACK = _floppy->sect << 2 >> F_SIZECODE;
- + F_GAP = _floppy->fmt_gap;
- + F_FILL = FD_FILL_BYTE;
- +
- + raw_cmd->kernel_data = floppy_track_buffer;
- + raw_cmd->length = 4 * F_SECT_PER_TRACK;
- +
- + /* allow for about 30ms for data transport per track */
- + head_shift = (F_SECT_PER_TRACK + 5) / 6;
- +
- + /* a ``cylinder'' is two tracks plus a little stepping time */
- + track_shift = 2 * head_shift + 3;
- +
- + /* position of logical sector 1 on this track */
- + n = (track_shift * format_req.track + head_shift * format_req.head )
- + % F_SECT_PER_TRACK;
- +
- + /* determine interleave */
- + il = 1;
- + if (_floppy->sect > DP->interleave_sect && F_SIZECODE == 2)
- + il++;
- +
- + /* initialize field */
- + for (count = 0; count < F_SECT_PER_TRACK; ++count) {
- + here[count].track = format_req.track;
- + here[count].head = format_req.head;
- + here[count].sect = 0;
- + here[count].size = F_SIZECODE;
- + }
- + /* place logical sectors */
- + for (count = 1; count <= F_SECT_PER_TRACK; ++count) {
- + here[n].sect = count;
- + n = (n+il) % F_SECT_PER_TRACK;
- + if (here[n].sect) { /* sector busy, find next free sector */
- + ++n;
- + if (n>= F_SECT_PER_TRACK) {
- + n-=F_SECT_PER_TRACK;
- + while (here[n].sect) ++n;
- + }
- + }
- + }
- +}
- +
- +static void redo_format(void)
- +{
- + buffer_track = -1;
- + setup_format_params(format_req.track << STRETCH(_floppy));
- + floppy_start();
- +#ifdef DEBUGT
- + debugt("queue format request");
- +#endif
- +}
- +
- +static struct cont_t format_cont={
- + format_interrupt,
- + redo_format,
- + bad_flp_intr,
- + generic_done };
- +
- +static int do_format(kdev_t device, struct format_descr *tmp_format_req)
- +{
- + int ret;
- + int drive=DRIVE(device);
- +
- + LOCK_FDC(drive,1);
- + set_floppy(device);
- + if (!_floppy ||
- + _floppy->track > DP->tracks ||
- + tmp_format_req->track >= _floppy->track ||
- + tmp_format_req->head >= _floppy->head ||
- + (_floppy->sect << 2) % (1 << FD_SIZECODE(_floppy)) ||
- + !_floppy->fmt_gap) {
- + process_fd_request();
- + return -EINVAL;
- + }
- + format_req = *tmp_format_req;
- + format_errors = 0;
- + cont = &format_cont;
- + errors = &format_errors;
- + IWAIT(redo_format);
- + process_fd_request();
- + return ret;
- +}
- +
- +/*
- + * Buffer read/write and support
- + * =============================
- + */
- +
- +/* new request_done. Can handle physical sectors which are smaller than a
- + * logical buffer */
- +static void request_done(int uptodate)
- +{
- + int block;
- +
- + probing = 0;
- + reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate);
- +
- + if (!CURRENT){
- + DPRINT("request list destroyed in floppy request done\n");
- + return;
- + }
- +
- + if (uptodate){
- + /* maintain values for invalidation on geometry
- + * change */
- + block = current_count_sectors + CURRENT->sector;
- + INFBOUND(DRS->maxblock, block);
- + if (block > _floppy->sect)
- + DRS->maxtrack = 1;
- +
- + /* unlock chained buffers */
- + while (current_count_sectors && CURRENT &&
- + current_count_sectors >= CURRENT->current_nr_sectors ){
- + current_count_sectors -= CURRENT->current_nr_sectors;
- + CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
- + CURRENT->sector += CURRENT->current_nr_sectors;
- + end_request(1);
- + }
- + if ( current_count_sectors && CURRENT){
- + /* "unlock" last subsector */
- + CURRENT->buffer += current_count_sectors <<9;
- + CURRENT->current_nr_sectors -= current_count_sectors;
- + CURRENT->nr_sectors -= current_count_sectors;
- + CURRENT->sector += current_count_sectors;
- + return;
- + }
- +
- + if ( current_count_sectors && ! CURRENT )
- + DPRINT("request list destroyed in floppy request done\n");
- +
- + } else {
- + if(CURRENT->cmd == WRITE) {
- + /* record write error information */
- + DRWE->write_errors++;
- + if(DRWE->write_errors == 1) {
- + DRWE->first_error_sector = CURRENT->sector;
- + DRWE->first_error_generation = DRS->generation;
- + }
- + DRWE->last_error_sector = CURRENT->sector;
- + DRWE->last_error_generation = DRS->generation;
- + }
- + end_request(0);
- + }
- +}
- +
- +/* Interrupt handler evaluating the result of the r/w operation */
- +static void rw_interrupt(void)
- +{
- + int nr_sectors, ssize, eoc;
- +
- + if ( ! DRS->first_read_date )
- + DRS->first_read_date = jiffies;
- +
- + nr_sectors = 0;
- + CODE2SIZE;
- +
- + if(ST1 & ST1_EOC)
- + eoc = 1;
- + else
- + eoc = 0;
- + nr_sectors = ((R_TRACK-TRACK)*_floppy->head+R_HEAD-HEAD) *
- + _floppy->sect + ((R_SECTOR-SECTOR+eoc) << SIZECODE >> 2) -
- + (sector_t % _floppy->sect) % ssize;
- +
- +#ifdef FLOPPY_SANITY_CHECK
- + if ( nr_sectors > current_count_sectors + ssize -
- + (current_count_sectors + sector_t) % ssize +
- + sector_t % ssize){
- + DPRINT("long rw: %x instead of %lx\n",
- + nr_sectors, current_count_sectors);
- + printk("rs=%d s=%d\n", R_SECTOR, SECTOR);
- + printk("rh=%d h=%d\n", R_HEAD, HEAD);
- + printk("rt=%d t=%d\n", R_TRACK, TRACK);
- + printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK,
- + sector_t, ssize);
- + }
- +#endif
- + INFBOUND(nr_sectors,0);
- + SUPBOUND(current_count_sectors, nr_sectors);
- +
- + switch (interpret_errors()){
- + case 2:
- + cont->redo();
- + return;
- + case 1:
- + if ( !current_count_sectors){
- + cont->error();
- + cont->redo();
- + return;
- + }
- + break;
- + case 0:
- + if ( !current_count_sectors){
- + cont->redo();
- + return;
- + }
- + current_type[current_drive] = _floppy;
- + floppy_sizes[TOMINOR(current_drive) ]= _floppy->size>>1;
- + break;
- + }
- +
- + if (probing) {
- + if (DP->flags & FTD_MSG)
- + DPRINT("Auto-detected floppy type %s in fd%d\n",
- + _floppy->name,current_drive);
- + current_type[current_drive] = _floppy;
- + floppy_sizes[TOMINOR(current_drive)] = _floppy->size >> 1;
- + probing = 0;
- + }
- +
- + if ( CT(COMMAND) != FD_READ ||
- + raw_cmd->kernel_data == CURRENT->buffer ){
- + /* transfer directly from buffer */
- + cont->done(1);
- + } else if ( CT(COMMAND) == FD_READ){
- + buffer_track = raw_cmd->track;
- + buffer_drive = current_drive;
- + INFBOUND(buffer_max, nr_sectors + sector_t);
- + }
- + cont->redo();
- +}
- +
- +/* Compute maximal contiguous buffer size. */
- +static int buffer_chain_size(void)
- +{
- + struct buffer_head *bh;
- + int size;
- + char *base;
- +
- + base = CURRENT->buffer;
- + size = CURRENT->current_nr_sectors << 9;
- + bh = CURRENT->bh;
- +
- + if(bh){
- + bh = bh->b_reqnext;
- + while ( bh && bh->b_data == base + size ){
- + size += bh->b_size;
- + bh = bh->b_reqnext;
- + }
- + }
- + return size >> 9;
- +}
- +
- +/* Compute the maximal transfer size */
- +static int transfer_size(int ssize, int max_sector, int max_size)
- +{
- + SUPBOUND(max_sector, sector_t + max_size);
- +
- + /* alignment */
- + max_sector -= (max_sector % _floppy->sect ) % ssize;
- +
- + /* transfer size, beginning not aligned */
- + current_count_sectors = max_sector - sector_t ;
- +
- + return max_sector;
- +}
- +
- +/*
- + * Move data from/to the track buffer to/from the buffer cache.
- + */
- +static void copy_buffer(int ssize, int max_sector, int max_sector_2)
- +{
- + int remaining; /* number of transferred 512-byte sectors */
- + struct buffer_head *bh;
- + char *buffer, *dma_buffer;
- + int size;
- +
- + max_sector = transfer_size(ssize,
- + minimum(max_sector, max_sector_2),
- + CURRENT->nr_sectors);
- +
- + if (current_count_sectors <= 0 && CT(COMMAND) == FD_WRITE &&
- + buffer_max > sector_t + CURRENT->nr_sectors)
- + current_count_sectors = minimum(buffer_max - sector_t,
- + CURRENT->nr_sectors);
- +
- + remaining = current_count_sectors << 9;
- +#ifdef FLOPPY_SANITY_CHECK
- + if ((remaining >> 9) > CURRENT->nr_sectors &&
- + CT(COMMAND) == FD_WRITE ){
- + DPRINT("in copy buffer\n");
- + printk("current_count_sectors=%ld\n", current_count_sectors);
- + printk("remaining=%d\n", remaining >> 9);
- + printk("CURRENT->nr_sectors=%ld\n",CURRENT->nr_sectors);
- + printk("CURRENT->current_nr_sectors=%ld\n",
- + CURRENT->current_nr_sectors);
- + printk("max_sector=%d\n", max_sector);
- + printk("ssize=%d\n", ssize);
- + }
- +#endif
- +
- + buffer_max = maximum(max_sector, buffer_max);
- +
- + dma_buffer = floppy_track_buffer + ((sector_t - buffer_min) << 9);
- +
- + bh = CURRENT->bh;
- + size = CURRENT->current_nr_sectors << 9;
- + buffer = CURRENT->buffer;
- +
- + while ( remaining > 0){
- + SUPBOUND(size, remaining);
- +#ifdef FLOPPY_SANITY_CHECK
- + if (dma_buffer + size >
- + floppy_track_buffer + (max_buffer_sectors << 10) ||
- + dma_buffer < floppy_track_buffer ){
- + DPRINT("buffer overrun in copy buffer %d\n",
- + (int) ((floppy_track_buffer - dma_buffer) >>9));
- + printk("sector_t=%d buffer_min=%d\n",
- + sector_t, buffer_min);
- + printk("current_count_sectors=%ld\n",
- + current_count_sectors);
- + if ( CT(COMMAND) == FD_READ )
- + printk("read\n");
- + if ( CT(COMMAND) == FD_READ )
- + printk("write\n");
- + break;
- + }
- + if ( ((unsigned long)buffer) % 512 )
- + DPRINT("%p buffer not aligned\n", buffer);
- +#endif
- + if (CT(COMMAND) == FD_READ)
- + memcpy( buffer, dma_buffer, size);
- + else
- + memcpy( dma_buffer, buffer, size);
- + remaining -= size;
- + if ( !remaining)
- + break;
- +
- + dma_buffer += size;
- + bh = bh->b_reqnext;
- +#ifdef FLOPPY_SANITY_CHECK
- + if ( !bh){
- + DPRINT("bh=null in copy buffer after copy\n");
- + break;
- + }
- +#endif
- + size = bh->b_size;
- + buffer = bh->b_data;
- + }
- +#ifdef FLOPPY_SANITY_CHECK
- + if ( remaining ){
- + if ( remaining > 0 )
- + max_sector -= remaining >> 9;
- + DPRINT("weirdness: remaining %d\n", remaining>>9);
- + }
- +#endif
- +}
- +
- +/*
- + * Formulate a read/write request.
- + * this routine decides where to load the data (directly to buffer, or to
- + * tmp floppy area), how much data to load (the size of the buffer, the whole
- + * track, or a single sector)
- + * All floppy_track_buffer handling goes in here. If we ever add track buffer
- + * allocation on the fly, it should be done here. No other part should need
- + * modification.
- + */
- +
- +static int make_raw_rw_request(void)
- +{
- + int aligned_sector_t;
- + int max_sector, max_size, tracksize, ssize;
- +
- + set_fdc(DRIVE(CURRENT->rq_dev));
- +
- + raw_cmd = &default_raw_cmd;
- + raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
- + FD_RAW_NEED_SEEK;
- + raw_cmd->cmd_count = NR_RW;
- + if (CURRENT->cmd == READ){
- + raw_cmd->flags |= FD_RAW_READ;
- + COMMAND = FM_MODE(_floppy,FD_READ);
- + } else if (CURRENT->cmd == WRITE){
- + raw_cmd->flags |= FD_RAW_WRITE;
- + COMMAND = FM_MODE(_floppy,FD_WRITE);
- + } else {
- + DPRINT("make_raw_rw_request: unknown command\n");
- + return 0;
- + }
- +
- + max_sector = _floppy->sect * _floppy->head;
- +
- + TRACK = CURRENT->sector / max_sector;
- + sector_t = CURRENT->sector % max_sector;
- + if (_floppy->track && TRACK >= _floppy->track )
- + return 0;
- + HEAD = sector_t / _floppy->sect;
- +
- + if (((_floppy->stretch & FD_SWAPSIDES) || TESTF(FD_NEED_TWADDLE)) &&
- + sector_t < _floppy->sect)
- + max_sector = _floppy->sect;
- +
- + /* 2M disks have phantom sectors on the first track */
- + if ((_floppy->rate & FD_2M ) && (!TRACK) && (!HEAD)){
- + max_sector = 2 * _floppy->sect / 3;
- + if (sector_t >= max_sector){
- + current_count_sectors = minimum(_floppy->sect - sector_t,
- + CURRENT->nr_sectors);
- + return 1;
- + }
- + SIZECODE = 2;
- + } else
- + SIZECODE = FD_SIZECODE(_floppy);
- + raw_cmd->rate = _floppy->rate & 0x43;
- + if ((_floppy->rate & FD_2M) &&
- + (TRACK || HEAD ) &&
- + raw_cmd->rate == 2)
- + raw_cmd->rate = 1;
- +
- + if ( SIZECODE )
- + SIZECODE2 = 0xff;
- + else
- + SIZECODE2 = 0x80;
- + raw_cmd->track = TRACK << STRETCH(_floppy);
- + DR_SELECT = UNIT(current_drive) + PH_HEAD(_floppy,HEAD);
- + GAP = _floppy->gap;
- + CODE2SIZE;
- + SECT_PER_TRACK = _floppy->sect << 2 >> SIZECODE;
- + SECTOR = ((sector_t % _floppy->sect) << 2 >> SIZECODE) + 1;
- + tracksize = _floppy->sect - _floppy->sect % ssize;
- + if ( tracksize < _floppy->sect ){
- + SECT_PER_TRACK ++;
- + if ( tracksize <= sector_t % _floppy->sect)
- + SECTOR--;
- + while ( tracksize <= sector_t % _floppy->sect){
- + while( tracksize + ssize > _floppy->sect ){
- + SIZECODE--;
- + ssize >>= 1;
- + }
- + SECTOR++; SECT_PER_TRACK ++;
- + tracksize += ssize;
- + }
- + max_sector = HEAD * _floppy->sect + tracksize;
- + } else if ( !TRACK && !HEAD && !( _floppy->rate & FD_2M ) && probing)
- + max_sector = _floppy->sect;
- +
- + aligned_sector_t = sector_t - ( sector_t % _floppy->sect ) % ssize;
- + max_size = CURRENT->nr_sectors;
- + if ((raw_cmd->track == buffer_track) &&
- + (current_drive == buffer_drive) &&
- + (sector_t >= buffer_min) && (sector_t < buffer_max)) {
- + /* data already in track buffer */
- + if (CT(COMMAND) == FD_READ) {
- + copy_buffer(1, max_sector, buffer_max);
- + return 1;
- + }
- + } else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){
- + if (CT(COMMAND) == FD_WRITE){
- + if(sector_t + CURRENT->nr_sectors > ssize &&
- + sector_t + CURRENT->nr_sectors < ssize + ssize)
- + max_size = ssize + ssize;
- + else
- + max_size = ssize;
- + }
- + raw_cmd->flags &= ~FD_RAW_WRITE;
- + raw_cmd->flags |= FD_RAW_READ;
- + COMMAND = FM_MODE(_floppy,FD_READ);
- + } else if ((unsigned long)CURRENT->buffer < MAX_DMA_ADDRESS ) {
- +#if 0
- + unsigned long dma_limit;
- +#endif
- + int direct, indirect;
- +
- + indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) -
- + sector_t;
- +
- + /*
- + * Do NOT use minimum() here---MAX_DMA_ADDRESS is 64 bits wide
- + * on a 64 bit machine!
- + */
- + max_size = buffer_chain_size();
- +#if 0
- + dma_limit = (MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer)) >> 9;
- + if ((unsigned long) max_size > dma_limit) {
- + max_size = dma_limit;
- + }
- + /* 64 kb boundaries */
- + if (CROSS_64KB(CURRENT->buffer, max_size << 9))
- + max_size = ( K_64 - ((long) CURRENT->buffer) % K_64)>>9;
- +#endif
- + direct = transfer_size(ssize,max_sector,max_size) - sector_t;
- + /*
- + * We try to read tracks, but if we get too many errors, we
- + * go back to reading just one sector at a time.
- + *
- + * This means we should be able to read a sector even if there
- + * are other bad sectors on this track.
- + */
- + if (!direct ||
- + (indirect * 2 > direct * 3 &&
- + *errors < DP->max_errors.read_track &&
- + /*!TESTF( FD_NEED_TWADDLE) &&*/
- + ((!probing || (DP->read_track&(1<<DRS->probed_format)))))){
- + max_size = CURRENT->nr_sectors;
- + } else {
- + raw_cmd->kernel_data = CURRENT->buffer;
- + raw_cmd->length = current_count_sectors << 9;
- + if (raw_cmd->length == 0){
- + DPRINT("zero dma transfer attempted from make_raw_request\n");
- + DPRINT("indirect=%d direct=%d sector_t=%d",
- + indirect, direct, sector_t);
- + return 0;
- + }
- + return 2;
- + }
- + }
- +
- + if ( CT(COMMAND) == FD_READ )
- + max_size = max_sector; /* unbounded */
- +
- + /* claim buffer track if needed */
- + if (buffer_track != raw_cmd->track || /* bad track */
- + buffer_drive !=current_drive || /* bad drive */
- + sector_t > buffer_max ||
- + sector_t < buffer_min ||
- + ((CT(COMMAND) == FD_READ ||
- + (aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize ))&&
- + max_sector > 2 * max_buffer_sectors + buffer_min &&
- + max_size + sector_t > 2 * max_buffer_sectors + buffer_min)
- + /* not enough space */ ){
- + buffer_track = -1;
- + buffer_drive = current_drive;
- + buffer_max = buffer_min = aligned_sector_t;
- + }
- + raw_cmd->kernel_data = floppy_track_buffer +
- + ((aligned_sector_t-buffer_min )<<9);
- +
- + if ( CT(COMMAND) == FD_WRITE ){
- + /* copy write buffer to track buffer.
- + * if we get here, we know that the write
- + * is either aligned or the data already in the buffer
- + * (buffer will be overwritten) */
- +#ifdef FLOPPY_SANITY_CHECK
- + if (sector_t != aligned_sector_t && buffer_track == -1 )
- + DPRINT("internal error offset !=0 on write\n");
- +#endif
- + buffer_track = raw_cmd->track;
- + buffer_drive = current_drive;
- + copy_buffer(ssize, max_sector, 2*max_buffer_sectors+buffer_min);
- + } else
- + transfer_size(ssize, max_sector,
- + 2*max_buffer_sectors+buffer_min-aligned_sector_t);
- +
- + /* round up current_count_sectors to get dma xfer size */
- + raw_cmd->length = sector_t+current_count_sectors-aligned_sector_t;
- + raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1;
- + raw_cmd->length <<= 9;
- +#ifdef FLOPPY_SANITY_CHECK
- + if ((raw_cmd->length < current_count_sectors << 9) ||
- + (raw_cmd->kernel_data != CURRENT->buffer &&
- + CT(COMMAND) == FD_WRITE &&
- + (aligned_sector_t + (raw_cmd->length >> 9) > buffer_max ||
- + aligned_sector_t < buffer_min )) ||
- + raw_cmd->length % ( 128 << SIZECODE ) ||
- + raw_cmd->length <= 0 || current_count_sectors <= 0){
- + DPRINT("fractionary current count b=%lx s=%lx\n",
- + raw_cmd->length, current_count_sectors);
- + if ( raw_cmd->kernel_data != CURRENT->buffer )
- + printk("addr=%d, length=%ld\n",
- + (int) ((raw_cmd->kernel_data -
- + floppy_track_buffer ) >> 9),
- + current_count_sectors);
- + printk("st=%d ast=%d mse=%d msi=%d\n",
- + sector_t, aligned_sector_t, max_sector, max_size);
- + printk("ssize=%x SIZECODE=%d\n", ssize, SIZECODE);
- + printk("command=%x SECTOR=%d HEAD=%d, TRACK=%d\n",
- + COMMAND, SECTOR, HEAD, TRACK);
- + printk("buffer drive=%d\n", buffer_drive);
- + printk("buffer track=%d\n", buffer_track);
- + printk("buffer_min=%d\n", buffer_min );
- + printk("buffer_max=%d\n", buffer_max );
- + return 0;
- + }
- +
- + if (raw_cmd->kernel_data != CURRENT->buffer ){
- + if (raw_cmd->kernel_data < floppy_track_buffer ||
- + current_count_sectors < 0 ||
- + raw_cmd->length < 0 ||
- + raw_cmd->kernel_data + raw_cmd->length >
- + floppy_track_buffer + (max_buffer_sectors << 10)){
- + DPRINT("buffer overrun in schedule dma\n");
- + printk("sector_t=%d buffer_min=%d current_count=%ld\n",
- + sector_t, buffer_min,
- + raw_cmd->length >> 9 );
- + printk("current_count_sectors=%ld\n",
- + current_count_sectors);
- + if ( CT(COMMAND) == FD_READ )
- + printk("read\n");
- + if ( CT(COMMAND) == FD_READ )
- + printk("write\n");
- + return 0;
- + }
- + } else if (raw_cmd->length > CURRENT->nr_sectors << 9 ||
- + current_count_sectors > CURRENT->nr_sectors){
- + DPRINT("buffer overrun in direct transfer\n");
- + return 0;
- + } else if ( raw_cmd->length < current_count_sectors << 9 ){
- + DPRINT("more sectors than bytes\n");
- + printk("bytes=%ld\n", raw_cmd->length >> 9 );
- + printk("sectors=%ld\n", current_count_sectors);
- + }
- + if (raw_cmd->length == 0){
- + DPRINT("zero dma transfer attempted from make_raw_request\n");
- + return 0;
- + }
- +#endif
- + return 2;
- +}
- +
- +static void redo_fd_request(void)
- +{
- +#define REPEAT {request_done(0); continue; }
- + kdev_t device;
- + int tmp;
- +
- + lastredo = jiffies;
- + if (current_drive < N_DRIVE)
- + floppy_off(current_drive);
- +
- + if (CURRENT && CURRENT->rq_status == RQ_INACTIVE){
- + CLEAR_INTR;
- + unlock_fdc();
- + return;
- + }
- +
- + while(1){
- + if (!CURRENT) {
- + CLEAR_INTR;
- + unlock_fdc();
- + return;
- + }
- + if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
- + panic(DEVICE_NAME ": request list destroyed");
- + if (CURRENT->bh && !buffer_locked(CURRENT->bh))
- + panic(DEVICE_NAME ": block not locked");
- +
- + device = CURRENT->rq_dev;
- + set_fdc( DRIVE(device));
- + reschedule_timeout(CURRENTD, "redo fd request", 0);
- +
- + set_floppy(device);
- + raw_cmd = & default_raw_cmd;
- + raw_cmd->flags = 0;
- + if(start_motor(redo_fd_request)) return;
- + disk_change(current_drive);
- + if(test_bit(current_drive, &fake_change) ||
- + TESTF(FD_DISK_CHANGED)){
- + DPRINT("disk absent or changed during operation\n");
- + REPEAT;
- + }
- + if (!_floppy) { /* Autodetection */
- + if (!probing){
- + DRS->probed_format = 0;
- + if ( next_valid_format() ){
- + DPRINT("no autodetectable formats\n");
- + _floppy = NULL;
- + REPEAT;
- + }
- + }
- + probing = 1;
- + _floppy = floppy_type+DP->autodetect[DRS->probed_format];
- + } else
- + probing = 0;
- + errors = & (CURRENT->errors);
- + tmp = make_raw_rw_request();
- + if ( tmp < 2 ){
- + request_done(tmp);
- + continue;
- + }
- +
- + if (TESTF(FD_NEED_TWADDLE))
- + twaddle();
- + floppy_tq.routine = (void *)(void *) floppy_start;
- + queue_task(&floppy_tq, &tq_immediate);
- + mark_bh(IMMEDIATE_BH);
- +#ifdef DEBUGT
- + debugt("queue fd request");
- +#endif
- + return;
- + }
- +#undef REPEAT
- +}
- +
- +static struct cont_t rw_cont={
- + rw_interrupt,
- + redo_fd_request,
- + bad_flp_intr,
- + request_done };
- +
- +static struct tq_struct request_tq =
- +{ 0, 0, (void *) (void *) redo_fd_request, 0 };
- +
- +static void process_fd_request(void)
- +{
- + cont = &rw_cont;
- + queue_task(&request_tq, &tq_immediate);
- + mark_bh(IMMEDIATE_BH);
- +}
- +
- +static void do_fd_request(void)
- +{
- + sti();
- + if (fdc_busy){
- + /* fdc busy, this new request will be treated when the
- + current one is done */
- + is_alive("do fd request, old request running");
- + return;
- + }
- + lock_fdc(MAXTIMEOUT,0);
- + process_fd_request();
- + is_alive("do fd request");
- +}
- +
- +static struct cont_t poll_cont={
- + success_and_wakeup,
- + floppy_ready,
- + generic_failure,
- + generic_done };
- +
- +static int poll_drive(int interruptible, int flag)
- +{
- + int ret;
- + /* no auto-sense, just clear dcl */
- + raw_cmd = &default_raw_cmd;
- + raw_cmd->flags= flag;
- + raw_cmd->track=0;
- + raw_cmd->cmd_count=0;
- + cont = &poll_cont;
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("setting NEWCHANGE in poll_drive\n");
- + }
- +#endif
- + SETF(FD_DISK_NEWCHANGE);
- + WAIT(floppy_ready);
- + return ret;
- +}
- +
- +/*
- + * User triggered reset
- + * ====================
- + */
- +
- +static void reset_intr(void)
- +{
- + printk("weird, reset interrupt called\n");
- +}
- +
- +static struct cont_t reset_cont={
- + reset_intr,
- + success_and_wakeup,
- + generic_failure,
- + generic_done };
- +
- +static int user_reset_fdc(int drive, int arg, int interruptible)
- +{
- + int ret;
- +
- + ret=0;
- + LOCK_FDC(drive,interruptible);
- + if(arg == FD_RESET_ALWAYS)
- + FDCS->reset=1;
- + if ( FDCS->reset ){
- + cont = &reset_cont;
- + WAIT(reset_fdc);
- + }
- + process_fd_request();
- + return ret;
- +}
- +
- +/*
- + * Misc Ioctl's and support
- + * ========================
- + */
- +static int fd_copyout(void *param, const void *address, int size)
- +{
- + int ret;
- +
- + ECALL(verify_area(VERIFY_WRITE,param,size));
- + memcpy_tofs(param,(void *) address, size);
- + return 0;
- +}
- +
- +static int fd_copyin(void *param, void *address, int size)
- +{
- + int ret;
- +
- + ECALL(verify_area(VERIFY_READ,param,size));
- + memcpy_fromfs((void *) address, param, size);
- + return 0;
- +}
- +
- +#define COPYOUT(x) ECALL(fd_copyout( (void *)param, &(x), sizeof(x)))
- +#define COPYIN(x) ECALL(fd_copyin( (void *)param, &(x), sizeof(x)))
- +
- +static inline const char *drive_name(int type, int drive)
- +{
- + struct floppy_struct *floppy;
- +
- + if ( type )
- + floppy = floppy_type + type;
- + else {
- + if ( UDP->native_format )
- + floppy = floppy_type + UDP->native_format;
- + else
- + return "(null)";
- + }
- + if ( floppy->name )
- + return floppy->name;
- + else
- + return "(null)";
- +}
- +
- +
- +/* raw commands */
- +static void raw_cmd_done(int flag)
- +{
- + int i;
- +
- + if(!flag) {
- + raw_cmd->flags |= FD_RAW_FAILURE;
- + raw_cmd->flags |= FD_RAW_HARDFAILURE;
- + } else {
- + raw_cmd->reply_count = inr;
- + for( i=0; i< raw_cmd->reply_count; i++)
- + raw_cmd->reply[i] = reply_buffer[i];
- +
- + if ( raw_cmd->flags & ( FD_RAW_READ | FD_RAW_WRITE ))
- + raw_cmd->length = fd_get_dma_residue();
- +
- + if( (raw_cmd->flags & FD_RAW_SOFTFAILURE) &&
- + (!raw_cmd->reply_count || (raw_cmd->reply[0] & 0xc0)))
- + raw_cmd->flags |= FD_RAW_FAILURE;
- +
- + if( disk_change(current_drive) )
- + raw_cmd->flags |= FD_RAW_DISK_CHANGE;
- + else
- + raw_cmd->flags &= ~FD_RAW_DISK_CHANGE;
- + if(raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER)
- + motor_off_callback(current_drive);
- +
- + if(raw_cmd->next &&
- + (!(raw_cmd->flags & FD_RAW_FAILURE) ||
- + !(raw_cmd->flags & FD_RAW_STOP_IF_FAILURE)) &&
- + ((raw_cmd->flags & FD_RAW_FAILURE) ||
- + !(raw_cmd->flags &FD_RAW_STOP_IF_SUCCESS))) {
- + raw_cmd = raw_cmd->next;
- + return;
- + }
- + }
- + generic_done(flag);
- +}
- +
- +
- +static struct cont_t raw_cmd_cont={
- + success_and_wakeup,
- + floppy_start,
- + generic_failure,
- + raw_cmd_done
- +};
- +
- +static inline int raw_cmd_copyout(int cmd, char *param,
- + struct floppy_raw_cmd *ptr)
- +{
- + struct old_floppy_raw_cmd old_raw_cmd;
- + int ret;
- +
- + while(ptr) {
- + if(cmd == OLDFDRAWCMD) {
- + old_raw_cmd.flags = ptr->flags;
- + old_raw_cmd.data = ptr->data;
- + old_raw_cmd.length = ptr->length;
- + old_raw_cmd.rate = ptr->rate;
- + old_raw_cmd.reply_count = ptr->reply_count;
- + memcpy(old_raw_cmd.reply, ptr->reply, 7);
- + COPYOUT(old_raw_cmd);
- + param += sizeof(old_raw_cmd);
- + } else {
- + COPYOUT(*ptr);
- + param += sizeof(struct floppy_raw_cmd);
- + }
- +
- + if ( (ptr->flags & FD_RAW_READ) && ptr->buffer_length){
- + if(ptr->length>=0 && ptr->length<=ptr->buffer_length)
- + ECALL(fd_copyout(ptr->data,
- + ptr->kernel_data,
- + ptr->buffer_length -
- + ptr->length));
- + }
- + ptr = ptr->next;
- + }
- + return 0;
- +}
- +
- +
- +static void raw_cmd_free(struct floppy_raw_cmd **ptr)
- +{
- + struct floppy_raw_cmd *next,*this;
- +
- + this = *ptr;
- + *ptr = 0;
- + while(this) {
- + if (this->buffer_length) {
- + fd_dma_mem_free((unsigned long)this->kernel_data,
- + this->buffer_length);
- + this->buffer_length = 0;
- + }
- + next = this->next;
- + kfree(this);
- + this = next;
- + }
- +}
- +
- +
- +static inline int raw_cmd_copyin(int cmd, char *param,
- + struct floppy_raw_cmd **rcmd)
- +{
- + struct floppy_raw_cmd *ptr;
- + struct old_floppy_raw_cmd old_raw_cmd;
- + int ret;
- + int i;
- +
- + *rcmd = 0;
- + while(1) {
- + ptr = (struct floppy_raw_cmd *)
- + kmalloc(sizeof (struct floppy_raw_cmd ), GFP_USER);
- + if (!ptr)
- + return -ENOMEM;
- + *rcmd = ptr;
- + if(cmd == OLDFDRAWCMD){
- + COPYIN(old_raw_cmd);
- + ptr->flags = old_raw_cmd.flags;
- + ptr->data = old_raw_cmd.data;
- + ptr->length = old_raw_cmd.length;
- + ptr->rate = old_raw_cmd.rate;
- + ptr->cmd_count = old_raw_cmd.cmd_count;
- + ptr->track = old_raw_cmd.track;
- + ptr->phys_length = 0;
- + ptr->next = 0;
- + ptr->buffer_length = 0;
- + memcpy(ptr->cmd, old_raw_cmd.cmd, 9);
- + param += sizeof(struct old_floppy_raw_cmd);
- + if(ptr->cmd_count > 9)
- + return -EINVAL;
- + } else {
- + COPYIN(*ptr);
- + ptr->next = 0;
- + ptr->buffer_length = 0;
- + param += sizeof(struct floppy_raw_cmd);
- + if (ptr->cmd_count > 33)
- + /* the command may now also take up the space
- + * initially intended for the reply & the
- + * reply count. Needed for long 82078 commands
- + * such as RESTORE, which takes ... 17 command
- + * bytes. Murphy's law #137: When you reserve
- + * 16 bytes for a structure, you'll one day
- + * discover that you really need 17...
- + */
- + return -EINVAL;
- + }
- +
- + for(i=0; i< 16; i++)
- + ptr->reply[i] = 0;
- + ptr->resultcode = 0;
- + ptr->kernel_data = 0;
- +
- + if(ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
- + if (ptr->length <= 0)
- + return -EINVAL;
- + ptr->kernel_data =(char*)fd_dma_mem_alloc(ptr->length);
- + if(!ptr->kernel_data)
- + return -ENOMEM;
- + ptr->buffer_length = ptr->length;
- + }
- + if ( ptr->flags & FD_RAW_READ )
- + ECALL( verify_area( VERIFY_WRITE, ptr->data,
- + ptr->length ));
- + if(ptr->flags & FD_RAW_WRITE)
- + ECALL(fd_copyin(ptr->data, ptr->kernel_data,
- + ptr->length));
- + rcmd = & (ptr->next);
- + if( ! (ptr->flags & FD_RAW_MORE))
- + return 0;
- + ptr->rate &= 0x43;
- + }
- +}
- +
- +
- +static int raw_cmd_ioctl(int cmd, void *param)
- +{
- + int drive, ret, ret2;
- + struct floppy_raw_cmd *my_raw_cmd;
- +
- + if ( FDCS->rawcmd <= 1 )
- + FDCS->rawcmd = 1;
- + for ( drive= 0; drive < N_DRIVE; drive++){
- + if ( FDC(drive) != fdc)
- + continue;
- + if ( drive == current_drive ){
- + if ( UDRS->fd_ref > 1 ){
- + FDCS->rawcmd = 2;
- + break;
- + }
- + } else if ( UDRS->fd_ref ){
- + FDCS->rawcmd = 2;
- + break;
- + }
- + }
- +
- + if(FDCS->reset)
- + return -EIO;
- +
- + ret = raw_cmd_copyin(cmd, param, &my_raw_cmd);
- + if (ret) {
- + raw_cmd_free(&my_raw_cmd);
- + return ret;
- + }
- +
- + raw_cmd = my_raw_cmd;
- + cont = &raw_cmd_cont;
- + ret=wait_til_done(floppy_start,1);
- +#ifdef DCL_DEBUG
- + if (DP->flags & FD_DEBUG){
- + DPRINT("calling disk change from raw_cmd ioctl\n");
- + }
- +#endif
- +
- + if (ret != -EINTR && FDCS->reset)
- + ret = -EIO;
- +
- + DRS->track = NO_TRACK;
- +
- + ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd);
- + if(!ret)
- + ret = ret2;
- + raw_cmd_free(&my_raw_cmd);
- + return ret;
- +}
- +
- +static int invalidate_drive(kdev_t rdev)
- +{
- + /* invalidate the buffer track to force a reread */
- + set_bit( DRIVE(rdev), &fake_change);
- + process_fd_request();
- + check_disk_change(rdev);
- + return 0;
- +}
- +
- +
- +static inline void clear_write_error(int drive)
- +{
- + CLEARSTRUCT(UDRWE);
- +}
- +
- +static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
- + int drive, int type, kdev_t device)
- +{
- + int cnt;
- +
- + /* sanity checking for parameters.*/
- + if (g->sect <= 0 ||
- + g->head <= 0 ||
- + g->track <= 0 ||
- + g->track > UDP->tracks>>STRETCH(g) ||
- + /* check if reserved bits are set */
- + (g->stretch&~(FD_STRETCH|FD_SWAPSIDES)) != 0)
- + return -EINVAL;
- + if (type) {
- + if (!suser())
- + return -EPERM;
- + LOCK_FDC(drive,1);
- + for (cnt = 0; cnt < N_DRIVE; cnt++){
- + if (ITYPE(drive_state[cnt].fd_device) == type &&
- + drive_state[cnt].fd_ref)
- + set_bit(drive, &fake_change);
- + }
- + floppy_type[type] = *g;
- + floppy_type[type].name="user format";
- + for (cnt = type << 2; cnt < (type << 2) + 4; cnt++)
- + floppy_sizes[cnt]= floppy_sizes[cnt+0x80]=
- + floppy_type[type].size>>1;
- + process_fd_request();
- + for ( cnt = 0; cnt < N_DRIVE; cnt++){
- + if (ITYPE(drive_state[cnt].fd_device) == type &&
- + drive_state[cnt].fd_ref)
- + check_disk_change(
- + MKDEV(FLOPPY_MAJOR,
- + drive_state[cnt].fd_device));
- + }
- + } else {
- + LOCK_FDC(drive,1);
- + if ( cmd != FDDEFPRM )
- + /* notice a disk change immediately, else
- + * we loose our settings immediately*/
- + CALL(poll_drive(1, FD_RAW_NEED_DISK));
- + user_params[drive] = *g;
- + if (buffer_drive == drive)
- + SUPBOUND(buffer_max, user_params[drive].sect);
- + current_type[drive] = &user_params[drive];
- + floppy_sizes[drive] = user_params[drive].size >> 1;
- + if (cmd == FDDEFPRM)
- + DRS->keep_data = -1;
- + else
- + DRS->keep_data = 1;
- + /* invalidation. Invalidate only when needed, i.e.
- + * when there are already sectors in the buffer cache
- + * whose number will change. This is useful, because
- + * mtools often changes the geometry of the disk after
- + * looking at the boot block */
- + if (DRS->maxblock > user_params[drive].sect || DRS->maxtrack)
- + invalidate_drive(device);
- + else
- + process_fd_request();
- + }
- + return 0;
- +}
- +
- +/* handle obsolete ioctl's */
- +static struct translation_entry {
- + int newcmd;
- + int oldcmd;
- + int oldsize; /* size of 0x00xx-style ioctl. Reflects old structures, thus
- + * use numeric values. NO SIZEOFS */
- +} translation_table[]= {
- + {FDCLRPRM, 0, 0},
- + {FDSETPRM, 1, 28},
- + {FDDEFPRM, 2, 28},
- + {FDGETPRM, 3, 28},
- + {FDMSGON, 4, 0},
- + {FDMSGOFF, 5, 0},
- + {FDFMTBEG, 6, 0},
- + {FDFMTTRK, 7, 12},
- + {FDFMTEND, 8, 0},
- + {FDSETEMSGTRESH, 10, 0},
- + {FDFLUSH, 11, 0},
- + {FDSETMAXERRS, 12, 20},
- + {OLDFDRAWCMD, 30, 0},
- + {FDGETMAXERRS, 14, 20},
- + {FDGETDRVTYP, 16, 16},
- + {FDSETDRVPRM, 20, 88},
- + {FDGETDRVPRM, 21, 88},
- + {FDGETDRVSTAT, 22, 52},
- + {FDPOLLDRVSTAT, 23, 52},
- + {FDRESET, 24, 0},
- + {FDGETFDCSTAT, 25, 40},
- + {FDWERRORCLR, 27, 0},
- + {FDWERRORGET, 28, 24},
- + {FDRAWCMD, 0, 0},
- + {FDEJECT, 0, 0},
- + {FDTWADDLE, 40, 0} };
- +
- +static inline int normalize_0x02xx_ioctl(int *cmd, int *size)
- +{
- + int i;
- +
- + for (i=0; i < ARRAY_SIZE(translation_table); i++) {
- + if ((*cmd & 0xffff) == (translation_table[i].newcmd & 0xffff)){
- + *size = _IOC_SIZE(*cmd);
- + *cmd = translation_table[i].newcmd;
- + if (*size > _IOC_SIZE(*cmd)) {
- + printk("ioctl not yet supported\n");
- + return -EFAULT;
- + }
- + return 0;
- + }
- + }
- + return -EINVAL;
- +}
- +
- +static inline int xlate_0x00xx_ioctl(int *cmd, int *size)
- +{
- + int i;
- + /* old ioctls' for kernels <= 1.3.33 */
- + /* When the next even release will come around, we'll start
- + * warning against these.
- + * When the next odd release will come around, we'll fail with
- + * -EINVAL */
- + if(strcmp(system_utsname.version, "1.4.0") >= 0)
- + printk("obsolete floppy ioctl %x\n", *cmd);
- + if((system_utsname.version[0] == '1' &&
- + strcmp(system_utsname.version, "1.5.0") >= 0) ||
- + (system_utsname.version[0] >= '2' &&
- + strcmp(system_utsname.version, "2.1.0") >= 0))
- + return -EINVAL;
- + for (i=0; i < ARRAY_SIZE(translation_table); i++) {
- + if (*cmd == translation_table[i].oldcmd) {
- + *size = translation_table[i].oldsize;
- + *cmd = translation_table[i].newcmd;
- + return 0;
- + }
- + }
- + return -EINVAL;
- +}
- +
- +static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
- + unsigned long param)
- +{
- +#define IOCTL_MODE_BIT 8
- +#define OPEN_WRITE_BIT 16
- +#define IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
- +#define OUT(c,x) case c: outparam = (const char *) (x); break
- +#define IN(c,x,tag) case c: *(x) = inparam. tag ; return 0
- +
- + int i,drive,type;
- + kdev_t device;
- + int ret;
- + int size;
- + union inparam {
- + struct floppy_struct g; /* geometry */
- + struct format_descr f;
- + struct floppy_max_errors max_errors;
- + struct floppy_drive_params dp;
- + } inparam; /* parameters coming from user space */
- + const char *outparam; /* parameters passed back to user space */
- +
- + device = inode->i_rdev;
- + switch (cmd) {
- + RO_IOCTLS(device,param);
- + }
- + type = TYPE(device);
- + drive = DRIVE(device);
- +
- + /* convert compatibility eject ioctls into floppy eject ioctl.
- + * We do this in order to provide a means to eject floppy disks before
- + * installing the new fdutils package */
- + if(cmd == CDROMEJECT || /* CD-ROM eject */
- + cmd == 0x6470 /* SunOS floppy eject */) {
- + DPRINT("obsolete eject ioctl\n");
- + DPRINT("please use floppycontrol --eject\n");
- + cmd = FDEJECT;
- + }
- +
- + /* convert the old style command into a new style command */
- + if ((cmd & 0xff00) == 0x0200) {
- + ECALL(normalize_0x02xx_ioctl(&cmd, &size));
- + } else if ((cmd & 0xff00) == 0x0000) {
- + ECALL(xlate_0x00xx_ioctl(&cmd, &size));
- + } else
- + return -EINVAL;
- +
- + /* permission checks */
- + if (((cmd & 0x80) && !suser()) ||
- + ((cmd & 0x40) && !IOCTL_ALLOWED))
- + return -EPERM;
- +
- + /* verify writability of result, and fail early */
- + if (_IOC_DIR(cmd) & _IOC_READ)
- + ECALL(verify_area(VERIFY_WRITE,(void *) param, size));
- +
- + /* copyin */
- + CLEARSTRUCT(&inparam);
- + if (_IOC_DIR(cmd) & _IOC_WRITE)
- + ECALL(fd_copyin((void *)param, &inparam, size))
- +
- + switch (cmd) {
- + case FDEJECT:
- + if (UDRS->fd_ref != 1)
- + /* somebody else has this drive open */
- + return -EBUSY;
- + LOCK_FDC(drive,1);
- +
- + /* do the actual eject. Fails on
- + * non-Sparc architectures */
- + ret=fd_eject(UNIT(drive));
- +
- + USETF(FD_DISK_CHANGED);
- + USETF(FD_VERIFY);
- + process_fd_request();
- + return ret;
- + case FDCLRPRM:
- + LOCK_FDC(drive,1);
- + current_type[drive] = NULL;
- + floppy_sizes[drive] = MAX_DISK_SIZE;
- + UDRS->keep_data = 0;
- + return invalidate_drive(device);
- + case FDSETPRM:
- + case FDDEFPRM:
- + return set_geometry(cmd, & inparam.g,
- + drive, type, device);
- + case FDGETPRM:
- + LOCK_FDC(drive,1);
- + CALL(poll_drive(1,0));
- + process_fd_request();
- + if (type)
- + outparam = (char *) &floppy_type[type];
- + else
- + outparam = (char *) current_type[drive];
- + if(!outparam)
- + return -ENODEV;
- + break;
- +
- + case FDMSGON:
- + UDP->flags |= FTD_MSG;
- + return 0;
- + case FDMSGOFF:
- + UDP->flags &= ~FTD_MSG;
- + return 0;
- +
- + case FDFMTBEG:
- + LOCK_FDC(drive,1);
- + CALL(poll_drive(1, FD_RAW_NEED_DISK));
- + ret = UDRS->flags;
- + process_fd_request();
- + if(ret & FD_VERIFY)
- + return -ENODEV;
- + if(!(ret & FD_DISK_WRITABLE))
- + return -EROFS;
- + return 0;
- + case FDFMTTRK:
- + if (UDRS->fd_ref != 1)
- + return -EBUSY;
- + return do_format(device, &inparam.f);
- + case FDFMTEND:
- + case FDFLUSH:
- + LOCK_FDC(drive,1);
- + return invalidate_drive(device);
- +
- + case FDSETEMSGTRESH:
- + UDP->max_errors.reporting =
- + (unsigned short) (param & 0x0f);
- + return 0;
- + OUT(FDGETMAXERRS, &UDP->max_errors);
- + IN(FDSETMAXERRS, &UDP->max_errors, max_errors);
- +
- + case FDGETDRVTYP:
- + outparam = drive_name(type,drive);
- + SUPBOUND(size,strlen(outparam)+1);
- + break;
- +
- + IN(FDSETDRVPRM, UDP, dp);
- + OUT(FDGETDRVPRM, UDP);
- +
- + case FDPOLLDRVSTAT:
- + LOCK_FDC(drive,1);
- + CALL(poll_drive(1, FD_RAW_NEED_DISK));
- + process_fd_request();
- + /* fall through */
- + OUT(FDGETDRVSTAT, UDRS);
- +
- + case FDRESET:
- + return user_reset_fdc(drive, (int)param, 1);
- +
- + OUT(FDGETFDCSTAT,UFDCS);
- +
- + case FDWERRORCLR:
- + CLEARSTRUCT(UDRWE);
- + return 0;
- + OUT(FDWERRORGET,UDRWE);
- +
- + case OLDFDRAWCMD:
- + case FDRAWCMD:
- + if (type)
- + return -EINVAL;
- + LOCK_FDC(drive,1);
- + set_floppy(device);
- + CALL(i = raw_cmd_ioctl(cmd,(void *) param));
- + process_fd_request();
- + return i;
- +
- + case FDTWADDLE:
- + LOCK_FDC(drive,1);
- + twaddle();
- + process_fd_request();
- + return 0;
- +
- + default:
- + return -EINVAL;
- + }
- +
- + if (_IOC_DIR(cmd) & _IOC_READ)
- + return fd_copyout((void *)param, outparam, size);
- + else
- + return 0;
- +#undef IOCTL_ALLOWED
- +#undef OUT
- +#undef IN
- +}
- +
- +static void config_types(void)
- +{
- + int first=1;
- + int drive;
- +
- + /* read drive info out of physical CMOS */
- + drive=0;
- + if (!UDP->cmos )
- + UDP->cmos= FLOPPY0_TYPE;
- + drive=1;
- + if (!UDP->cmos && FLOPPY1_TYPE)
- + UDP->cmos = FLOPPY1_TYPE;
- +
- + /* XXX */
- + /* additional physical CMOS drive detection should go here */
- +
- + for (drive=0; drive < N_DRIVE; drive++){
- + if (UDP->cmos >= 16)
- + UDP->cmos = 0;
- + if (UDP->cmos >= 0 && UDP->cmos <= NUMBER(default_drive_params))
- + memcpy((char *) UDP,
- + (char *) (&default_drive_params[(int)UDP->cmos].params),
- + sizeof(struct floppy_drive_params));
- + if (UDP->cmos){
- + if (first)
- + printk(KERN_INFO "Floppy drive(s): ");
- + else
- + printk(", ");
- + first=0;
- + if (UDP->cmos > 0 ){
- + allowed_drive_mask |= 1 << drive;
- + printk("fd%d is %s", drive,
- + default_drive_params[(int)UDP->cmos].name);
- + } else
- + printk("fd%d is unknown type %d",drive,
- + (unsigned int)UDP->cmos);
- + }
- + }
- + if(!first)
- + printk("\n");
- +}
- +
- +static int floppy_read(struct inode * inode, struct file * filp,
- + char * buf, int count)
- +{
- + int drive = DRIVE(inode->i_rdev);
- +
- + check_disk_change(inode->i_rdev);
- + if (UTESTF(FD_DISK_CHANGED))
- + return -ENXIO;
- + return block_read(inode, filp, buf, count);
- +}
- +
- +static int floppy_write(struct inode * inode, struct file * filp,
- + const char * buf, int count)
- +{
- + int block;
- + int ret;
- + int drive = DRIVE(inode->i_rdev);
- +
- + if (!UDRS->maxblock)
- + UDRS->maxblock=1;/* make change detectable */
- + check_disk_change(inode->i_rdev);
- + if (UTESTF(FD_DISK_CHANGED))
- + return -ENXIO;
- + if (!UTESTF(FD_DISK_WRITABLE))
- + return -EROFS;
- + block = (filp->f_pos + count) >> 9;
- + INFBOUND(UDRS->maxblock, block);
- + ret= block_write(inode, filp, buf, count);
- + return ret;
- +}
- +
- +static void floppy_release(struct inode * inode, struct file * filp)
- +{
- + int drive;
- +
- + drive = DRIVE(inode->i_rdev);
- +
- + if( !filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
- + /* if the file is mounted OR (writable now AND writable at
- + * open time) Linus: Does this cover all cases? */
- + block_fsync(inode,filp);
- +
- + if (UDRS->fd_ref < 0)
- + UDRS->fd_ref=0;
- + else if (!UDRS->fd_ref--) {
- + DPRINT("floppy_release with fd_ref == 0");
- + UDRS->fd_ref = 0;
- + }
- + floppy_release_irq_and_dma();
- +}
- +
- +/*
- + * floppy_open check for aliasing (/dev/fd0 can be the same as
- + * /dev/PS0 etc), and disallows simultaneous access to the same
- + * drive with different device numbers.
- + */
- +#define RETERR(x) do{floppy_release(inode,filp); return -(x);}while(0)
- +
- +static int floppy_open(struct inode * inode, struct file * filp)
- +{
- + int drive;
- + int old_dev;
- + int try;
- + char *tmp;
- +
- + if (!filp) {
- + DPRINT("Weird, open called with filp=0\n");
- + return -EIO;
- + }
- +
- + drive = DRIVE(inode->i_rdev);
- + if (drive >= N_DRIVE ||
- + !( allowed_drive_mask & ( 1 << drive)) ||
- + fdc_state[FDC(drive)].version == FDC_NONE)
- + return -ENXIO;
- +
- + if (TYPE(inode->i_rdev) >= NUMBER(floppy_type))
- + return -ENXIO;
- + old_dev = UDRS->fd_device;
- + if (UDRS->fd_ref && old_dev != MINOR(inode->i_rdev))
- + return -EBUSY;
- +
- + if(!UDRS->fd_ref && (UDP->flags & FD_BROKEN_DCL)){
- + USETF(FD_DISK_CHANGED);
- + USETF(FD_VERIFY);
- + }
- +
- + if(UDRS->fd_ref == -1 ||
- + (UDRS->fd_ref && (filp->f_flags & O_EXCL)))
- + return -EBUSY;
- +
- + if (floppy_grab_irq_and_dma())
- + return -EBUSY;
- +
- + if (filp->f_flags & O_EXCL)
- + UDRS->fd_ref = -1;
- + else
- + UDRS->fd_ref++;
- +
- + if (!floppy_track_buffer){
- + /* if opening an ED drive, reserve a big buffer,
- + * else reserve a small one */
- + if ((UDP->cmos == 6) || (UDP->cmos == 5))
- + try = 64; /* Only 48 actually useful */
- + else
- + try = 32; /* Only 24 actually useful */
- +
- + tmp=(char *)fd_dma_mem_alloc(1024 * try);
- + if (!tmp) {
- + try >>= 1; /* buffer only one side */
- + INFBOUND(try, 16);
- + tmp= (char *)fd_dma_mem_alloc(1024*try);
- + }
- + if (!tmp) {
- + DPRINT("Unable to allocate DMA memory\n");
- + RETERR(ENXIO);
- + }
- + if (floppy_track_buffer)
- + fd_dma_mem_free((unsigned long)tmp,try*1024);
- + else {
- + buffer_min = buffer_max = -1;
- + floppy_track_buffer = tmp;
- + max_buffer_sectors = try;
- + }
- + }
- +
- + UDRS->fd_device = MINOR(inode->i_rdev);
- + if (old_dev != -1 && old_dev != MINOR(inode->i_rdev)) {
- + if (buffer_drive == drive)
- + buffer_track = -1;
- + invalidate_buffers(MKDEV(FLOPPY_MAJOR,old_dev));
- + }
- +
- + /* Allow ioctls if we have write-permissions even if read-only open */
- + if ((filp->f_mode & 2) || (permission(inode,2) == 0))
- + filp->f_mode |= IOCTL_MODE_BIT;
- + if (filp->f_mode & 2)
- + filp->f_mode |= OPEN_WRITE_BIT;
- +
- + if (UFDCS->rawcmd == 1)
- + UFDCS->rawcmd = 2;
- +
- + if (filp->f_flags & O_NDELAY)
- + return 0;
- + if (filp->f_mode & 3) {
- + UDRS->last_checked = 0;
- + check_disk_change(inode->i_rdev);
- + if (UTESTF(FD_DISK_CHANGED))
- + RETERR(ENXIO);
- + }
- + if ((filp->f_mode & 2) && !(UTESTF(FD_DISK_WRITABLE)))
- + RETERR(EROFS);
- + return 0;
- +#undef RETERR
- +}
- +
- +/*
- + * Check if the disk has been changed or if a change has been faked.
- + */
- +static int check_floppy_change(kdev_t dev)
- +{
- + int drive = DRIVE( dev );
- +
- + if (MAJOR(dev) != MAJOR_NR) {
- + DPRINT("check_floppy_change: not a floppy\n");
- + return 0;
- + }
- +
- + if (UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY))
- + return 1;
- +
- + if (UDP->checkfreq < jiffies - UDRS->last_checked){
- + lock_fdc(drive,0);
- + poll_drive(0,0);
- + process_fd_request();
- + }
- +
- + if(UTESTF(FD_DISK_CHANGED) ||
- + UTESTF(FD_VERIFY) ||
- + test_bit(drive, &fake_change) ||
- + (!TYPE(dev) && !current_type[drive]))
- + return 1;
- + return 0;
- +}
- +
- +/* revalidate the floppy disk, i.e. trigger format autodetection by reading
- + * the bootblock (block 0). "Autodetection" is also needed to check whether
- + * there is a disk in the drive at all... Thus we also do it for fixed
- + * geometry formats */
- +static int floppy_revalidate(kdev_t dev)
- +{
- +#define NO_GEOM (!current_type[drive] && !TYPE(dev))
- + struct buffer_head * bh;
- + int drive=DRIVE(dev);
- + int cf;
- +
- + if(UTESTF(FD_DISK_CHANGED) ||
- + UTESTF(FD_VERIFY) ||
- + test_bit(drive, &fake_change) ||
- + NO_GEOM){
- + lock_fdc(drive,0);
- + cf = UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY);
- + if (!(cf || test_bit(drive, &fake_change) || NO_GEOM)){
- + process_fd_request(); /* already done by another thread */
- + return 0;
- + }
- + UDRS->maxblock = 0;
- + UDRS->maxtrack = 0;
- + if (buffer_drive == drive)
- + buffer_track = -1;
- + clear_bit(drive, &fake_change);
- + UCLEARF(FD_DISK_CHANGED);
- + if (cf)
- + UDRS->generation++;
- + if (NO_GEOM){
- + /* auto-sensing */
- + int size = floppy_blocksizes[MINOR(dev)];
- + if (!size)
- + size = 1024;
- + if (!(bh = getblk(dev,0,size))){
- + process_fd_request();
- + return 1;
- + }
- + if ( bh && !buffer_uptodate(bh))
- + ll_rw_block(READ, 1, &bh);
- + process_fd_request();
- + wait_on_buffer(bh);
- + brelse(bh);
- + return 0;
- + }
- + if (cf)
- + poll_drive(0, FD_RAW_NEED_DISK);
- + process_fd_request();
- + }
- + return 0;
- +}
- +
- +static struct file_operations floppy_fops = {
- + NULL, /* lseek - default */
- + floppy_read, /* read - general block-dev read */
- + floppy_write, /* write - general block-dev write */
- + NULL, /* readdir - bad */
- + NULL, /* select */
- + fd_ioctl, /* ioctl */
- + NULL, /* mmap */
- + floppy_open, /* open */
- + floppy_release, /* release */
- + block_fsync, /* fsync */
- + NULL, /* fasync */
- + check_floppy_change, /* media_change */
- + floppy_revalidate, /* revalidate */
- +};
- +
- +/*
- + * Floppy Driver initialization
- + * =============================
- + */
- +
- +/* Determine the floppy disk controller type */
- +/* This routine was written by David C. Niemi */
- +static char get_fdc_version(void)
- +{
- + int r;
- +
- + output_byte(FD_DUMPREGS); /* 82072 and better know DUMPREGS */
- + if (FDCS->reset)
- + return FDC_NONE;
- + if ((r = result()) <= 0x00)
- + return FDC_NONE; /* No FDC present ??? */
- + if ((r==1) && (reply_buffer[0] == 0x80)){
- + printk(KERN_INFO "FDC %d is an 8272A\n",fdc);
- + return FDC_8272A; /* 8272a/765 don't know DUMPREGS */
- + }
- + if (r != 10) {
- + printk("FDC %d init: DUMPREGS: unexpected return of %d bytes.\n",
- + fdc, r);
- + return FDC_UNKNOWN;
- + }
- +
- + if(!fdc_configure()) {
- + printk(KERN_INFO "FDC %d is an 82072\n",fdc);
- + return FDC_82072; /* 82072 doesn't know CONFIGURE */
- + }
- +
- + output_byte(FD_PERPENDICULAR);
- + if(need_more_output() == MORE_OUTPUT) {
- + output_byte(0);
- + } else {
- + printk(KERN_INFO "FDC %d is an 82072A\n", fdc);
- + return FDC_82072A; /* 82072A as found on Sparcs. */
- + }
- +
- + output_byte(FD_UNLOCK);
- + r = result();
- + if ((r == 1) && (reply_buffer[0] == 0x80)){
- + printk(KERN_INFO "FDC %d is a pre-1991 82077\n", fdc);
- + return FDC_82077_ORIG; /* Pre-1991 82077, doesn't know
- + * LOCK/UNLOCK */
- + }
- + if ((r != 1) || (reply_buffer[0] != 0x00)) {
- + printk("FDC %d init: UNLOCK: unexpected return of %d bytes.\n",
- + fdc, r);
- + return FDC_UNKNOWN;
- + }
- + output_byte(FD_PARTID);
- + r = result();
- + if (r != 1) {
- + printk("FDC %d init: PARTID: unexpected return of %d bytes.\n",
- + fdc, r);
- + return FDC_UNKNOWN;
- + }
- + if (reply_buffer[0] == 0x80) {
- + printk(KERN_INFO "FDC %d is a post-1991 82077\n",fdc);
- + return FDC_82077; /* Revised 82077AA passes all the tests */
- + }
- + switch (reply_buffer[0] >> 5) {
- + case 0x0:
- + /* Either a 82078-1 or a 82078SL running at 5Volt */
- + printk(KERN_INFO "FDC %d is an 82078.\n",fdc);
- + return FDC_82078;
- + case 0x1:
- + printk(KERN_INFO "FDC %d is a 44pin 82078\n",fdc);
- + return FDC_82078;
- + case 0x2:
- + printk(KERN_INFO "FDC %d is a S82078B\n", fdc);
- + return FDC_S82078B;
- + case 0x3:
- + printk(KERN_INFO "FDC %d is a National Semiconductor PC87306\n", fdc);
- + return FDC_87306;
- + default:
- + printk(KERN_INFO "FDC %d init: 82078 variant with unknown PARTID=%d.\n",
- + fdc, reply_buffer[0] >> 5);
- + return FDC_82078_UNKN;
- + }
- +} /* get_fdc_version */
- +
- +/* lilo configuration */
- +
- +/* we make the invert_dcl function global. One day, somebody might
- + * want to centralize all thinkpad related options into one lilo option,
- + * there are just so many thinkpad related quirks!
- + */
- +void floppy_invert_dcl(int *ints,int param)
- +{
- + int i;
- +
- + for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
- + if (param)
- + default_drive_params[i].params.flags |= 0x80;
- + else
- + default_drive_params[i].params.flags &= ~0x80;
- + }
- + DPRINT("Configuring drives for inverted dcl\n");
- +}
- +
- +static void daring(int *ints,int param)
- +{
- + int i;
- +
- + for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
- + if (param){
- + default_drive_params[i].params.select_delay = 0;
- + default_drive_params[i].params.flags |= FD_SILENT_DCL_CLEAR;
- + } else {
- + default_drive_params[i].params.select_delay = 2*HZ/100;
- + default_drive_params[i].params.flags &= ~FD_SILENT_DCL_CLEAR;
- + }
- + }
- + DPRINT("Assuming %s floppy hardware\n", param ? "standard" : "broken");
- +}
- +
- +static void set_cmos(int *ints, int dummy)
- +{
- + int current_drive=0;
- +
- + if (ints[0] != 2) {
- + DPRINT("wrong number of parameters for cmos\n");
- + return;
- + }
- + current_drive = ints[1];
- + if (current_drive < 0 || current_drive >= 8) {
- + DPRINT("bad drive for set_cmos\n");
- + return;
- + }
- + if (current_drive >= 4 && !FDC2)
- + FDC2 = 0x370;
- + if (ints[2] <= 0 ||
- + (ints[2] >= NUMBER(default_drive_params) && ints[2] != 16)){
- + DPRINT("bad cmos code %d\n", ints[2]);
- + return;
- + }
- + DP->cmos = ints[2];
- + DPRINT("setting cmos code to %d\n", ints[2]);
- +}
- +
- +static struct param_table {
- + const char *name;
- + void (*fn)(int *ints, int param);
- + int *var;
- + int def_param;
- +} config_params[]={
- + { "allowed_drive_mask", 0, &allowed_drive_mask, 0xff },
- + { "all_drives", 0, &allowed_drive_mask, 0xff },
- + { "asus_pci", 0, &allowed_drive_mask, 0x33 },
- +
- + { "daring", daring, 0, 1 },
- +
- + { "two_fdc", 0, &FDC2, 0x370 },
- + { "one_fdc", 0, &FDC2, 0 },
- +
- + { "thinkpad", floppy_invert_dcl, 0, 1 },
- +
- + { "nodma", 0, &use_virtual_dma, 1 },
- + { "omnibook", 0, &use_virtual_dma, 1 },
- + { "dma", 0, &use_virtual_dma, 0 },
- +
- + { "fifo_depth", 0, &fifo_depth, 0xa },
- + { "nofifo", 0, &no_fifo, 0x20 },
- + { "usefifo", 0, &no_fifo, 0 },
- +
- + { "cmos", set_cmos, 0, 0 },
- +
- + { "unexpected_interrupts", 0, &print_unex, 1 },
- + { "no_unexpected_interrupts", 0, &print_unex, 0 },
- + { "L40SX", 0, &print_unex, 0 } };
- +
- +#define FLOPPY_SETUP
- +void floppy_setup(char *str, int *ints)
- +{
- + int i;
- + int param;
- + if (str)
- + for (i=0; i< ARRAY_SIZE(config_params); i++){
- + if (strcmp(str,config_params[i].name) == 0){
- + if (ints[0])
- + param = ints[1];
- + else
- + param = config_params[i].def_param;
- + if(config_params[i].fn)
- + config_params[i].fn(ints,param);
- + if(config_params[i].var) {
- + DPRINT("%s=%d\n", str, param);
- + *config_params[i].var = param;
- + }
- + return;
- + }
- + }
- + if (str) {
- + DPRINT("unknown floppy option [%s]\n", str);
- +
- + DPRINT("allowed options are:");
- + for (i=0; i< ARRAY_SIZE(config_params); i++)
- + printk(" %s",config_params[i].name);
- + printk("\n");
- + } else
- + DPRINT("botched floppy option\n");
- + DPRINT("Read linux/arch/arm/drivers/block/README.fd\n");
- +}
- +
- +int floppy_init(void)
- +{
- + int i,unit,drive;
- + int have_no_fdc = -EIO;
- +
- + raw_cmd = NULL;
- +
- + if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
- + printk("Unable to get major %d for floppy\n",MAJOR_NR);
- + return -EBUSY;
- + }
- +
- + for(i=0; i<256; i++)
- + if (ITYPE(i))
- + floppy_sizes[i] = floppy_type[ITYPE(i)].size >> 1;
- + else
- + floppy_sizes[i] = MAX_DISK_SIZE;
- +
- + blk_size[MAJOR_NR] = floppy_sizes;
- + blksize_size[MAJOR_NR] = floppy_blocksizes;
- + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- + reschedule_timeout(MAXTIMEOUT, "floppy init", MAXTIMEOUT);
- + config_types();
- +
- + for (i = 0 ; i < N_FDC ; i++) {
- + fdc = i;
- + CLEARSTRUCT(FDCS);
- + FDCS->dtr = -1;
- + FDCS->dor = 0x4;
- + }
- +
- + fdc_state[0].address = FDC1;
- +#if N_FDC > 1
- + fdc_state[1].address = FDC2;
- +#endif
- +
- + if(floppy_grab_irq_and_dma()){
- + unregister_blkdev(MAJOR_NR,"fd");
- + return -EBUSY;
- + }
- +
- + /* initialise drive state */
- + for (drive = 0; drive < N_DRIVE ; drive++) {
- + CLEARSTRUCT(UDRS);
- + CLEARSTRUCT(UDRWE);
- + UDRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE | FD_DISK_CHANGED;
- + UDRS->fd_device = -1;
- + floppy_track_buffer = NULL;
- + max_buffer_sectors = 0;
- + }
- +
- + for (i = 0 ; i < N_FDC ; i++) {
- + fdc = i;
- + FDCS->driver_version = FD_DRIVER_VERSION;
- + for(unit=0; unit<4; unit++)
- + FDCS->track[unit] = 0;
- + if (FDCS->address == -1 )
- + continue;
- + FDCS->rawcmd = 2;
- + if (user_reset_fdc(-1,FD_RESET_ALWAYS,0)){
- + FDCS->address = -1;
- + FDCS->version = FDC_NONE;
- + continue;
- + }
- + /* Try to determine the floppy controller type */
- + FDCS->version = get_fdc_version();
- + if (FDCS->version == FDC_NONE){
- + FDCS->address = -1;
- + continue;
- + }
- +
- + request_region(FDCS->address, 6, "floppy");
- + request_region(FDCS->address+7, 1, "floppy DIR");
- + /* address + 6 is reserved, and may be taken by IDE.
- + * Unfortunately, Adaptec doesn't know this :-(, */
- +
- + have_no_fdc = 0;
- + /* Not all FDCs seem to be able to handle the version command
- + * properly, so force a reset for the standard FDC clones,
- + * to avoid interrupt garbage.
- + */
- + user_reset_fdc(-1,FD_RESET_ALWAYS,0);
- + }
- + fdc=0;
- + del_timer(&fd_timeout);
- + current_drive = 0;
- + floppy_release_irq_and_dma();
- + initialising=0;
- + if (have_no_fdc) {
- + DPRINT("no floppy controllers found\n");
- + unregister_blkdev(MAJOR_NR,"fd");
- + }
- + return have_no_fdc;
- +}
- +
- +static int floppy_grab_irq_and_dma(void)
- +{
- + int i;
- + unsigned long flags;
- +
- + INT_OFF;
- + if (usage_count++){
- + INT_ON;
- + return 0;
- + }
- + INT_ON;
- + MOD_INC_USE_COUNT;
- + for(i=0; i< N_FDC; i++){
- + if (fdc_state[i].address != -1){
- + fdc = i;
- + reset_fdc_info(1);
- + arm_set_dor(FDCS->dor);
- + }
- + }
- + fdc = 0;
- + set_dor(0, ~0, 8); /* avoid immediate interrupt */
- +
- + if (fd_request_irq()) {
- + DPRINT("Unable to grab IRQ%d for the floppy driver\n",
- + FLOPPY_IRQ);
- + MOD_DEC_USE_COUNT;
- + usage_count--;
- + return -1;
- + }
- + if (fd_request_dma()) {
- + DPRINT("Unable to grab DMA%d for the floppy driver\n",
- + FLOPPY_DMA);
- + fd_free_irq();
- + MOD_DEC_USE_COUNT;
- + usage_count--;
- + return -1;
- + }
- + for(fdc = 0; fdc < N_FDC ; fdc++)
- + if(FDCS->address != -1)
- + arm_set_dor(FDCS->dor);
- + fdc = 0;
- + fd_enable_irq();
- + return 0;
- +}
- +
- +static void floppy_release_irq_and_dma(void)
- +{
- +#ifdef FLOPPY_SANITY_CHECK
- + int drive;
- +#endif
- + long tmpsize;
- + unsigned long tmpaddr;
- + unsigned long flags;
- +
- + INT_OFF;
- + if (--usage_count){
- + INT_ON;
- + return;
- + }
- + INT_ON;
- + fd_disable_dma();
- + fd_free_dma();
- + fd_disable_irq();
- + fd_free_irq();
- +
- + set_dor(0, ~0, 8);
- +#if N_FDC > 1
- + set_dor(1, ~8, 0);
- +#endif
- + floppy_enable_hlt();
- +
- + if (floppy_track_buffer && max_buffer_sectors) {
- + tmpsize = max_buffer_sectors*1024;
- + tmpaddr = (unsigned long)floppy_track_buffer;
- + floppy_track_buffer = NULL;
- + max_buffer_sectors = 0;
- + buffer_min = buffer_max = -1;
- + fd_dma_mem_free(tmpaddr, tmpsize);
- + }
- +
- +#ifdef FLOPPY_SANITY_CHECK
- + for(drive=0; drive < N_FDC * 4; drive++)
- + if( motor_off_timer[drive].next )
- + printk("motor off timer %d still active\n", drive);
- +
- + if(fd_timeout.next)
- + printk("floppy timer still active:%s\n", timeout_message);
- + if (fd_timer.next)
- + printk("auxiliary floppy timer still active\n");
- + if(floppy_tq.sync)
- + printk("task queue still active\n");
- +#endif
- + MOD_DEC_USE_COUNT;
- +}
- +
- +
- +#ifdef MODULE
- +
- +extern char *get_options(char *str, int *ints);
- +
- +char *floppy=NULL;
- +
- +static void parse_floppy_cfg_string(char *cfg)
- +{
- + char *ptr;
- + int ints[11];
- +
- + while(*cfg) {
- + for(ptr = cfg;*cfg && *cfg != ' ' && *cfg != '\t'; cfg++);
- + if(*cfg) {
- + *cfg = '\0';
- + cfg++;
- + }
- + if(*ptr)
- + floppy_setup(get_options(ptr,ints),ints);
- + }
- +}
- +
- +static void mod_setup(char *pattern, void (*setup)(char *, int *))
- +{
- + unsigned long i;
- + char c;
- + int j;
- + int match;
- + char buffer[100];
- + int ints[11];
- + int length = strlen(pattern)+1;
- +
- + match=0;
- + j=1;
- +
- + for(i=current->mm->env_start; i< current->mm->env_end; i ++){
- + c= get_fs_byte(i);
- + if(match){
- + if(j==99)
- + c='\0';
- + buffer[j] = c;
- + if(!c || c == ' ' || c == '\t'){
- + if(j){
- + buffer[j] = '\0';
- + setup(get_options(buffer,ints),ints);
- + }
- + j=0;
- + } else
- + j++;
- + if(!c)
- + break;
- + continue;
- + }
- + if( (!j && !c) || ( j && c == pattern[j-1]))
- + j++;
- + else
- + j=0;
- + if(j==length){
- + match=1;
- + j=0;
- + }
- + }
- +}
- +
- +
- +#ifdef __cplusplus
- +extern "C" {
- +#endif
- +int init_module(void)
- +{
- + printk(KERN_INFO "inserting floppy driver for %s\n", kernel_version);
- +
- + if(floppy)
- + parse_floppy_cfg_string(floppy);
- + else
- + mod_setup("floppy=", floppy_setup);
- +
- + return floppy_init();
- +}
- +
- +void cleanup_module(void)
- +{
- + int fdc, dummy;
- +
- + for(fdc=0; fdc<2; fdc++)
- + if (FDCS->address != -1){
- + release_region(FDCS->address, 6);
- + release_region(FDCS->address+7, 1);
- + }
- +
- + unregister_blkdev(MAJOR_NR, "fd");
- +
- + blk_dev[MAJOR_NR].request_fn = 0;
- + /* eject disk, if any */
- + dummy = fd_eject(0);
- +}
- +
- +#ifdef __cplusplus
- +}
- +#endif
- +
- +#else
- +/* eject the boot floppy (if we need the drive for a different root floppy) */
- +/* This should only be called at boot time when we're sure that there's no
- + * resource contention. */
- +void floppy_eject(void)
- +{
- + int dummy;
- + floppy_grab_irq_and_dma();
- + lock_fdc(MAXTIMEOUT,0);
- + dummy=fd_eject(0);
- + process_fd_request();
- + floppy_release_irq_and_dma();
- +}
- +#endif
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/floppydma.S linux/arch/arm/drivers/block/floppydma.S
- --- linux.orig/arch/arm/drivers/block/floppydma.S Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/floppydma.S Sat Jun 22 21:55:21 1996
- @@ -0,0 +1,74 @@
- +/*
- + * linux/arch/arm/lib/floppydma.S
- + *
- + * Copyright (C) 1995, 1996 Russell King
- + */
- +
- +#include <asm/assembler.h>
- + .text
- +
- +LC0: .word _floppyfiq_len
- +
- + .global _floppy_fiqin_start
- + .global _floppy_fiqin_end
- +_floppy_fiqin_start:
- + subs r9, r9, #1
- + ldrgtb r12, [r11, #-4]
- + ldrleb r12, [r11], #0
- + strb r12, [r10], #1
- + subs pc, lr, #4
- +_floppy_fiqin_end:
- +
- + .global _floppy_fiqout_start
- + .global _floppy_fiqout_end
- +_floppy_fiqout_start:
- + subs r9, r9, #1
- + ldrgeb r12, [r10], #1
- + movlt r12, #0
- + strleb r12, [r11], #0
- + subles pc, lr, #4
- + strb r12, [r11, #-4]
- + subs pc, lr, #4
- +_floppy_fiqout_end:
- +
- +@ Params:
- +@ r0 = length
- +@ r1 = address
- +@ r2 = floppy port
- +@ Puts these into R9_fiq, R10_fiq, R11_fiq
- + .global _floppy_fiqsetup
- +_floppy_fiqsetup:
- + mov ip, sp
- + stmfd sp!, {fp, ip, lr, pc}
- + sub fp, ip, #4
- + mov r3, pc
- + teqp pc, #0x0c000001 @ disable FIQs, IRQs, FIQ mode
- + mov r0, r0 @ NOP
- + mov r9, r0
- + mov r10, r1
- + mov r11, r2
- + teqp r3, #0 @ back to normal
- + mov r0, r0 @ NOP
- + ldmea fp, {fp, sp, pc}^
- +
- + .global _floppy_fiqresidual
- +_floppy_fiqresidual:
- + mov ip, sp
- + stmfd sp!, {fp, ip, lr, pc}
- + sub fp, ip, #4
- + mov r3, pc
- + teqp pc, #0x0c000001 @ disable FIQs, IRQs, FIQ mode
- + mov r0, r0 @ NOP
- + mov r0, r9
- + teqp r3, #0
- + mov r0, r0 @ NOP
- + ldmea fp, {fp, sp, pc}^
- + .data
- +
- + .global _floppyfiq_len
- + .global _floppyfiq_to
- + .global _floppyfiq_from
- +
- +_floppyfiq_len: .word 0
- +_floppyfiq_to: .word 0
- +_floppyfiq_from: .word 0
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/genhd.c linux/arch/arm/drivers/block/genhd.c
- --- linux.orig/arch/arm/drivers/block/genhd.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/genhd.c Sun Aug 25 15:34:39 1996
- @@ -0,0 +1,401 @@
- +/*
- + * Code extracted from
- + * linux/kernel/hd.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + *
- + *
- + * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
- + * in the early extended-partition checks and added DM partitions
- + *
- + * Support for DiskManager v6.0x added by Mark Lord,
- + * with information provided by OnTrack. This now works for linux fdisk
- + * and LILO, as well as loadlin and bootln. Note that disks other than
- + * /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1).
- + *
- + * More flexible handling of extended partitions - aeb, 950831
- + *
- + * Modifications Copyright (C) 1995, 1996 Russell King
- + *
- + * Origional from linux/drivers/genhd.c
- + *
- + * Altered to find image files on ADFS-formatted hard drives.
- + */
- +
- +#include <linux/config.h>
- +#include <linux/fs.h>
- +#include <linux/genhd.h>
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +#include <linux/string.h>
- +#ifdef CONFIG_BLK_DEV_INITRD
- +#include <linux/blk.h>
- +#endif
- +
- +#include <asm/system.h>
- +
- +/*
- + * Many architectures don't like unaligned accesses, which is
- + * frequently the case with the nr_sects and start_sect partition
- + * table entries.
- + */
- +#include <asm/unaligned.h>
- +
- +#define SYS_IND(p) get_unaligned(&p->sys_ind)
- +#define NR_SECTS(p) get_unaligned(&p->nr_sects)
- +#define START_SECT(p) get_unaligned(&p->start_sect)
- +
- +
- +struct gendisk *gendisk_head;
- +
- +static int current_minor;
- +extern int *blk_size[];
- +extern void rd_load(void);
- +extern void initrd_load(void);
- +
- +extern int chr_dev_init(void);
- +extern int blk_dev_init(void);
- +extern int scsi_dev_init(void);
- +extern int net_dev_init(void);
- +
- +/*
- + * disk_name() is used by genhd.c and md.c.
- + * It formats the devicename of the indicated disk
- + * into the supplied buffer, and returns a pointer
- + * to that same buffer (for convenience).
- + */
- +char *disk_name (struct gendisk *hd, int minor, char *buf)
- +{
- + unsigned int part;
- + const char *maj = hd->major_name;
- + char unit = (minor >> hd->minor_shift) + 'a';
- +
- +#ifdef CONFIG_BLK_DEV_IDE
- + /*
- + * IDE devices use multiple major numbers, but the drives
- + * are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}..
- + * This requires special handling here.
- + */
- + switch (hd->major) {
- + case IDE3_MAJOR:
- + unit += 2;
- + case IDE2_MAJOR:
- + unit += 2;
- + case IDE1_MAJOR:
- + unit += 2;
- + case IDE0_MAJOR:
- + maj = "hd";
- + }
- +#endif
- + part = minor & ((1 << hd->minor_shift) - 1);
- + if (part)
- + sprintf(buf, "%s%c%d", maj, unit, part);
- + else
- + sprintf(buf, "%s%c", maj, unit);
- + return buf;
- +}
- +
- +void add_partition (struct gendisk *hd, int minor, int start, int size)
- +{
- + char buf[8];
- + hd->part[minor].start_sect = start;
- + hd->part[minor].nr_sects = size;
- + printk(" %s", disk_name(hd, minor, buf));
- +}
- +
- +static inline int is_extended_partition(struct partition *p)
- +{
- + return (SYS_IND(p) == DOS_EXTENDED_PARTITION ||
- + SYS_IND(p) == LINUX_EXTENDED_PARTITION);
- +}
- +
- +#ifdef CONFIG_MSDOS_PARTITION
- +/*
- + * Create devices for each logical partition in an extended partition.
- + * The logical partitions form a linked list, with each entry being
- + * a partition table with two entries. The first entry
- + * is the real data partition (with a start relative to the partition
- + * table start). The second is a pointer to the next logical partition
- + * (with a start relative to the entire extended partition).
- + * We do not create a Linux partition for the partition tables, but
- + * only for the actual data partitions.
- + */
- +
- +static void extended_partition(struct gendisk *hd, kdev_t dev)
- +{
- + struct buffer_head *bh;
- + struct partition *p;
- + unsigned long first_sector, first_size, this_sector, this_size;
- + int mask = (1 << hd->minor_shift) - 1;
- + int i;
- +
- + first_sector = hd->part[MINOR(dev)].start_sect;
- + first_size = hd->part[MINOR(dev)].nr_sects;
- + this_sector = first_sector;
- +
- + while (1) {
- + if ((current_minor & mask) == 0)
- + return;
- + if (!(bh = bread(dev,0,1024)))
- + return;
- + /*
- + * This block is from a device that we're about to stomp on.
- + * So make sure nobody thinks this block is usable.
- + */
- + bh->b_state = 0;
- +
- + if (*(unsigned short *) (bh->b_data+510) != 0xAA55)
- + goto done;
- +
- + p = (struct partition *) (0x1BE + bh->b_data);
- +
- + this_size = hd->part[MINOR(dev)].nr_sects;
- +
- + /*
- + * Usually, the first entry is the real data partition,
- + * the 2nd entry is the next extended partition, or empty,
- + * and the 3rd and 4th entries are unused.
- + * However, DRDOS sometimes has the extended partition as
- + * the first entry (when the data partition is empty),
- + * and OS/2 seems to use all four entries.
- + */
- +
- + /*
- + * First process the data partition(s)
- + */
- + for (i=0; i<4; i++, p++) {
- + if (!NR_SECTS(p) || is_extended_partition(p))
- + continue;
- +
- + /* Check the 3rd and 4th entries -
- + these sometimes contain random garbage */
- + if (i >= 2
- + && START_SECT(p) + NR_SECTS(p) > this_size
- + && (this_sector + START_SECT(p) < first_sector ||
- + this_sector + START_SECT(p) + NR_SECTS(p) >
- + first_sector + first_size))
- + continue;
- +
- + add_partition(hd, current_minor, this_sector+START_SECT(p), NR_SECTS(p));
- + current_minor++;
- + if ((current_minor & mask) == 0)
- + goto done;
- + }
- + /*
- + * Next, process the (first) extended partition, if present.
- + * (So far, there seems to be no reason to make
- + * extended_partition() recursive and allow a tree
- + * of extended partitions.)
- + * It should be a link to the next logical partition.
- + * Create a minor for this just long enough to get the next
- + * partition table. The minor will be reused for the next
- + * data partition.
- + */
- + p -= 4;
- + for (i=0; i<4; i++, p++)
- + if(NR_SECTS(p) && is_extended_partition(p))
- + break;
- + if (i == 4)
- + goto done; /* nothing left to do */
- +
- + hd->part[current_minor].nr_sects = NR_SECTS(p);
- + hd->part[current_minor].start_sect = first_sector + START_SECT(p);
- + this_sector = first_sector + START_SECT(p);
- + dev = MKDEV(hd->major, current_minor);
- + brelse(bh);
- + }
- +done:
- + brelse(bh);
- +}
- +
- +static int msdos_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector)
- +{
- + int i, minor = current_minor;
- + struct buffer_head *bh;
- + struct partition *p;
- + unsigned char *data;
- + int mask = (1 << hd->minor_shift) - 1;
- +
- + if (!(bh = bread(dev,0,1024))) {
- + printk(" unable to read partition table\n");
- + return -1;
- + }
- + data = bh->b_data;
- + /* In some cases we modify the geometry */
- + /* of the drive (below), so ensure that */
- + /* nobody else tries to re-use this data. */
- + bh->b_state = 0;
- +
- + if (*(unsigned short *) (0x1fe + data) != 0xAA55) {
- + brelse(bh);
- + return 0;
- + }
- + printk (" [DOS]");
- + p = (struct partition *) (0x1be + data);
- +
- + current_minor += 4;
- + for (i=1 ; i<=4 ; minor++, i++, p++) {
- + if(!NR_SECTS(p))
- + continue;
- + add_partition(hd, minor, first_sector+START_SECT(p), NR_SECTS(p));
- + if (is_extended_partition(p)) {
- + printk(" <");
- + /*
- + * If we are rereading the partition table, we need
- + * to set the size of the partition so that we will
- + * be able to bread the block containing the extended
- + * partition info.
- + */
- + hd->sizes[minor] = hd->part[minor].nr_sects
- + >> (BLOCK_SIZE_BITS - 9);
- + extended_partition(hd, MKDEV(hd->major, minor));
- + printk(" >");
- + /* prevent someone doing mkfs or mkswap on an
- + extended partition, but leave room for LILO */
- + if (hd->part[minor].nr_sects > 2)
- + hd->part[minor].nr_sects = 2;
- + }
- + }
- + /*
- + * Check for old-style Disk Manager partition table
- + */
- + if (*(unsigned short *) (data+0xfc) == 0x55AA) {
- + p = (struct partition *) (0x1be + data);
- + for (i = 4 ; i < 16 ; i++, current_minor++) {
- + p--;
- + if ((current_minor & mask) == 0)
- + break;
- + if (!(START_SECT(p) && NR_SECTS(p)))
- + continue;
- + add_partition(hd, current_minor, START_SECT(p), NR_SECTS(p));
- + }
- + }
- + printk("\n");
- + brelse(bh);
- + return 1;
- +}
- +
- +#endif /* CONFIG_MSDOS_PARTITION */
- +
- +extern int adfspart_initdev (struct gendisk *hd, kdev_t dev, int first_sector, int minor);
- +
- +static void check_partition(struct gendisk *hd, kdev_t dev)
- +{
- + static int first_time = 1;
- + unsigned long first_sector;
- + char buf[8];
- +
- + if (first_time)
- + printk("Partition check:\n");
- + first_time = 0;
- + first_sector = hd->part[MINOR(dev)].start_sect;
- +
- + /*
- + * This is a kludge to allow the partition check to be
- + * skipped for specific drives (e.g. IDE cd-rom drives)
- + */
- + if ((int)first_sector == -1) {
- + hd->part[MINOR(dev)].start_sect = 0;
- + return;
- + }
- +
- + printk(" %s:", disk_name(hd, MINOR(dev), buf));
- +
- + if (adfspart_initdev (hd, dev, first_sector, current_minor))
- + return;
- +
- +#ifdef CONFIG_MSDOS_PARTITION
- + if (msdos_partition(hd, dev, first_sector))
- + return;
- +#endif
- + printk(" unknown partition table\n");
- +}
- +
- +/*
- + * This function is used to re-read partition tables for removable disks.
- + * Much of the cleanup from the old partition tables should have already been
- + * done
- + */
- +
- +/*
- + * This function will re-read the partition tables for a given device,
- + * and set things back up again. There are some important caveats,
- + * however. You must ensure that no one is using the device, and no one
- + * can start using the device while this function is being executed.
- + */
- +void resetup_one_dev(struct gendisk *dev, int drive)
- +{
- + int i;
- + int first_minor = drive << dev->minor_shift;
- + int end_minor = first_minor + dev->max_p;
- +
- + blk_size[dev->major] = NULL;
- + current_minor = 1 + first_minor;
- + check_partition(dev, MKDEV(dev->major, first_minor));
- +
- + /*
- + * We need to set the sizes array before we will be able to access
- + * any of the partitions on this device.
- + */
- + if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
- + for (i = first_minor; i < end_minor; i++)
- + dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
- + blk_size[dev->major] = dev->sizes;
- + }
- +}
- +
- +static void setup_dev(struct gendisk *dev)
- +{
- + int i, drive;
- + int end_minor = dev->max_nr * dev->max_p;
- +
- + blk_size[dev->major] = NULL;
- + for (i = 0 ; i < end_minor; i++) {
- + dev->part[i].start_sect = 0;
- + dev->part[i].nr_sects = 0;
- + }
- + dev->init(dev);
- + for (drive=0 ; drive < dev->nr_real ; drive++) {
- + int first_minor = drive << dev->minor_shift;
- + current_minor = 1 + first_minor;
- + check_partition(dev, MKDEV(dev->major, first_minor));
- + }
- + if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
- + for (i = 0; i < end_minor; i++)
- + dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
- + blk_size[dev->major] = dev->sizes;
- + }
- +}
- +
- +void device_setup(void)
- +{
- + extern void console_map_init(void);
- + extern void adfsimg_init (void);
- + struct gendisk *p;
- + int nr=0;
- +
- + chr_dev_init();
- + blk_dev_init();
- + sti();
- +#ifdef CONFIG_SCSI
- + scsi_dev_init();
- +#endif
- +#ifdef CONFIG_INET
- + net_dev_init();
- +#endif
- + console_map_init();
- +#ifdef CONFIG_BLK_DEV_IMG
- + adfsimg_init ();
- +#endif
- + for (p = gendisk_head ; p ; p=p->next) {
- + setup_dev(p);
- + nr += p->nr_real;
- + }
- +#ifdef CONFIG_BLK_DEV_RAM
- +#ifdef CONFIG_BLK_DEV_INITRD
- + if (initrd_start && mount_initrd) initrd_load();
- + else
- +#endif
- + rd_load();
- +#endif
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/hd.c linux/arch/arm/drivers/block/hd.c
- --- linux.orig/arch/arm/drivers/block/hd.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/hd.c Tue Apr 23 23:18:07 1996
- @@ -0,0 +1,1137 @@
- +/*
- + * linux/arch/arm/drivers/block/hd.c
- + * [ origional file: linux/drivers/block/hd.c ]
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + * Modified 1995 Russell King for ARM processor.
- + */
- +
- +/*
- + * This is the low-level hd interrupt support. It traverses the
- + * request-list, using interrupts to jump between functions. As
- + * all the functions are called within interrupts, we may not
- + * sleep. Special care is recommended.
- + *
- + * modified by Drew Eckhardt to check nr of hd's from the CMOS.
- + *
- + * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
- + * in the early extended-partition checks and added DM partitions
- + *
- + * IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
- + * and general streamlining by mlord@bnr.ca (Mark Lord).
- + */
- +
- +#define DEFAULT_MULT_COUNT 0 /* set to 0 to disable multiple mode at boot */
- +#define DEFAULT_UNMASK_INTR 0 /* set to 0 to *NOT* unmask irq's more often */
- +
- +#include <asm/irq.h>
- +#include <linux/errno.h>
- +#include <linux/signal.h>
- +#include <linux/sched.h>
- +#include <linux/timer.h>
- +#include <linux/fs.h>
- +#include <linux/kernel.h>
- +#include <linux/hdreg.h>
- +#include <linux/genhd.h>
- +#include <linux/malloc.h>
- +#include <linux/string.h>
- +#include <linux/ioport.h>
- +
- +#define REALLY_SLOW_IO
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <asm/segment.h>
- +
- +#define MAJOR_NR HD_MAJOR
- +#include <linux/blk.h>
- +
- +#undef HD_IRQ
- +#define HD_IRQ 11
- +
- +static int revalidate_hddisk(kdev_t, int);
- +
- +#define HD_DELAY 0
- +
- +#define MAX_ERRORS 16 /* Max read/write errors/sector */
- +#define RESET_FREQ 8 /* Reset controller every 8th retry */
- +#define RECAL_FREQ 4 /* Recalibrate every 4th retry */
- +#define MAX_HD 2
- +
- +#define STAT_OK (READY_STAT|SEEK_STAT)
- +#define OK_STATUS(s) (((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK)
- +
- +static void recal_intr(void);
- +static void bad_rw_intr(void);
- +
- +static char recalibrate[MAX_HD] =;
- +static char special_op[MAX_HD];
- +static int access_count[MAX_HD];
- +static char busy[MAX_HD];
- +static struct wait_queue * busy_wait;
- +
- +static int reset;
- +static int hd_error;
- +
- +/*
- + * This struct defines the HD's and their types.
- + */
- +struct hd_i_struct {
- + unsigned int head,sect,cyl,wpcom,lzone,ctl;
- + };
- +static struct hd_driveid *hd_ident_info[MAX_HD];
- +
- +#ifdef HD_TYPE
- +static struct hd_i_struct hd_info[] = { HD_TYPE };
- +struct hd_i_struct bios_info[] = { HD_TYPE };
- +static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct)));
- +#else
- +static struct hd_i_struct hd_info[];
- +struct hd_i_struct bios_info[];
- +static int NR_HD;
- +#endif
- +
- +static struct hd_struct hd[MAX_HD<<6];
- +static int hd_sizes[MAX_HD<<6];
- +static int hd_blocksizes[MAX_HD<<6];
- +static int hd_hardsectsizes[MAX_HD<<6];
- +
- +#if (HD_DELAY > 0)
- +unsigned long last_req;
- +
- +unsigned long read_timer(void)
- +{
- + unsigned long t, flags;
- + int i;
- +
- + save_flags_cli (flags);
- + t = jiffies * 11932;
- + outb_p(0, 0x43);
- + i = inb_p(0x40);
- + i |= inb(0x40) << 8;
- + restore_flags(flags);
- + return(t - i);
- +}
- +#endif
- +
- +void hd_setup(char *str, int *ints)
- +{
- + int hdind = 0;
- +
- + if (ints[0] != 3)
- + return;
- + if (bios_info[0].head != 0)
- + hdind=1;
- + bios_info[hdind].head = hd_info[hdind].head = ints[2];
- + bios_info[hdind].sect = hd_info[hdind].sect = ints[3];
- + bios_info[hdind].cyl = hd_info[hdind].cyl = ints[1];
- + bios_info[hdind].wpcom = hd_info[hdind].wpcom = 0;
- + bios_info[hdind].lzone = hd_info[hdind].lzone = ints[1];
- + bios_info[hdind].ctl = hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
- + NR_HD = hdind+1;
- +}
- +
- +static void dump_status (const char *msg, unsigned int stat)
- +{
- + unsigned long flags;
- + char devc;
- +
- + devc = CURRENT ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
- + save_flags (flags);
- + sti();
- + printk("hd%c: %s: status=0x%02x { ", devc, msg, stat & 0xff);
- + if (stat & BUSY_STAT) printk("Busy ");
- + if (stat & READY_STAT) printk("DriveReady ");
- + if (stat & WRERR_STAT) printk("WriteFault ");
- + if (stat & SEEK_STAT) printk("SeekComplete ");
- + if (stat & DRQ_STAT) printk("DataRequest ");
- + if (stat & ECC_STAT) printk("CorrectedError ");
- + if (stat & INDEX_STAT) printk("Index ");
- + if (stat & ERR_STAT) printk("Error ");
- + printk("}\n");
- + if ((stat & ERR_STAT) == 0) {
- + hd_error = 0;
- + } else {
- + hd_error = inb(HD_ERROR);
- + printk("hd%c: %s: error=0x%02x { ", devc, msg, hd_error & 0xff);
- + if (hd_error & BBD_ERR) printk("BadSector ");
- + if (hd_error & ECC_ERR) printk("UncorrectableError ");
- + if (hd_error & ID_ERR) printk("SectorIdNotFound ");
- + if (hd_error & ABRT_ERR) printk("DriveStatusError ");
- + if (hd_error & TRK0_ERR) printk("TrackZeroNotFound ");
- + if (hd_error & MARK_ERR) printk("AddrMarkNotFound ");
- + printk("}");
- + if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
- + printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),
- + inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));
- + if (CURRENT)
- + printk(", sector=%ld", CURRENT->sector);
- + }
- + printk("\n");
- + }
- + restore_flags (flags);
- +}
- +
- +void check_status(void)
- +{
- + int i = inb_p(HD_STATUS);
- +
- + if (!OK_STATUS(i)) {
- + dump_status("check_status", i);
- + bad_rw_intr();
- + }
- +}
- +
- +static int controller_busy(void)
- +{
- + int retries = 100000;
- + unsigned char status;
- +
- + do {
- + status = inb_p(HD_STATUS);
- + } while ((status & BUSY_STAT) && --retries);
- + return status;
- +}
- +
- +static int status_ok(void)
- +{
- + unsigned char status = inb_p(HD_STATUS);
- +
- + if (status & BUSY_STAT)
- + return 1; /* Ancient, but does it make sense??? */
- + if (status & WRERR_STAT)
- + return 0;
- + if (!(status & READY_STAT))
- + return 0;
- + if (!(status & SEEK_STAT))
- + return 0;
- + return 1;
- +}
- +
- +static int controller_ready(unsigned int drive, unsigned int head)
- +{
- + int retry = 100;
- +
- + do {
- + if (controller_busy() & BUSY_STAT)
- + return 0;
- + outb_p(0xA0 | (drive<<4) | head, HD_CURRENT);
- + if (status_ok())
- + return 1;
- + } while (--retry);
- + return 0;
- +}
- +
- +static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
- + unsigned int head,unsigned int cyl,unsigned int cmd,
- + void (*intr_addr)(void))
- +{
- + unsigned short port;
- +
- +#if (HD_DELAY > 0)
- + while (read_timer() - last_req < HD_DELAY)
- + /* nothing */;
- +#endif
- + if (reset)
- + return;
- + if (!controller_ready(drive, head)) {
- + reset = 1;
- + return;
- + }
- + SET_INTR(intr_addr);
- + outb_p(hd_info[drive].ctl,HD_CMD);
- + port=HD_DATA;
- + outb_p(hd_info[drive].wpcom>>2,++port);
- + outb_p(nsect,++port);
- + outb_p(sect,++port);
- + outb_p(cyl,++port);
- + outb_p(cyl>>8,++port);
- + outb_p(0xA0|(drive<<4)|head,++port);
- + outb_p(cmd,++port);
- +}
- +
- +static void hd_request (void);
- +static unsigned int identified [MAX_HD]; /* 1 = drive ID already displayed */
- +static unsigned int unmask_intr [MAX_HD]; /* 1 = unmask IRQs during I/O */
- +static unsigned int max_mult [MAX_HD]; /* max sectors for MultMode */
- +static unsigned int mult_req [MAX_HD]; /* requested MultMode count */
- +static unsigned int mult_count [MAX_HD]; /* currently enabled MultMode count */
- +static struct request WCURRENT;
- +
- +static void fixstring (unsigned char *s, int bytecount)
- +{
- + unsigned char *p, *end = &s[bytecount &= ~1]; /* bytecount must be even */
- +
- + /* convert from big-endian to little-endian */
- + for (p = end ; p != s;) {
- + unsigned short *pp = (unsigned short *) (p -= 2);
- + *pp = (*pp >> 8) | (*pp << 8);
- + }
- +
- + /* strip leading blanks */
- + while (s != end && *s == ' ')
- + ++s;
- +
- + /* compress internal blanks and strip trailing blanks */
- + while (s != end && *s) {
- + if (*s++ != ' ' || (s != end && *s && *s != ' '))
- + *p++ = *(s-1);
- + }
- +
- + /* wipe out trailing garbage */
- + while (p != end)
- + *p++ = '\0';
- +}
- +
- +static void identify_intr(void)
- +{
- + unsigned int dev = DEVICE_NR(CURRENT->rq_dev);
- + unsigned short stat = inb_p(HD_STATUS);
- + struct hd_driveid *id = hd_ident_info[dev];
- +
- + if (unmask_intr[dev])
- + sti();
- + if (stat & (BUSY_STAT|ERR_STAT)) {
- + printk (" hd%c: non-IDE device, %dMB, CHS=%d/%d/%d\n", dev+'a',
- + hd_info[dev].cyl*hd_info[dev].head*hd_info[dev].sect / 2048,
- + hd_info[dev].cyl, hd_info[dev].head, hd_info[dev].sect);
- + if (id != NULL) {
- + hd_ident_info[dev] = NULL;
- + kfree_s (id, 512);
- + }
- + } else {
- + insw(HD_DATA, id, 256); /* get ID info */
- + max_mult[dev] = id->max_multsect;
- + if ((id->field_valid&1) && id->cur_cyls && id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {
- + /*
- + * Extract the physical drive geometry for our use.
- + * Note that we purposely do *not* update the bios_info.
- + * This way, programs that use it (like fdisk) will
- + * still have the same logical view as the BIOS does,
- + * which keeps the partition table from being screwed.
- + */
- + hd_info[dev].cyl = id->cur_cyls;
- + hd_info[dev].head = id->cur_heads;
- + hd_info[dev].sect = id->cur_sectors;
- + }
- + fixstring (id->serial_no, sizeof(id->serial_no));
- + fixstring (id->fw_rev, sizeof(id->fw_rev));
- + fixstring (id->model, sizeof(id->model));
- + printk (" hd%c: %.40s, %dMB w/%dKB Cache, CHS=%d/%d/%d, MaxMult=%d\n",
- + dev+'a', id->model, id->cyls*id->heads*id->sectors/2048,
- + id->buf_size/2, bios_info[dev].cyl, bios_info[dev].head,
- + bios_info[dev].sect, id->max_multsect);
- + /*
- + * Early model Quantum drives go weird at this point,
- + * but doing a recalibrate seems to "fix" them.
- + * (Doing a full reset confuses some other model Quantums)
- + */
- + if (!strncmp(id->model, "QUANTUM", 7))
- + special_op[dev] = recalibrate[dev] = 1;
- + }
- +#if (HD_DELAY > 0)
- + last_req = read_timer();
- +#endif
- + hd_request();
- + return;
- +}
- +
- +static void set_multmode_intr(void)
- +{
- + unsigned int dev = DEVICE_NR(CURRENT->rq_dev), stat = inb_p(HD_STATUS);
- +
- + if (unmask_intr[dev])
- + sti();
- + if (stat & (BUSY_STAT|ERR_STAT)) {
- + mult_req[dev] = mult_count[dev] = 0;
- + dump_status("set multmode failed", stat);
- + } else {
- + if ((mult_count[dev] = mult_req[dev]))
- + printk (" hd%c: enabled %d-sector multiple mode\n",
- + dev+'a', mult_count[dev]);
- + else
- + printk (" hd%c: disabled multiple mode\n", dev+'a');
- + }
- +#if (HD_DELAY > 0)
- + last_req = read_timer();
- +#endif
- + hd_request();
- + return;
- +}
- +
- +static int drive_busy(void)
- +{
- + unsigned int i;
- + unsigned char c;
- +
- + for (i = 0; i < 500000 ; i++) {
- + c = inb_p(HD_STATUS);
- + if ((c & (BUSY_STAT | READY_STAT | SEEK_STAT)) == STAT_OK)
- + return 0;
- + }
- + dump_status("reset timed out", c);
- + return 1;
- +}
- +
- +static void reset_controller(void)
- +{
- + int i;
- +
- + outb_p(4,HD_CMD);
- + for(i = 0; i < 1000; i++) barrier();
- + outb_p(hd_info[0].ctl & 0x0f,HD_CMD);
- + for(i = 0; i < 1000; i++) barrier();
- + if (drive_busy())
- + printk("hd: controller still busy\n");
- + else if ((hd_error = inb(HD_ERROR)) != 1)
- + printk("hd: controller reset failed: %02x\n",hd_error);
- +}
- +
- +static void reset_hd(void)
- +{
- + static int i;
- +
- +repeat:
- + if (reset) {
- + reset = 0;
- + i = -1;
- + reset_controller();
- + } else {
- + check_status();
- + if (reset)
- + goto repeat;
- + }
- + if (++i < NR_HD) {
- + special_op[i] = recalibrate[i] = 1;
- + if (unmask_intr[i]) {
- + unmask_intr[i] = DEFAULT_UNMASK_INTR;
- + printk("hd%c: reset irq-unmasking to %d\n",i+'a',
- + DEFAULT_UNMASK_INTR);
- + }
- + if (mult_req[i] || mult_count[i]) {
- + mult_count[i] = 0;
- + mult_req[i] = DEFAULT_MULT_COUNT;
- + printk("hd%c: reset multiple mode to %d\n",i+'a',
- + DEFAULT_MULT_COUNT);
- + }
- + hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
- + hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
- + if (reset)
- + goto repeat;
- + } else
- + hd_request();
- +}
- +
- +/*
- + * Ok, don't know what to do with the unexpected interrupts: on some machines
- + * doing a reset and a retry seems to result in an eternal loop. Right now I
- + * ignore it, and just set the timeout.
- + *
- + * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
- + * drive enters "idle", "standby", or "sleep" mode, so if the status looks
- + * "good", we just ignore the interrupt completely.
- + */
- +void unexpected_hd_interrupt(void)
- +{
- + unsigned int stat = inb_p(HD_STATUS);
- +
- + if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) {
- + dump_status ("unexpected interrupt", stat);
- + SET_TIMER;
- + }
- +}
- +
- +/*
- + * bad_rw_intr() now tries to be a bit smarter and does things
- + * according to the error returned by the controller.
- + * -Mika Liljeberg (liljeber@cs.Helsinki.FI)
- + */
- +static void bad_rw_intr(void)
- +{
- + int dev;
- +
- + if (!CURRENT)
- + return;
- + dev = DEVICE_NR(CURRENT->rq_dev);
- + if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
- + end_request(0);
- + special_op[dev] = recalibrate[dev] = 1;
- + } else if (CURRENT->errors % RESET_FREQ == 0)
- + reset = 1;
- + else if ((hd_error & TRK0_ERR) || CURRENT->errors % RECAL_FREQ == 0)
- + special_op[dev] = recalibrate[dev] = 1;
- + /* Otherwise just retry */
- +}
- +
- +static inline int wait_DRQ(void)
- +{
- + int retries = 100000, stat;
- +
- + while (--retries > 0)
- + if ((stat = inb_p(HD_STATUS)) & DRQ_STAT)
- + return 0;
- + dump_status("wait_DRQ", stat);
- + return -1;
- +}
- +
- +static void read_intr(void)
- +{
- + unsigned int dev = DEVICE_NR(CURRENT->rq_dev);
- + int i, retries = 100000, msect = mult_count[dev], nsect;
- +
- + if (unmask_intr[dev])
- + sti(); /* permit other IRQs during xfer */
- + do {
- + i = (unsigned) inb_p(HD_STATUS);
- + if (i & BUSY_STAT)
- + continue;
- + if (!OK_STATUS(i))
- + break;
- + if (i & DRQ_STAT)
- + goto ok_to_read;
- + } while (--retries > 0);
- + dump_status("read_intr", i);
- + bad_rw_intr();
- + hd_request();
- + return;
- +ok_to_read:
- + if (msect) {
- + if ((nsect = CURRENT->current_nr_sectors) > msect)
- + nsect = msect;
- + msect -= nsect;
- + } else
- + nsect = 1;
- + insw(HD_DATA,CURRENT->buffer,nsect<<8);
- + CURRENT->sector += nsect;
- + CURRENT->buffer += nsect<<9;
- + CURRENT->errors = 0;
- + i = (CURRENT->nr_sectors -= nsect);
- +
- +#ifdef DEBUG
- + printk("hd%c: read: sectors(%ld-%ld), remaining=%ld, buffer=0x%08lx\n",
- + dev+'a', CURRENT->sector, CURRENT->sector+nsect,
- + CURRENT->nr_sectors, (unsigned long) CURRENT->buffer+(nsect<<9));
- +#endif
- + if ((CURRENT->current_nr_sectors -= nsect) <= 0)
- + end_request(1);
- + if (i > 0) {
- + if (msect)
- + goto ok_to_read;
- + SET_INTR(&read_intr);
- + return;
- + }
- + (void) inb_p(HD_STATUS);
- +#if (HD_DELAY > 0)
- + last_req = read_timer();
- +#endif
- + if (CURRENT)
- + hd_request();
- + return;
- +}
- +
- +static inline void multwrite (unsigned int dev)
- +{
- + unsigned int mcount = mult_count[dev];
- +
- + while (mcount--) {
- + outsw(HD_DATA,WCURRENT.buffer,256);
- + if (!--WCURRENT.nr_sectors)
- + return;
- + WCURRENT.buffer += 512;
- + if (!--WCURRENT.current_nr_sectors) {
- + WCURRENT.bh = WCURRENT.bh->b_reqnext;
- + if (WCURRENT.bh == NULL)
- + panic("buffer list corrupted\n");
- + WCURRENT.current_nr_sectors = WCURRENT.bh->b_size>>9;
- + WCURRENT.buffer = WCURRENT.bh->b_data;
- + }
- + }
- +}
- +
- +static void multwrite_intr(void)
- +{
- + int i;
- + unsigned int dev = DEVICE_NR(WCURRENT.rq_dev);
- +
- + if (unmask_intr[dev])
- + sti();
- + if (OK_STATUS(i=inb_p(HD_STATUS))) {
- + if (i & DRQ_STAT) {
- + if (WCURRENT.nr_sectors) {
- + multwrite(dev);
- + SET_INTR(&multwrite_intr);
- + return;
- + }
- + } else {
- + if (!WCURRENT.nr_sectors) { /* all done? */
- + for (i = CURRENT->nr_sectors; i > 0;){
- + i -= CURRENT->current_nr_sectors;
- + end_request(1);
- + }
- +#if (HD_DELAY > 0)
- + last_req = read_timer();
- +#endif
- + if (CURRENT)
- + hd_request();
- + return;
- + }
- + }
- + }
- + dump_status("multwrite_intr", i);
- + bad_rw_intr();
- + hd_request();
- +}
- +
- +static void write_intr(void)
- +{
- + int i;
- + int retries = 100000;
- +
- + if (unmask_intr[DEVICE_NR(WCURRENT.rq_dev)])
- + sti();
- + do {
- + i = (unsigned) inb_p(HD_STATUS);
- + if (i & BUSY_STAT)
- + continue;
- + if (!OK_STATUS(i))
- + break;
- + if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT))
- + goto ok_to_write;
- + } while (--retries > 0);
- + dump_status("write_intr", i);
- + bad_rw_intr();
- + hd_request();
- + return;
- +ok_to_write:
- + CURRENT->sector++;
- + i = --CURRENT->nr_sectors;
- + --CURRENT->current_nr_sectors;
- + CURRENT->buffer += 512;
- + if (!i || (CURRENT->bh && !SUBSECTOR(i)))
- + end_request(1);
- + if (i > 0) {
- + SET_INTR(&write_intr);
- + outsw(HD_DATA,CURRENT->buffer,256);
- + sti();
- + } else {
- +#if (HD_DELAY > 0)
- + last_req = read_timer();
- +#endif
- + hd_request();
- + }
- + return;
- +}
- +
- +static void recal_intr(void)
- +{
- + check_status();
- +#if (HD_DELAY > 0)
- + last_req = read_timer();
- +#endif
- + hd_request();
- +}
- +
- +/*
- + * This is another of the error-routines I don't know what to do with. The
- + * best idea seems to just set reset, and start all over again.
- + */
- +static void hd_times_out(void)
- +{
- + unsigned int dev;
- +
- + DEVICE_INTR = NULL;
- + if (!CURRENT)
- + return;
- + disable_irq(HD_IRQ);
- + sti();
- + reset = 1;
- + dev = DEVICE_NR(CURRENT->rq_dev);
- + printk("hd%c: timeout\n", dev+'a');
- + if (++CURRENT->errors >= MAX_ERRORS) {
- +#ifdef DEBUG
- + printk("hd%c: too many errors\n", dev+'a');
- +#endif
- + end_request(0);
- + }
- + cli();
- + hd_request();
- + enable_irq(HD_IRQ);
- +}
- +
- +int do_special_op (unsigned int dev)
- +{
- + if (recalibrate[dev]) {
- + recalibrate[dev] = 0;
- + hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
- + return reset;
- + }
- + if (!identified[dev]) {
- + identified[dev] = 1;
- + unmask_intr[dev] = DEFAULT_UNMASK_INTR;
- + mult_req[dev] = DEFAULT_MULT_COUNT;
- + hd_out(dev,0,0,0,0,WIN_IDENTIFY,&identify_intr);
- + return reset;
- + }
- + if (mult_req[dev] != mult_count[dev]) {
- + hd_out(dev,mult_req[dev],0,0,0,WIN_SETMULT,&set_multmode_intr);
- + return reset;
- + }
- + if (hd_info[dev].head > 16) {
- + printk ("hd%c: cannot handle device with more than 16 heads - giving up\n", dev+'a');
- + end_request(0);
- + }
- + special_op[dev] = 0;
- + return 1;
- +}
- +
- +/*
- + * The driver enables interrupts as much as possible. In order to do this,
- + * (a) the device-interrupt is disabled before entering hd_request(),
- + * and (b) the timeout-interrupt is disabled before the sti().
- + *
- + * Interrupts are still masked (by default) whenever we are exchanging
- + * data/cmds with a drive, because some drives seem to have very poor
- + * tolerance for latency during I/O. For devices which don't suffer from
- + * that problem (most don't), the unmask_intr[] flag can be set to unmask
- + * other interrupts during data/cmd transfers (by defining DEFAULT_UNMASK_INTR
- + * to 1, or by using "hdparm -u1 /dev/hd?" from the shell).
- + */
- +static void hd_request(void)
- +{
- + unsigned int dev, block, nsect, sec, track, head, cyl;
- +
- + if (CURRENT && CURRENT->rq_status == RQ_INACTIVE) return;
- + if (DEVICE_INTR)
- + return;
- +repeat:
- + timer_active &= ~(1<<HD_TIMER);
- + sti();
- + INIT_REQUEST;
- + if (reset) {
- + cli();
- + reset_hd();
- + return;
- + }
- + dev = MINOR(CURRENT->rq_dev);
- + block = CURRENT->sector;
- + nsect = CURRENT->nr_sectors;
- + if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects || ((block+nsect) > hd[dev].nr_sects)) {
- +#ifdef DEBUG
- + if (dev >= (NR_HD<<6))
- + printk("hd: bad minor number: device=%s\n",
- + kdevname(CURRENT->rq_dev));
- + else
- + printk("hd%c: bad access: block=%d, count=%d\n",
- + (MINOR(CURRENT->rq_dev)>>6)+'a', block, nsect);
- +#endif
- + end_request(0);
- + goto repeat;
- + }
- + block += hd[dev].start_sect;
- + dev >>= 6;
- + if (special_op[dev]) {
- + if (do_special_op(dev))
- + goto repeat;
- + return;
- + }
- + sec = block % hd_info[dev].sect + 1;
- + track = block / hd_info[dev].sect;
- + head = track % hd_info[dev].head;
- + cyl = track / hd_info[dev].head;
- +#ifdef DEBUG
- + printk("hd%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx\n",
- + dev+'a', (CURRENT->cmd == READ)?"read":"writ",
- + cyl, head, sec, nsect, (unsigned long) CURRENT->buffer);
- +#endif
- + if (!unmask_intr[dev])
- + cli();
- + if (CURRENT->cmd == READ) {
- + unsigned int cmd = mult_count[dev] > 1 ? WIN_MULTREAD : WIN_READ;
- + hd_out(dev,nsect,sec,head,cyl,cmd,&read_intr);
- + if (reset)
- + goto repeat;
- + return;
- + }
- + if (CURRENT->cmd == WRITE) {
- +printk ("hd%c: write disabled\n", (MINOR(CURRENT->rq_dev)>>6)+'a');
- +end_request (0);
- +goto repeat;
- + if (mult_count[dev])
- + hd_out(dev,nsect,sec,head,cyl,WIN_MULTWRITE,&multwrite_intr);
- + else
- + hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
- + if (reset)
- + goto repeat;
- + if (wait_DRQ()) {
- + bad_rw_intr();
- + goto repeat;
- + }
- + if (mult_count[dev]) {
- + WCURRENT = *CURRENT;
- + multwrite(dev);
- + } else
- + outsw(HD_DATA,CURRENT->buffer,256);
- + return;
- + }
- + panic("unknown hd-command");
- +}
- +
- +static void do_hd_request (void)
- +{
- + disable_irq(HD_IRQ);
- + hd_request();
- + enable_irq(HD_IRQ);
- +}
- +
- +static int hd_ioctl(struct inode * inode, struct file * file,
- + unsigned int cmd, unsigned long arg)
- +{
- + struct hd_geometry *loc = (struct hd_geometry *) arg;
- + int dev, err;
- + unsigned long flags;
- +
- + if ((!inode) || !(inode->i_rdev))
- + return -EINVAL;
- + dev = DEVICE_NR(inode->i_rdev);
- + if (dev >= NR_HD)
- + return -EINVAL;
- + switch (cmd) {
- + case HDIO_GETGEO:
- + if (!loc) return -EINVAL;
- + err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
- + if (err)
- + return err;
- + put_user(bios_info[dev].head,
- + (char *) &loc->heads);
- + put_user(bios_info[dev].sect,
- + (char *) &loc->sectors);
- + put_user(bios_info[dev].cyl,
- + (short *) &loc->cylinders);
- + put_user(hd[MINOR(inode->i_rdev)].start_sect,
- + (long *) &loc->start);
- + return 0;
- + case BLKRASET:
- + if(!suser()) return -EACCES;
- + if(arg > 0xff) return -EINVAL;
- + read_ahead[MAJOR(inode->i_rdev)] = arg;
- + return 0;
- + case BLKRAGET:
- + if (!arg) return -EINVAL;
- + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
- + if (err)
- + return err;
- + put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
- + return 0;
- + case BLKGETSIZE: /* Return device size */
- + if (!arg) return -EINVAL;
- + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
- + if (err)
- + return err;
- + put_user(hd[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
- + return 0;
- + case BLKFLSBUF:
- + if(!suser()) return -EACCES;
- + fsync_dev(inode->i_rdev);
- + invalidate_buffers(inode->i_rdev);
- + return 0;
- +
- + case BLKRRPART: /* Re-read partition tables */
- + return revalidate_hddisk(inode->i_rdev, 1);
- +
- + case HDIO_SET_UNMASKINTR:
- + if (!suser()) return -EACCES;
- + if ((arg > 1) || (MINOR(inode->i_rdev) & 0x3F))
- + return -EINVAL;
- + unmask_intr[dev] = arg;
- + return 0;
- +
- + case HDIO_GET_UNMASKINTR:
- + if (!arg) return -EINVAL;
- + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
- + if (err)
- + return err;
- + put_user(unmask_intr[dev], (long *) arg);
- + return 0;
- +
- + case HDIO_GET_MULTCOUNT:
- + if (!arg) return -EINVAL;
- + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
- + if (err)
- + return err;
- + put_user(mult_count[dev], (long *) arg);
- + return 0;
- +
- + case HDIO_SET_MULTCOUNT:
- + if (!suser()) return -EACCES;
- + if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;
- + save_flags(flags);
- + cli(); /* a prior request might still be in progress */
- + if (arg > max_mult[dev])
- + err = -EINVAL; /* out of range for device */
- + else if (mult_req[dev] != mult_count[dev]) {
- + special_op[dev] = 1;
- + err = -EBUSY; /* busy, try again */
- + } else {
- + mult_req[dev] = arg;
- + special_op[dev] = 1;
- + err = 0;
- + }
- + restore_flags(flags);
- + return err;
- +
- + case HDIO_GET_IDENTITY:
- + if (!arg) return -EINVAL;
- + if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;
- + if (hd_ident_info[dev] == NULL) return -ENOMSG;
- + err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(struct hd_driveid));
- + if (err)
- + return err;
- + memcpy_tofs((char *)arg, (char *) hd_ident_info[dev], sizeof(struct hd_driveid));
- + return 0;
- +
- + RO_IOCTLS(inode->i_rdev,arg);
- + default:
- + return -EINVAL;
- + }
- +}
- +
- +static int hd_open(struct inode * inode, struct file * filp)
- +{
- + int target;
- + target = DEVICE_NR(inode->i_rdev);
- +
- + if (target >= NR_HD)
- + return -ENODEV;
- + while (busy[target])
- + sleep_on(&busy_wait);
- + access_count[target]++;
- + return 0;
- +}
- +
- +/*
- + * Releasing a block device means we sync() it, so that it can safely
- + * be forgotten about...
- + */
- +static void hd_release(struct inode * inode, struct file * file)
- +{
- + int target;
- + sync_dev(inode->i_rdev);
- +
- + target = DEVICE_NR(inode->i_rdev);
- + access_count[target]--;
- +
- +}
- +
- +static void hd_geninit(struct gendisk *);
- +
- +static struct gendisk hd_gendisk = {
- + MAJOR_NR, /* Major number */
- + "hd", /* Major name */
- + 6, /* Bits to shift to get real from partition */
- + 1 << 6, /* Number of partitions per real */
- + MAX_HD, /* maximum number of real */
- + hd_geninit, /* init function */
- + hd, /* hd struct */
- + hd_sizes, /* block sizes */
- + 0, /* number */
- + (void *) bios_info, /* internal */
- + NULL /* next */
- +};
- +
- +static void hd_interrupt(int irq, struct pt_regs *regs)
- +{
- + void (*handler)(void) = DEVICE_INTR;
- +
- + DEVICE_INTR = NULL;
- + timer_active &= ~(1<<HD_TIMER);
- + if (!handler)
- + handler = unexpected_hd_interrupt;
- + handler();
- + sti();
- +}
- +
- +/*
- + * Since we find out the physical drive geometry, we don't touch that.
- + * We only alter the logical disk geometry that is passed to user programs.
- + * [as per PC Linux].
- + */
- +void hd_set_geometry (kdev_t dev, unsigned char secspertrack, unsigned char heads,
- + unsigned long discsize, unsigned int secsize)
- +{
- + int minor = MINOR(dev);
- + int drv = minor >> 6;
- +
- + if (bios_info[drv].cyl == 1) {
- + bios_info[drv].cyl = discsize / (secspertrack * heads * secsize);
- + bios_info[drv].head = heads;
- + bios_info[drv].wpcom = -1;
- + bios_info[drv].ctl = 8;
- + bios_info[drv].lzone = bios_info[drv].cyl - 1;
- + bios_info[drv].sect = secspertrack;
- + }
- + hd[minor].start_sect = 0;
- + hd[minor].nr_sects = discsize / secsize;
- +}
- +
- +/*
- + * This is the harddisk IRQ description. The SA_INTERRUPT in sa_flags
- + * means we run the IRQ-handler with interrupts disabled: this is bad for
- + * interrupt latency, but anything else has led to problems on some
- + * machines...
- + *
- + * We enable interrupts in some of the routines after making sure it's
- + * safe.
- + */
- +static void hd_geninit(struct gendisk *dev)
- +{
- + int i;
- +
- + if (!NR_HD) {
- + int drive;
- + extern int number_ide_drives;
- + /*
- + * Default settings
- + *
- + * If we don't know anything about the drive, then set it
- + * so that we have enough to read the boot sector of the
- + * ADFS drive. This means that you *MUST* specify the
- + * drive parameters of *all* drives if you have one IDE
- + * drive that is not ADFS formatted.
- + */
- + for (drive=0 ; drive<2 ; drive++) {
- + bios_info[drive].cyl = hd_info[drive].cyl = 1;
- + bios_info[drive].head = hd_info[drive].head = 1;
- + bios_info[drive].wpcom = hd_info[drive].wpcom = -1;
- + bios_info[drive].ctl = hd_info[drive].ctl = 8;
- + bios_info[drive].lzone = hd_info[drive].lzone = 1;
- + bios_info[drive].sect = hd_info[drive].sect = 17;
- + }
- +
- + /*
- + * We only set this to the one that the host OS gave us
- + * if the user has not defined any types.
- + */
- + NR_HD = number_ide_drives;
- + }
- +
- + i = NR_HD;
- + while (i-- > 0) {
- + /*
- + * The newer E-IDE BIOSs handle drives larger than 1024
- + * cylinders by increasing the number of logical heads
- + * to keep the number of logical cylinders below the
- + * sacred INT13 limit of 1024 (10 bits). If that is
- + * what's happening here, we'll find out and correct
- + * it later when "identifying" the drive.
- + */
- + hd[i<<6].nr_sects = bios_info[i].head *
- + bios_info[i].sect * bios_info[i].cyl;
- + hd_ident_info[i] = (struct hd_driveid *) kmalloc(512,GFP_KERNEL);
- + special_op[i] = 1;
- + }
- + if (NR_HD) {
- + if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd")) {
- + printk("hd: unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
- + NR_HD = 0;
- + } else {
- + request_region(HD_DATA, 8, "hd");
- + request_region(HD_CMD, 1, "hd(cmd)");
- + }
- + }
- + hd_gendisk.nr_real = NR_HD;
- +
- + for (i = 0; i < (MAX_HD << 6); i++) {
- + hd_blocksizes[i] = 1024;
- + hd_hardsectsizes[i] = 512;
- + }
- + blksize_size[MAJOR_NR] = hd_blocksizes;
- + hardsect_size[MAJOR_NR] = hd_hardsectsizes;
- +}
- +
- +static struct file_operations hd_fops = {
- + NULL, /* lseek - default */
- + block_read, /* read - general block-dev read */
- + block_write, /* write - general block-dev write */
- + NULL, /* readdir - bad */
- + NULL, /* select */
- + hd_ioctl, /* ioctl */
- + NULL, /* mmap */
- + hd_open, /* open */
- + hd_release, /* release */
- + block_fsync /* fsync */
- +};
- +
- +int hd_init(void)
- +{
- + if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
- + printk("hd: unable to get major %d for harddisk\n",MAJOR_NR);
- + return -1;
- + }
- + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */
- + hd_gendisk.next = gendisk_head;
- + gendisk_head = &hd_gendisk;
- + timer_table[HD_TIMER].fn = hd_times_out;
- + return 0;
- +}
- +
- +#define DEVICE_BUSY busy[target]
- +#define USAGE access_count[target]
- +#define CAPACITY (bios_info[target].head*bios_info[target].sect*bios_info[target].cyl)
- +/* We assume that the the bios parameters do not change, so the disk capacity
- + will not change */
- +#undef MAYBE_REINIT
- +#define GENDISK_STRUCT hd_gendisk
- +
- +/*
- + * This routine is called to flush all partitions and partition tables
- + * for a changed scsi disk, and then re-read the new partition table.
- + * If we are revalidating a disk because of a media change, then we
- + * enter with usage == 0. If we are using an ioctl, we automatically have
- + * usage == 1 (we need an open channel to use an ioctl :-), so this
- + * is our limit.
- + */
- +static int revalidate_hddisk(kdev_t dev, int maxusage)
- +{
- + int target;
- + struct gendisk * gdev;
- + int max_p;
- + int start;
- + int i;
- + long flags;
- +
- + target = DEVICE_NR(dev);
- + gdev = &GENDISK_STRUCT;
- +
- + save_flags_cli (flags);
- + if (DEVICE_BUSY || USAGE > maxusage) {
- + restore_flags(flags);
- + return -EBUSY;
- + };
- + DEVICE_BUSY = 1;
- + restore_flags(flags);
- +
- + max_p = gdev->max_p;
- + start = target << gdev->minor_shift;
- +
- + for (i=max_p - 1; i >=0 ; i--) {
- + int minor = start + i;
- + kdev_t devi = MKDEV(MAJOR_NR, minor);
- + sync_dev(devi);
- + invalidate_inodes(devi);
- + invalidate_buffers(devi);
- + gdev->part[minor].start_sect = 0;
- + gdev->part[minor].nr_sects = 0;
- + };
- +
- +#ifdef MAYBE_REINIT
- + MAYBE_REINIT;
- +#endif
- +
- + gdev->part[start].nr_sects = CAPACITY;
- + resetup_one_dev(gdev, target);
- +
- + DEVICE_BUSY = 0;
- + wake_up(&busy_wait);
- + return 0;
- +}
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/ide-ics.c linux/arch/arm/drivers/block/ide-ics.c
- --- linux.orig/arch/arm/drivers/block/ide-ics.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/ide-ics.c Sun Jun 16 19:37:45 1996
- @@ -0,0 +1,88 @@
- +/*
- + * linux/arch/arm/drivers/block/ide-ics.c
- + *
- + * Copyright (c) 1996 Russell King.
- + *
- + * Changelog:
- + * 08-06-1996 RMK Created
- + */
- +
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#endif
- +#include <linux/errno.h>
- +
- +#include <asm/ecard.h>
- +
- +static const int icside_prods[] = {};
- +static const int icside_manus[] = {};
- +
- +#ifndef MODULE
- +
- +int icside_init (void)
- +{
- + struct expansion_card *ec;
- + unsigned long port;
- + int i;
- +
- + while (1) {
- + if ((ec = ecard_find (0, sizeof (icside_prods) / sizeof (int), icside_prods, icside_manus))
- + == NULL)
- + break;
- +
- + port = ((unsigned long)ecard_address (ec->slot_no, ECARD_MEMC, 0)) >> 2;
- +
- + ecard_claim (ec);
- +
- + i = ide_register_port (port, ec->irq, 16);
- + if (i < 0)
- + return i;
- + }
- + return 0;
- +}
- +
- +#else
- +static struct expansion_card *ec[MAX_ECARDS];
- +
- +int init_module (void)
- +{
- + int i, j = 0;
- +
- + for (i = 0; i < MAX_ECARDS; i++)
- + ec[i] = NULL;
- +
- + while (1) {
- + unsigned long port;
- + if ((ec[j] = ecard_find (0, sizeof (icside_prods) / sizeof (int), icside_prods, icside_manus))
- + == NULL)
- + break;
- +
- + port = ((unsigned long)ecard_address (ec[j]->slot_no, ECARD_MEMC, 0)) >> 2;
- +
- + ecard_claim (ec[j]);
- +
- + i = ide_register_port (port, ec[j]->irq, 16);
- + if (i < 0)
- + return i;
- + j ++;
- + }
- + return 0;
- +}
- +
- +void cleanup_module (void)
- +{
- + int i;
- +
- + for (i = 0; i < MAX_ECARDS; i++)
- + if (ec[i]) {
- + unsigned long port;
- + port = ((unsigned long)ecard_address (ec[i]->slot_no, ECARD_MEMC, 0)) >> 2;
- +
- + ide_unregister_port (port, ec[i]->irq, 16);
- + ecard_release (ec[i]);
- + ec[i] = NULL;
- + }
- +}
- +#endif
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/ide.c linux/arch/arm/drivers/block/ide.c
- --- linux.orig/arch/arm/drivers/block/ide.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/ide.c Sat Aug 17 23:23:26 1996
- @@ -0,0 +1,3384 @@
- +/*
- + * linux/drivers/block/ide.c Version 5.51 Aug 10, 1996
- + *
- + * Copyright (C) 1994-1996 Linus Torvalds & authors (see below)
- + */
- +#define _IDE_C /* needed by <linux/blk.h> */
- +
- +/*
- + * Maintained by Mark Lord <mlord@pobox.com>
- + * and Gadi Oxman <gadio@netvision.net.il>
- + *
- + * This is the multiple IDE interface driver, as evolved from hd.c.
- + * It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
- + * There can be up to two drives per interface, as per the ATA-2 spec.
- + *
- + * Primary: ide0, port 0x1f0; major=3; hda is minor=0; hdb is minor=64
- + * Secondary: ide1, port 0x170; major=22; hdc is minor=0; hdd is minor=64
- + * Tertiary: ide2, port 0x???; major=33; hde is minor=0; hdf is minor=64
- + * Quaternary: ide3, port 0x???; major=34; hdg is minor=0; hdh is minor=64
- + *
- + * It is easy to extend ide.c to handle more than four interfaces:
- + *
- + * Change the MAX_HWIFS constant in ide.h.
- + *
- + * Define some new major numbers (in major.h), and insert them into
- + * the ide_hwif_to_major table in ide.c.
- + *
- + * Fill in the extra values for the new interfaces into the two tables
- + * inside ide.c: default_io_base[] and default_irqs[].
- + *
- + * Create the new request handlers by cloning "do_ide3_request()"
- + * for each new interface, and add them to the switch statement
- + * in the ide_init() function in ide.c.
- + *
- + * Recompile, create the new /dev/ entries, and it will probably work.
- + *
- + * From hd.c:
- + * |
- + * | It traverses the request-list, using interrupts to jump between functions.
- + * | As nearly all functions can be called within interrupts, we may not sleep.
- + * | Special care is recommended. Have Fun!
- + * |
- + * | modified by Drew Eckhardt to check nr of hd's from the CMOS.
- + * |
- + * | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
- + * | in the early extended-partition checks and added DM partitions.
- + * |
- + * | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
- + * |
- + * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
- + * | and general streamlining by Mark Lord (mlord@pobox.com).
- + *
- + * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
- + *
- + * Mark Lord (mlord@pobox.com) (IDE Perf.Pkg)
- + * Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2")
- + * Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom)
- + *
- + * This was a rewrite of just about everything from hd.c, though some original
- + * code is still sprinkled about. Think of it as a major evolution, with
- + * inspiration from lots of linux users, esp. hamish@zot.apana.org.au
- + *
- + * Version 1.0 ALPHA initial code, primary i/f working okay
- + * Version 1.3 BETA dual i/f on shared irq tested & working!
- + * Version 1.4 BETA added auto probing for irq(s)
- + * Version 1.5 BETA added ALPHA (untested) support for IDE cd-roms,
- + * ...
- + * Version 3.5 correct the bios_cyl field if it's too small
- + * (linux 1.1.76) (to help fdisk with brain-dead BIOSs)
- + * Version 3.6 cosmetic corrections to comments and stuff
- + * (linux 1.1.77) reorganise probing code to make it understandable
- + * added halfway retry to probing for drive identification
- + * added "hdx=noprobe" command line option
- + * allow setting multmode even when identification fails
- + * Version 3.7 move set_geometry=1 from do_identify() to ide_init()
- + * increase DRQ_WAIT to eliminate nuisance messages
- + * wait for DRQ_STAT instead of DATA_READY during probing
- + * (courtesy of Gary Thomas gary@efland.UU.NET)
- + * Version 3.8 fixed byte-swapping for confused Mitsumi cdrom drives
- + * update of ide-cd.c from Scott, allows blocksize=1024
- + * cdrom probe fixes, inspired by jprang@uni-duisburg.de
- + * Version 3.9 don't use LBA if lba_capacity looks funny
- + * correct the drive capacity calculations
- + * fix probing for old Seagates without IDE_ALTSTATUS_REG
- + * fix byte-ordering for some NEC cdrom drives
- + * Version 3.10 disable multiple mode by default; was causing trouble
- + * Version 3.11 fix mis-identification of old WD disks as cdroms
- + * Version 3,12 simplify logic for selecting initial mult_count
- + * (fixes problems with buggy WD drives)
- + * Version 3.13 remove excess "multiple mode disabled" messages
- + * Version 3.14 fix ide_error() handling of BUSY_STAT
- + * fix byte-swapped cdrom strings (again.. arghh!)
- + * ignore INDEX bit when checking the ALTSTATUS reg
- + * Version 3.15 add SINGLE_THREADED flag for use with dual-CMD i/f
- + * ignore WRERR_STAT for non-write operations
- + * added vlb_sync support for DC-2000A & others,
- + * (incl. some Promise chips), courtesy of Frank Gockel
- + * Version 3.16 convert vlb_32bit and vlb_sync into runtime flags
- + * add ioctls to get/set VLB flags (HDIO_[SG]ET_CHIPSET)
- + * rename SINGLE_THREADED to SUPPORT_SERIALIZE,
- + * add boot flag to "serialize" operation for CMD i/f
- + * add optional support for DTC2278 interfaces,
- + * courtesy of andy@cercle.cts.com (Dyan Wile).
- + * add boot flag to enable "dtc2278" probe
- + * add probe to avoid EATA (SCSI) interfaces,
- + * courtesy of neuffer@goofy.zdv.uni-mainz.de.
- + * Version 4.00 tidy up verify_area() calls - heiko@colossus.escape.de
- + * add flag to ignore WRERR_STAT for some drives
- + * courtesy of David.H.West@um.cc.umich.edu
- + * assembly syntax tweak to vlb_sync
- + * removable drive support from scuba@cs.tu-berlin.de
- + * add transparent support for DiskManager-6.0x "Dynamic
- + * Disk Overlay" (DDO), most of this is in genhd.c
- + * eliminate "multiple mode turned off" message at boot
- + * Version 4.10 fix bug in ioctl for "hdparm -c3"
- + * fix DM6:DDO support -- now works with LILO, fdisk, ...
- + * don't treat some naughty WD drives as removable
- + * Version 4.11 updated DM6 support using info provided by OnTrack
- + * Version 5.00 major overhaul, multmode setting fixed, vlb_sync fixed
- + * added support for 3rd/4th/alternative IDE ports
- + * created ide.h; ide-cd.c now compiles separate from ide.c
- + * hopefully fixed infinite "unexpected_intr" from cdroms
- + * zillions of other changes and restructuring
- + * somehow reduced overall memory usage by several kB
- + * probably slowed things down slightly, but worth it
- + * Version 5.01 AT LAST!! Finally understood why "unexpected_intr"
- + * was happening at various times/places: whenever the
- + * ide-interface's ctl_port was used to "mask" the irq,
- + * it also would trigger an edge in the process of masking
- + * which would result in a self-inflicted interrupt!!
- + * (such a stupid way to build a hardware interrupt mask).
- + * This is now fixed (after a year of head-scratching).
- + * Version 5.02 got rid of need for {enable,disable}_irq_list()
- + * Version 5.03 tune-ups, comments, remove "busy wait" from drive resets
- + * removed PROBE_FOR_IRQS option -- no longer needed
- + * OOOPS! fixed "bad access" bug for 2nd drive on an i/f
- + * Version 5.04 changed "ira %d" to "irq %d" in DEBUG message
- + * added more comments, cleaned up unexpected_intr()
- + * OOOPS! fixed null pointer problem in ide reset code
- + * added autodetect for Triton chipset -- no effect yet
- + * Version 5.05 OOOPS! fixed bug in revalidate_disk()
- + * OOOPS! fixed bug in ide_do_request()
- + * added ATAPI reset sequence for cdroms
- + * Version 5.10 added Bus-Mastered DMA support for Triton Chipset
- + * some (mostly) cosmetic changes
- + * Version 5.11 added ht6560b support by malafoss@snakemail.hut.fi
- + * reworked PCI scanning code
- + * added automatic RZ1000 detection/support
- + * added automatic PCI CMD640 detection/support
- + * added option for VLB CMD640 support
- + * tweaked probe to find cdrom on hdb with disks on hda,hdc
- + * Version 5.12 some performance tuning
- + * added message to alert user to bad /dev/hd[cd] entries
- + * OOOPS! fixed bug in atapi reset
- + * driver now forces "serialize" again for all cmd640 chips
- + * noticed REALLY_SLOW_IO had no effect, moved it to ide.c
- + * made do_drive_cmd() into public ide_do_drive_cmd()
- + * Version 5.13 fixed typo ('B'), thanks to houston@boyd.geog.mcgill.ca
- + * fixed ht6560b support
- + * Version 5.13b (sss) fix problem in calling ide_cdrom_setup()
- + * don't bother invalidating nonexistent partitions
- + * Version 5.14 fixes to cmd640 support.. maybe it works now(?)
- + * added & tested full EZ-DRIVE support -- don't use LILO!
- + * don't enable 2nd CMD640 PCI port during init - conflict
- + * Version 5.15 bug fix in init_cmd640_vlb()
- + * bug fix in interrupt sharing code
- + * Version 5.16 ugh.. fix "serialize" support, broken in 5.15
- + * remove "Huh?" from cmd640 code
- + * added qd6580 interface speed select from Colten Edwards
- + * Version 5.17 kludge around bug in BIOS32 on Intel triton motherboards
- + * Version 5.18 new CMD640 code, moved to cmd640.c, #include'd for now
- + * new UMC8672 code, moved to umc8672.c, #include'd for now
- + * disallow turning on DMA when h/w not capable of DMA
- + * Version 5.19 fix potential infinite timeout on resets
- + * extend reset poll into a general purpose polling scheme
- + * add atapi tape drive support from Gadi Oxman
- + * simplify exit from _intr routines -- no IDE_DO_REQUEST
- + * Version 5.20 leave current rq on blkdev request list during I/O
- + * generalized ide_do_drive_cmd() for tape/cdrom driver use
- + * Version 5.21 fix nasty cdrom/tape bug (ide_preempt was messed up)
- + * Version 5.22 fix ide_xlate_1024() to work with/without drive->id
- + * Version 5.23 miscellaneous touch-ups
- + * Version 5.24 fix #if's for SUPPORT_CMD640
- + * Version 5.25 more touch-ups, fix cdrom resets, ...
- + * cmd640.c now configs/compiles separate from ide.c
- + * Version 5.26 keep_settings now maintains the using_dma flag
- + * fix [EZD] remap message to only output at boot time
- + * fix "bad /dev/ entry" message to say hdc, not hdc0
- + * fix ide_xlate_1024() to respect user specified CHS
- + * use CHS from partn table if it looks translated
- + * re-merged flags chipset,vlb_32bit,vlb_sync into io_32bit
- + * keep track of interface chipset type, when known
- + * add generic PIO mode "tuneproc" mechanism
- + * fix cmd640_vlb option
- + * fix ht6560b support (was completely broken)
- + * umc8672.c now configures/compiles separate from ide.c
- + * move dtc2278 support to dtc2278.c
- + * move ht6560b support to ht6560b.c
- + * move qd6580 support to qd6580.c
- + * add ali14xx support in ali14xx.c
- + * Version 5.27 add [no]autotune parameters to help cmd640
- + * move rz1000 support to rz1000.c
- + * Version 5.28 #include "ide_modes.h"
- + * fix disallow_unmask: now per-interface "no_unmask" bit
- + * force io_32bit to be the same on drive pairs of dtc2278
- + * improved IDE tape error handling, and tape DMA support
- + * bugfix in ide_do_drive_cmd() for cdroms + serialize
- + * Version 5.29 fixed non-IDE check for too many physical heads
- + * don't use LBA if capacity is smaller than CHS
- + * Version 5.30 remove real_devices kludge, formerly used by genhd.c
- + * Version 5.32 change "KB" to "kB"
- + * fix serialize (was broken in kernel 1.3.72)
- + * add support for "hdparm -I"
- + * use common code for disk/tape/cdrom IDE_DRIVE_CMDs
- + * add support for Promise DC4030VL caching card
- + * improved serialize support
- + * put partition check back into alphabetical order
- + * add config option for PCMCIA baggage
- + * try to make PCMCIA support safer to use
- + * improve security on ioctls(): all are suser() only
- + * Version 5.33 improve handling of HDIO_DRIVE_CMDs that read data
- + * Version 5.34 fix irq-sharing problem from 5.33
- + * fix cdrom ioctl problem from 5.33
- + * Version 5.35 cosmetic changes
- + * fix cli() problem in try_to_identify()
- + * Version 5.36 fixes to optional PCMCIA support
- + * Version 5.37 don't use DMA when "noautotune" is specified
- + * Version 5.37a (go) fix shared irq probing (was broken in kernel 1.3.72)
- + * call unplug_device() from ide_do_drive_cmd()
- + * Version 5.38 add "hdx=none" option, courtesy of Joel Maslak
- + * mask drive irq after use, if sharing with another hwif
- + * add code to help debug weird cmd640 problems
- + * Version 5.39 fix horrible error in earlier irq sharing "fix"
- + * Version 5.40 fix serialization -- was broken in 5.39
- + * help sharing by masking device irq after probing
- + * Version 5.41 more fixes to irq sharing/serialize detection
- + * disable io_32bit by default on drive reset
- + * Version 5.42 simplify irq-masking after probe
- + * fix NULL pointer deref in save_match()
- + * Version 5.43 Ugh.. unexpected_intr is back: try to exterminate it
- + * Version 5.44 Fix for "irq probe failed" on cmd640
- + * change path on message regarding MAKEDEV.ide
- + * add a throttle to the unexpected_intr() messages
- + * Version 5.45 fix ugly parameter parsing bugs (thanks Derek)
- + * include Gadi's magic fix for cmd640 unexpected_intr
- + * include mc68000 patches from Geert Uytterhoeven
- + * add Gadi's fix for PCMCIA cdroms
- + * Version 5.46 remove the mc68000 #ifdefs for 2.0.x
- + * Version 5.47 fix set_tune race condition
- + * fix bug in earlier PCMCIA cdrom update
- + * Version 5.48 if def'd, invoke CMD640_DUMP_REGS when irq probe fails
- + * lengthen the do_reset1() pulse, for laptops
- + * add idebus=xx parameter for cmd640 and ali chipsets
- + * no_unmask flag now per-drive instead of per-hwif
- + * fix tune_req so that it gets done immediately
- + * fix missing restore_flags() in ide_ioctl
- + * prevent use of io_32bit on cmd640 with no prefetch
- + * Version 5.49 fix minor quirks in probing routines
- + * Version 5.50 allow values as small as 20 for idebus=
- + * Version 5.51 force non io_32bit in drive_cmd_intr()
- + * change delay_10ms() to delay_50ms() to fix problems
- + *
- + *--------------------------------------------------------------------------------
- + * Ported to ARM architecture by Russell King (rmk92@ecs.soton.ac.uk)
- + *--------------------------------------------------------------------------------
- + * Some additional driver compile-time options are in ide.h
- + *
- + * To do, in likely order of completion:
- + * - modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f
- + */
- +
- +#undef REALLY_SLOW_IO /* most systems can safely undef this */
- +
- +#include <linux/config.h>
- +#include <linux/types.h>
- +#include <linux/string.h>
- +#include <linux/kernel.h>
- +#include <linux/delay.h>
- +#include <linux/timer.h>
- +#include <linux/mm.h>
- +#include <linux/ioport.h>
- +#include <linux/interrupt.h>
- +#include <linux/major.h>
- +#include <linux/blkdev.h>
- +#include <linux/errno.h>
- +#include <linux/hdreg.h>
- +#include <linux/genhd.h>
- +#include <linux/malloc.h>
- +
- +#include <asm/byteorder.h>
- +#include <asm/irq.h>
- +#include <asm/segment.h>
- +#include <asm/io.h>
- +
- +#ifdef CONFIG_PCI
- +#include <linux/bios32.h>
- +#include <linux/pci.h>
- +#endif /* CONFIG_PCI */
- +
- +#include "ide.h"
- +#include "ide_modes.h"
- +
- +#define IS_PROMISE_DRIVE (0) /* auto-NULLs out Promise code */
- +
- +static const byte ide_hwif_to_major[MAX_HWIFS] = {IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR};
- +static const unsigned long default_io_base[MAX_HWIFS] = {0x1f0};
- +static const byte default_irqs[MAX_HWIFS] = {11};
- +static int idebus_parameter; /* holds the "idebus=" parameter */
- +static int system_bus_speed; /* holds what we think is VESA/PCI bus speed */
- +
- +/*
- + * This is declared extern in ide.h, for access by other IDE modules:
- + */
- +ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */
- +
- +#if (DISK_RECOVERY_TIME > 0)
- +#error DISK_RECOVERY_TIME is not supported on the ARM.
- +/*
- + * For really screwy hardware (hey, at least it *can* be used with Linux)
- + * we can enforce a minimum delay time between successive operations.
- + */
- +static unsigned long read_timer(void)
- +{
- + unsigned long t, flags;
- + int i;
- +
- + save_flags(flags);
- + cli();
- + t = jiffies * 11932;
- + outb_p(0, 0x43);
- + i = inb_p(0x40);
- + i |= inb(0x40) << 8;
- + restore_flags(flags);
- + return (t - i);
- +}
- +
- +static void set_recovery_timer (ide_hwif_t *hwif)
- +{
- + hwif->last_time = read_timer();
- +}
- +#define SET_RECOVERY_TIMER(drive) set_recovery_timer (drive)
- +
- +#else
- +
- +#define SET_RECOVERY_TIMER(drive)
- +
- +#endif /* DISK_RECOVERY_TIME */
- +
- +
- +/*
- + * Do not even *think* about calling this!
- + */
- +static void init_hwif_data (unsigned int index)
- +{
- + byte *p;
- + unsigned int unit;
- + ide_hwif_t *hwif = &ide_hwifs[index];
- +
- + /* bulk initialize hwif & drive info with zeros */
- + p = ((byte *) hwif) + sizeof(ide_hwif_t);
- + do {
- + *--p = 0;
- + } while (p > (byte *) hwif);
- +
- + /* fill in any non-zero initial values */
- + hwif->index = index;
- + hwif->noprobe = (index > 1);
- + hwif->io.io_base = default_io_base[index];
- + hwif->io.ctl_port = hwif->io.io_base ? hwif->io.io_base+0x206 : 0x000;
- + hwif->io.io_size = 1;
- +#ifdef CONFIG_BLK_DEV_HD
- + if (hwif->io_base == HD_DATA)
- + hwif->noprobe = 1; /* may be overridden by ide_setup() */
- +#endif /* CONFIG_BLK_DEV_HD */
- + hwif->major = ide_hwif_to_major[index];
- + hwif->name[0] = 'i';
- + hwif->name[1] = 'd';
- + hwif->name[2] = 'e';
- + hwif->name[3] = '0' + index;
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- + hwif->tape_drive = NULL;
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- + for (unit = 0; unit < MAX_DRIVES; ++unit) {
- + ide_drive_t *drive = &hwif->drives[unit];
- +
- + drive->select.all = (unit<<4)|0xa0;
- + drive->hwif = hwif;
- + drive->ctl = 0x08;
- + drive->ready_stat = READY_STAT;
- + drive->bad_wstat = BAD_W_STAT;
- + drive->special.b.recalibrate = 1;
- + drive->special.b.set_geometry = 1;
- + drive->name[0] = 'h';
- + drive->name[1] = 'd';
- + drive->name[2] = 'a' + (index * MAX_DRIVES) + unit;
- + }
- +}
- +
- +/*
- + * init_ide_data() sets reasonable default values into all fields
- + * of all instances of the hwifs and drives, but only on the first call.
- + * Subsequent calls have no effect (they don't wipe out anything).
- + *
- + * This routine is normally called at driver initialization time,
- + * but may also be called MUCH earlier during kernel "command-line"
- + * parameter processing. As such, we cannot depend on any other parts
- + * of the kernel (such as memory allocation) to be functioning yet.
- + *
- + * This is too bad, as otherwise we could dynamically allocate the
- + * ide_drive_t structs as needed, rather than always consuming memory
- + * for the max possible number (MAX_HWIFS * MAX_DRIVES) of them.
- + */
- +#define MAGIC_COOKIE 0x12345678
- +static void init_ide_data (void)
- +{
- + unsigned int index;
- + static unsigned long magic_cookie = MAGIC_COOKIE;
- +
- + if (magic_cookie != MAGIC_COOKIE)
- + return; /* already initialized */
- + magic_cookie = 0;
- +
- + for (index = 0; index < MAX_HWIFS; ++index)
- + init_hwif_data(index);
- +
- + idebus_parameter = 0;
- + system_bus_speed = 0;
- +}
- +
- +/*
- + * ide_system_bus_speed() returns what we think is the system VESA/PCI
- + * bus speed (in Mhz). This is used for calculating interface PIO timings.
- + * The default is 40 for known PCI systems, 50 otherwise.
- + * The "idebus=xx" parameter can be used to override this value.
- + * The actual value to be used is computed/displayed the first time through.
- + */
- +int ide_system_bus_speed (void)
- +{
- + if (!system_bus_speed) {
- + if (idebus_parameter)
- + system_bus_speed = idebus_parameter; /* user supplied value */
- +#ifdef CONFIG_PCI
- + else if (pcibios_present())
- + system_bus_speed = 40; /* safe default value for PCI */
- +#endif /* CONFIG_PCI */
- + else
- + system_bus_speed = 50; /* safe default value for VESA and PCI */
- + printk("ide: Assuming %dMhz system bus speed for PIO modes; override with idebus=xx\n", system_bus_speed);
- + }
- + return system_bus_speed;
- +}
- +
- +/*
- + * This is used for most PIO data transfers *from* the IDE interface
- + */
- +void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
- +{
- + unsigned long io_base = HWIF(drive)->io.io_base;
- + unsigned long data_reg = io_base+HWIF(drive)->io.io_size*IDE_DATA_OFFSET;
- +
- + insw(data_reg, buffer, wcount<<1);
- +}
- +
- +/*
- + * This is used for most PIO data transfers *to* the IDE interface
- + */
- +void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
- +{
- + unsigned long io_base = HWIF(drive)->io.io_base;
- + unsigned long data_reg = io_base+HWIF(drive)->io.io_size*IDE_DATA_OFFSET;
- +
- + outsw(data_reg, buffer, wcount<<1);
- +}
- +
- +/*
- + * This should get invoked any time we exit the driver to
- + * wait for an interrupt response from a drive. handler() points
- + * at the appropriate code to handle the next interrupt, and a
- + * timer is started to prevent us from waiting forever in case
- + * something goes wrong (see the timer_expiry() handler later on).
- + */
- +void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout)
- +{
- + ide_hwgroup_t *hwgroup = HWGROUP(drive);
- +#ifdef DEBUG
- + if (hwgroup->handler != NULL) {
- + printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
- + drive->name, hwgroup->handler, handler);
- + }
- +#endif
- + hwgroup->handler = handler;
- + hwgroup->timer.expires = jiffies + timeout;
- + add_timer(&(hwgroup->timer));
- +}
- +
- +/*
- + * lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity"
- + * value for this drive (from its reported identification information).
- + *
- + * Returns: 1 if lba_capacity looks sensible
- + * 0 otherwise
- + */
- +static int lba_capacity_is_ok (struct hd_driveid *id)
- +{
- + unsigned long lba_sects = id->lba_capacity;
- + unsigned long chs_sects = id->cyls * id->heads * id->sectors;
- + unsigned long _10_percent = chs_sects / 10;
- +
- + /* perform a rough sanity check on lba_sects: within 10% is "okay" */
- + if ((lba_sects - chs_sects) < _10_percent)
- + return 1; /* lba_capacity is good */
- +
- + /* some drives have the word order reversed */
- + lba_sects = (lba_sects << 16) | (lba_sects >> 16);
- + if ((lba_sects - chs_sects) < _10_percent) {
- + id->lba_capacity = lba_sects; /* fix it */
- + return 1; /* lba_capacity is (now) good */
- + }
- + return 0; /* lba_capacity value is bad */
- +}
- +
- +/*
- + * current_capacity() returns the capacity (in sectors) of a drive
- + * according to its current geometry/LBA settings.
- + */
- +static unsigned long current_capacity (ide_drive_t *drive)
- +{
- + struct hd_driveid *id = drive->id;
- + unsigned long capacity = drive->cyl * drive->head * drive->sect;
- +
- + if (!drive->present)
- + return 0;
- + if (drive->media != ide_disk)
- + return 0x7fffffff; /* cdrom or tape */
- + drive->select.b.lba = 0;
- + /* Determine capacity, and use LBA if the drive properly supports it */
- + if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
- + if (id->lba_capacity >= capacity) {
- + capacity = id->lba_capacity;
- + drive->select.b.lba = 1;
- + }
- + }
- + return (capacity - drive->sect0);
- +}
- +
- +/*
- + * ide_geninit() is called exactly *once* for each major, from genhd.c,
- + * at the beginning of the initial partition check for the drives.
- + */
- +static void ide_geninit (struct gendisk *gd)
- +{
- + unsigned int unit;
- + ide_hwif_t *hwif = gd->real_devices;
- +
- + for (unit = 0; unit < gd->nr_real; ++unit) {
- + ide_drive_t *drive = &hwif->drives[unit];
- +#ifdef CONFIG_BLK_DEV_IDECD
- + if (drive->present && drive->media == ide_cdrom)
- + ide_cdrom_setup(drive);
- +#endif /* CONFIG_BLK_DEV_IDECD */
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- + if (drive->present && drive->media == ide_tape)
- + idetape_setup(drive);
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- + drive->part[0].nr_sects = current_capacity(drive);
- + if (!drive->present || drive->media != ide_disk) {
- + drive->part[0].start_sect = -1; /* skip partition check */
- + }
- + }
- +}
- +
- +/*
- + * init_gendisk() (as opposed to ide_geninit) is called for each major device,
- + * after probing for drives, to allocate partition tables and other data
- + * structures needed for the routines in genhd.c. ide_geninit() gets called
- + * somewhat later, during the partition check.
- + */
- +static void init_gendisk (ide_hwif_t *hwif)
- +{
- + struct gendisk *gd, **gdp;
- + unsigned int unit, units, minors;
- + int *bs, *hs;
- +
- + /* figure out maximum drive number on the interface */
- + for (units = MAX_DRIVES; units > 0; --units) {
- + if (hwif->drives[units-1].present)
- + break;
- + }
- + minors = units * (1<<PARTN_BITS);
- + gd = kmalloc (sizeof(struct gendisk), GFP_KERNEL);
- + gd->sizes = kmalloc (minors * sizeof(int), GFP_KERNEL);
- + gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL);
- + bs = kmalloc (minors*sizeof(int), GFP_KERNEL);
- + hs = kmalloc (minors*sizeof(int), GFP_KERNEL);
- +
- + memset(gd->part, 0, minors * sizeof(struct hd_struct));
- +
- + /* cdroms and msdos f/s are examples of non-1024 blocksizes */
- + blksize_size[hwif->major] = bs;
- + hardsect_size[hwif->major] = hs;
- + for (unit = 0; unit < minors; ++unit) {
- + *bs++ = BLOCK_SIZE;
- + *hs++ = SECTOR_WORDS << 2;
- + }
- +
- + for (unit = 0; unit < units; ++unit)
- + hwif->drives[unit].part = &gd->part[unit << PARTN_BITS];
- +
- + gd->major = hwif->major; /* our major device number */
- + gd->major_name = IDE_MAJOR_NAME; /* treated special in genhd.c */
- + gd->minor_shift = PARTN_BITS; /* num bits for partitions */
- + gd->max_p = 1<<PARTN_BITS; /* 1 + max partitions / drive */
- + gd->max_nr = units; /* max num real drives */
- + gd->nr_real = units; /* current num real drives */
- + gd->init = ide_geninit; /* initialization function */
- + gd->real_devices= hwif; /* ptr to internal data */
- + gd->next = NULL; /* linked list of major devs */
- +
- + for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) ;
- + hwif->gd = *gdp = gd; /* link onto tail of list */
- +}
- +
- +static void do_reset1 (ide_drive_t *, int); /* needed below */
- +
- +#ifdef CONFIG_BLK_DEV_IDEATAPI
- +/*
- + * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
- + * during an atapi drive reset operation. If the drive has not yet responded,
- + * and we have not yet hit our maximum waiting time, then the timer is restarted
- + * for another 50ms.
- + */
- +static void atapi_reset_pollfunc (ide_drive_t *drive)
- +{
- + ide_hwgroup_t *hwgroup = HWGROUP(drive);
- + byte stat;
- +
- + OUT_BYTE (drive->select.all, IDE_SELECT_REG);
- + udelay (10);
- +
- + if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
- + printk("%s: ATAPI reset complete\n", drive->name);
- + } else {
- + if (jiffies < hwgroup->poll_timeout) {
- + ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
- + return; /* continue polling */
- + }
- + hwgroup->poll_timeout = 0; /* end of polling */
- + printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat);
- + do_reset1 (drive, 1); /* do it the old fashioned way */
- + }
- + hwgroup->poll_timeout = 0; /* done polling */
- +}
- +#endif /* CONFIG_BLK_DEV_IDEATAPI */
- +
- +/*
- + * reset_pollfunc() gets invoked to poll the interface for completion every 50ms
- + * during an ide reset operation. If the drives have not yet responded,
- + * and we have not yet hit our maximum waiting time, then the timer is restarted
- + * for another 50ms.
- + */
- +static void reset_pollfunc (ide_drive_t *drive)
- +{
- + ide_hwgroup_t *hwgroup = HWGROUP(drive);
- + ide_hwif_t *hwif = HWIF(drive);
- + byte tmp;
- +
- + if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
- + if (jiffies < hwgroup->poll_timeout) {
- + ide_set_handler (drive, &reset_pollfunc, HZ/20);
- + return; /* continue polling */
- + }
- + printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
- + } else {
- + printk("%s: reset: ", hwif->name);
- + if ((tmp = GET_ERR()) == 1)
- + printk("success\n");
- + else {
- +#if FANCY_STATUS_DUMPS
- + printk("master: ");
- + switch (tmp & 0x7f) {
- + case 1: printk("passed");
- + break;
- + case 2: printk("formatter device error");
- + break;
- + case 3: printk("sector buffer error");
- + break;
- + case 4: printk("ECC circuitry error");
- + break;
- + case 5: printk("controlling MPU error");
- + break;
- + default:printk("error (0x%02x?)", tmp);
- + }
- + if (tmp & 0x80)
- + printk("; slave: failed");
- + printk("\n");
- +#else
- + printk("failed\n");
- +#endif /* FANCY_STATUS_DUMPS */
- + }
- + }
- + hwgroup->poll_timeout = 0; /* done polling */
- +}
- +
- +/*
- + * do_reset1() attempts to recover a confused drive by resetting it.
- + * Unfortunately, resetting a disk drive actually resets all devices on
- + * the same interface, so it can really be thought of as resetting the
- + * interface rather than resetting the drive.
- + *
- + * ATAPI devices have their own reset mechanism which allows them to be
- + * individually reset without clobbering other devices on the same interface.
- + *
- + * Unfortunately, the IDE interface does not generate an interrupt to let
- + * us know when the reset operation has finished, so we must poll for this.
- + * Equally poor, though, is the fact that this may a very long time to complete,
- + * (up to 30 seconds worstcase). So, instead of busy-waiting here for it,
- + * we set a timer to poll at 50ms intervals.
- + */
- +static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
- +{
- + unsigned int unit;
- + unsigned long flags;
- + ide_hwif_t *hwif = HWIF(drive);
- + ide_hwgroup_t *hwgroup = HWGROUP(drive);
- +
- + save_flags(flags);
- + cli(); /* Why ? */
- +
- +#ifdef CONFIG_BLK_DEV_IDEATAPI
- + /* For an ATAPI device, first try an ATAPI SRST. */
- + if (drive->media != ide_disk) {
- + if (!do_not_try_atapi) {
- + if (!drive->keep_settings) {
- + drive->unmask = 0;
- + drive->io_32bit = 0;
- + }
- + OUT_BYTE (drive->select.all, IDE_SELECT_REG);
- + udelay (20);
- + OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
- + hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
- + ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
- + restore_flags (flags);
- + return;
- + }
- + }
- +#endif /* CONFIG_BLK_DEV_IDEATAPI */
- +
- + /*
- + * First, reset any device state data we were maintaining
- + * for any of the drives on this interface.
- + */
- + for (unit = 0; unit < MAX_DRIVES; ++unit) {
- + ide_drive_t *rdrive = &hwif->drives[unit];
- + rdrive->special.all = 0;
- + rdrive->special.b.set_geometry = 1;
- + rdrive->special.b.recalibrate = 1;
- + if (OK_TO_RESET_CONTROLLER)
- + rdrive->mult_count = 0;
- + if (!rdrive->keep_settings) {
- + rdrive->mult_req = 0;
- + rdrive->unmask = 0;
- + rdrive->io_32bit = 0;
- + if (rdrive->using_dma) {
- + rdrive->using_dma = 0;
- + printk("%s: disabled DMA\n", rdrive->name);
- + }
- + }
- + if (rdrive->mult_req != rdrive->mult_count)
- + rdrive->special.b.set_multmode = 1;
- + }
- +
- +#if OK_TO_RESET_CONTROLLER
- + /*
- + * Note that we also set nIEN while resetting the device,
- + * to mask unwanted interrupts from the interface during the reset.
- + * However, due to the design of PC hardware, this will cause an
- + * immediate interrupt due to the edge transition it produces.
- + * This single interrupt gives us a "fast poll" for drives that
- + * recover from reset very quickly, saving us the first 50ms wait time.
- + */
- + OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG); /* set SRST and nIEN */
- + udelay(10); /* more than enough time */
- + OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */
- + udelay(10); /* more than enough time */
- + hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
- + ide_set_handler (drive, &reset_pollfunc, HZ/20);
- +#endif /* OK_TO_RESET_CONTROLLER */
- +
- + restore_flags (flags);
- +}
- +
- +/*
- + * ide_do_reset() is the entry point to the drive/interface reset code.
- + */
- +void ide_do_reset (ide_drive_t *drive)
- +{
- + do_reset1 (drive, 0);
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- + if (drive->media == ide_tape)
- + drive->tape.reset_issued=1;
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- +}
- +
- +/*
- + * Clean up after success/failure of an explicit drive cmd
- + */
- +void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
- +{
- + unsigned long flags;
- + struct request *rq = HWGROUP(drive)->rq;
- +
- + if (rq->cmd == IDE_DRIVE_CMD) {
- + byte *args = (byte *) rq->buffer;
- + rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
- + if (args) {
- + args[0] = stat;
- + args[1] = err;
- + args[2] = IN_BYTE(IDE_NSECTOR_REG);
- + }
- + }
- + save_flags(flags);
- + cli();
- + blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
- + HWGROUP(drive)->rq = NULL;
- + rq->rq_status = RQ_INACTIVE;
- + if (rq->sem != NULL)
- + up(rq->sem);
- + restore_flags(flags);
- +}
- +
- +/*
- + * Error reporting, in human readable form (luxurious, but a memory hog).
- + */
- +byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat)
- +{
- + unsigned long flags;
- + byte err = 0;
- +
- + save_flags (flags);
- + sti();
- + printk("%s: %s: status=0x%02x", drive->name, msg, stat);
- +#if FANCY_STATUS_DUMPS
- + if (drive->media == ide_disk) {
- + printk(" { ");
- + if (stat & BUSY_STAT)
- + printk("Busy ");
- + else {
- + if (stat & READY_STAT) printk("DriveReady ");
- + if (stat & WRERR_STAT) printk("DeviceFault ");
- + if (stat & SEEK_STAT) printk("SeekComplete ");
- + if (stat & DRQ_STAT) printk("DataRequest ");
- + if (stat & ECC_STAT) printk("CorrectedError ");
- + if (stat & INDEX_STAT) printk("Index ");
- + if (stat & ERR_STAT) printk("Error ");
- + }
- + printk("}");
- + }
- +#endif /* FANCY_STATUS_DUMPS */
- + printk("\n");
- + if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
- + err = GET_ERR();
- + printk("%s: %s: error=0x%02x", drive->name, msg, err);
- +#if FANCY_STATUS_DUMPS
- + if (drive->media == ide_disk) {
- + printk(" { ");
- + if (err & BBD_ERR) printk("BadSector ");
- + if (err & ECC_ERR) printk("UncorrectableError ");
- + if (err & ID_ERR) printk("SectorIdNotFound ");
- + if (err & ABRT_ERR) printk("DriveStatusError ");
- + if (err & TRK0_ERR) printk("TrackZeroNotFound ");
- + if (err & MARK_ERR) printk("AddrMarkNotFound ");
- + printk("}");
- + if (err & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
- + byte cur = IN_BYTE(IDE_SELECT_REG);
- + if (cur & 0x40) { /* using LBA? */
- + printk(", LBAsect=%ld", (unsigned long)
- + ((cur&0xf)<<24)
- + |(IN_BYTE(IDE_HCYL_REG)<<16)
- + |(IN_BYTE(IDE_LCYL_REG)<<8)
- + | IN_BYTE(IDE_SECTOR_REG));
- + } else {
- + printk(", CHS=%d/%d/%d",
- + (IN_BYTE(IDE_HCYL_REG)<<8) +
- + IN_BYTE(IDE_LCYL_REG),
- + cur & 0xf,
- + IN_BYTE(IDE_SECTOR_REG));
- + }
- + if (HWGROUP(drive)->rq)
- + printk(", sector=%ld", HWGROUP(drive)->rq->sector);
- + }
- + }
- +#endif /* FANCY_STATUS_DUMPS */
- + printk("\n");
- + }
- + restore_flags (flags);
- + return err;
- +}
- +
- +/*
- + * try_to_flush_leftover_data() is invoked in response to a drive
- + * unexpectedly having its DRQ_STAT bit set. As an alternative to
- + * resetting the drive, this routine tries to clear the condition
- + * by read a sector's worth of data from the drive. Of course,
- + * this may not help if the drive is *waiting* for data from *us*.
- + */
- +static void try_to_flush_leftover_data (ide_drive_t *drive)
- +{
- + int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;
- +
- + while (i > 0) {
- + unsigned long buffer[16];
- + unsigned int wcount = (i > 16) ? 16 : i;
- + i -= wcount;
- + ide_input_data (drive, buffer, wcount);
- + }
- +}
- +
- +/*
- + * ide_error() takes action based on the error returned by the controller.
- + */
- +void ide_error (ide_drive_t *drive, const char *msg, byte stat)
- +{
- + struct request *rq;
- + byte err;
- +
- + err = ide_dump_status(drive, msg, stat);
- + if ((rq = HWGROUP(drive)->rq) == NULL || drive == NULL)
- + return;
- + /* retry only "normal" I/O: */
- + if (rq->cmd == IDE_DRIVE_CMD) {
- + rq->errors = 1;
- + ide_end_drive_cmd(drive, stat, err);
- + return;
- + }
- + if (stat & BUSY_STAT) { /* other bits are useless when BUSY */
- + rq->errors |= ERROR_RESET;
- + } else {
- + if (drive->media == ide_disk && (stat & ERR_STAT)) {
- + /* err has different meaning on cdrom and tape */
- + if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */
- + rq->errors = ERROR_MAX;
- + else if (err & TRK0_ERR) /* help it find track zero */
- + rq->errors |= ERROR_RECAL;
- + }
- + if ((stat & DRQ_STAT) && rq->cmd != WRITE)
- + try_to_flush_leftover_data(drive);
- + }
- + if (GET_STAT() & (BUSY_STAT|DRQ_STAT))
- + rq->errors |= ERROR_RESET; /* Mmmm.. timing problem */
- +
- + if (rq->errors >= ERROR_MAX) {
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- + if (drive->media == ide_tape) {
- + rq->errors = 0;
- + idetape_end_request(0, HWGROUP(drive));
- + }
- + else
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- + ide_end_request(0, HWGROUP(drive));
- + }
- + else {
- + if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
- + ++rq->errors;
- + ide_do_reset(drive);
- + return;
- + } else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
- + drive->special.b.recalibrate = 1;
- + ++rq->errors;
- + }
- +}
- +
- +/*
- + * read_intr() is the handler for disk read/multread interrupts
- + */
- +static void read_intr (ide_drive_t *drive)
- +{
- + byte stat;
- + int i;
- + unsigned int msect, nsect;
- + struct request *rq;
- +
- + if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
- + ide_error(drive, "read_intr", stat);
- + return;
- + }
- + msect = drive->mult_count;
- +read_next:
- + rq = HWGROUP(drive)->rq;
- + if (msect) {
- + if ((nsect = rq->current_nr_sectors) > msect)
- + nsect = msect;
- + msect -= nsect;
- + } else
- + nsect = 1;
- + ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
- +#ifdef DEBUG
- + printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
- + drive->name, rq->sector, rq->sector+nsect-1,
- + (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
- +#endif
- + rq->sector += nsect;
- + rq->buffer += nsect<<9;
- + rq->errors = 0;
- + i = (rq->nr_sectors -= nsect);
- + if ((rq->current_nr_sectors -= nsect) <= 0)
- + ide_end_request(1, HWGROUP(drive));
- + if (i > 0) {
- + if (msect)
- + goto read_next;
- + ide_set_handler (drive, &read_intr, WAIT_CMD);
- + }
- +}
- +
- +/*
- + * write_intr() is the handler for disk write interrupts
- + */
- +static void write_intr (ide_drive_t *drive)
- +{
- + byte stat;
- + int i;
- + ide_hwgroup_t *hwgroup = HWGROUP(drive);
- + struct request *rq = hwgroup->rq;
- +
- + if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
- +#ifdef DEBUG
- + printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
- + drive->name, rq->sector, (unsigned long) rq->buffer,
- + rq->nr_sectors-1);
- +#endif
- + if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) {
- + rq->sector++;
- + rq->buffer += 512;
- + rq->errors = 0;
- + i = --rq->nr_sectors;
- + --rq->current_nr_sectors;
- + if (rq->current_nr_sectors <= 0)
- + ide_end_request(1, hwgroup);
- + if (i > 0) {
- + ide_output_data (drive, rq->buffer, SECTOR_WORDS);
- + ide_set_handler (drive, &write_intr, WAIT_CMD);
- + }
- + return;
- + }
- + }
- + ide_error(drive, "write_intr", stat);
- +}
- +
- +/*
- + * ide_multwrite() transfers a block of up to mcount sectors of data
- + * to a drive as part of a disk multiple-sector write operation.
- + */
- +void ide_multwrite (ide_drive_t *drive, unsigned int mcount)
- +{
- + struct request *rq = &HWGROUP(drive)->wrq;
- +
- + do {
- + unsigned int nsect = rq->current_nr_sectors;
- + if (nsect > mcount)
- + nsect = mcount;
- + mcount -= nsect;
- +
- + ide_output_data(drive, rq->buffer, nsect<<7);
- +#ifdef DEBUG
- + printk("%s: multwrite: sector %ld, buffer=0x%08lx, count=%d, remaining=%ld\n",
- + drive->name, rq->sector, (unsigned long) rq->buffer,
- + nsect, rq->nr_sectors - nsect);
- +#endif
- + if ((rq->nr_sectors -= nsect) <= 0)
- + break;
- + if ((rq->current_nr_sectors -= nsect) == 0) {
- + if ((rq->bh = rq->bh->b_reqnext) != NULL) {
- + rq->current_nr_sectors = rq->bh->b_size>>9;
- + rq->buffer = rq->bh->b_data;
- + } else {
- + panic("%s: buffer list corrupted\n", drive->name);
- + break;
- + }
- + } else {
- + rq->buffer += nsect << 9;
- + }
- + } while (mcount);
- +}
- +
- +/*
- + * multwrite_intr() is the handler for disk multwrite interrupts
- + */
- +static void multwrite_intr (ide_drive_t *drive)
- +{
- + byte stat;
- + int i;
- + ide_hwgroup_t *hwgroup = HWGROUP(drive);
- + struct request *rq = &hwgroup->wrq;
- +
- + if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
- + if (stat & DRQ_STAT) {
- + if (rq->nr_sectors) {
- + ide_multwrite(drive, drive->mult_count);
- + ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
- + return;
- + }
- + } else {
- + if (!rq->nr_sectors) { /* all done? */
- + rq = hwgroup->rq;
- + for (i = rq->nr_sectors; i > 0;){
- + i -= rq->current_nr_sectors;
- + ide_end_request(1, hwgroup);
- + }
- + return;
- + }
- + }
- + }
- + ide_error(drive, "multwrite_intr", stat);
- +}
- +
- +/*
- + * Issue a simple drive command
- + * The drive must be selected beforehand.
- + */
- +static void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
- +{
- + ide_set_handler (drive, handler, WAIT_CMD);
- + OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
- + OUT_BYTE(nsect,IDE_NSECTOR_REG);
- + OUT_BYTE(cmd,IDE_COMMAND_REG);
- +}
- +
- +/*
- + * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
- + */
- +static void set_multmode_intr (ide_drive_t *drive)
- +{
- + byte stat = GET_STAT();
- +
- + sti();
- + if (OK_STAT(stat,READY_STAT,BAD_STAT)) {
- + drive->mult_count = drive->mult_req;
- + } else {
- + drive->mult_req = drive->mult_count = 0;
- + drive->special.b.recalibrate = 1;
- + (void) ide_dump_status(drive, "set_multmode", stat);
- + }
- +}
- +
- +/*
- + * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
- + */
- +static void set_geometry_intr (ide_drive_t *drive)
- +{
- + byte stat = GET_STAT();
- +
- + sti();
- + if (!OK_STAT(stat,READY_STAT,BAD_STAT))
- + ide_error(drive, "set_geometry_intr", stat);
- +}
- +
- +/*
- + * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
- + */
- +static void recal_intr (ide_drive_t *drive)
- +{
- + byte stat = GET_STAT();
- +
- + sti();
- + if (!OK_STAT(stat,READY_STAT,BAD_STAT))
- + ide_error(drive, "recal_intr", stat);
- +}
- +
- +/*
- + * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
- + */
- +static void drive_cmd_intr (ide_drive_t *drive)
- +{
- + struct request *rq = HWGROUP(drive)->rq;
- + byte *args = (byte *) rq->buffer;
- + byte stat = GET_STAT();
- +
- + sti();
- + if ((stat & DRQ_STAT) && args && args[3]) {
- + byte io_32bit = drive->io_32bit;
- + drive->io_32bit = 0;
- + ide_input_data(drive, &args[4], args[3] * SECTOR_WORDS);
- + drive->io_32bit = io_32bit;
- + stat = GET_STAT();
- + }
- + if (OK_STAT(stat,READY_STAT,BAD_STAT))
- + ide_end_drive_cmd (drive, stat, GET_ERR());
- + else
- + ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
- +}
- +
- +/*
- + * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT
- + * commands to a drive. It used to do much more, but has been scaled back.
- + */
- +static inline void do_special (ide_drive_t *drive)
- +{
- + special_t *s = &drive->special;
- +
- +#ifdef DEBUG
- + printk("%s: do_special: 0x%02x\n", drive->name, s->all);
- +#endif
- + if (s->b.set_geometry) {
- + s->b.set_geometry = 0;
- + if (drive->media == ide_disk) {
- + OUT_BYTE(drive->sect,IDE_SECTOR_REG);
- + OUT_BYTE(drive->cyl,IDE_LCYL_REG);
- + OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
- + OUT_BYTE(((drive->head-1)|drive->select.all)&0xBF,IDE_SELECT_REG);
- + if (!IS_PROMISE_DRIVE)
- + ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr);
- + }
- + } else if (s->b.recalibrate) {
- + s->b.recalibrate = 0;
- + if (drive->media == ide_disk && !IS_PROMISE_DRIVE)
- + ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr);
- + } else if (s->b.set_tune) {
- + ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
- + s->b.set_tune = 0;
- + if (tuneproc != NULL)
- + tuneproc(drive, drive->tune_req);
- + } else if (s->b.set_multmode) {
- + s->b.set_multmode = 0;
- + if (drive->media == ide_disk) {
- + if (drive->id && drive->mult_req > drive->id->max_multsect)
- + drive->mult_req = drive->id->max_multsect;
- + if (!IS_PROMISE_DRIVE)
- + ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
- + } else
- + drive->mult_req = 0;
- + } else if (s->all) {
- + int special = s->all;
- + s->all = 0;
- + printk("%s: bad special flag: 0x%02x\n", drive->name, special);
- + }
- +}
- +
- +/*
- + * This routine busy-waits for the drive status to be not "busy".
- + * It then checks the status for all of the "good" bits and none
- + * of the "bad" bits, and if all is okay it returns 0. All other
- + * cases return 1 after invoking ide_error() -- caller should just return.
- + *
- + * This routine should get fixed to not hog the cpu during extra long waits..
- + * That could be done by busy-waiting for the first jiffy or two, and then
- + * setting a timer to wake up at half second intervals thereafter,
- + * until timeout is achieved, before timing out.
- + */
- +int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout)
- +{
- + byte stat;
- + unsigned long flags;
- +
- +test:
- + udelay(1); /* spec allows drive 400ns to change "BUSY" */
- + if (OK_STAT((stat = GET_STAT()), good, bad))
- + return 0; /* fast exit for most frequent case */
- + if (!(stat & BUSY_STAT)) {
- + ide_error(drive, "status error", stat);
- + return 1;
- + }
- +
- + save_flags(flags);
- + sti();
- + timeout += jiffies;
- + do {
- + if (!((stat = GET_STAT()) & BUSY_STAT)) {
- + restore_flags(flags);
- + goto test;
- + }
- + } while (jiffies <= timeout);
- +
- + restore_flags(flags);
- + ide_error(drive, "status timeout", GET_STAT());
- + return 1;
- +}
- +
- +/*
- + * do_rw_disk() issues READ and WRITE commands to a disk,
- + * using LBA if supported, or CHS otherwise, to address sectors.
- + * It also takes care of issuing special DRIVE_CMDs.
- + */
- +static inline void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
- +{
- + ide_hwif_t *hwif = HWIF(drive);
- + unsigned long io_base = hwif->io.io_base;
- + unsigned long io_size = hwif->io.io_size;
- +#ifdef CONFIG_BLK_DEV_PROMISE
- + int use_promise_io = 0;
- +#endif /* CONFIG_BLK_DEV_PROMISE */
- +
- + OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
- + OUT_BYTE(rq->nr_sectors,io_base+io_size*IDE_NSECTOR_OFFSET);
- +#ifdef CONFIG_BLK_DEV_PROMISE
- + if (IS_PROMISE_DRIVE) {
- + if (hwif->is_promise2 || rq->cmd == READ) {
- + use_promise_io = 1;
- + }
- + }
- + if (drive->select.b.lba || use_promise_io) {
- +#else /* !CONFIG_BLK_DEV_PROMISE */
- + if (drive->select.b.lba) {
- +#endif /* CONFIG_BLK_DEV_PROMISE */
- +#ifdef DEBUG
- + printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n",
- + drive->name, (rq->cmd==READ)?"read":"writ",
- + block, rq->nr_sectors, (unsigned long) rq->buffer);
- +#endif
- + OUT_BYTE(block,io_base+io_size*IDE_SECTOR_OFFSET);
- + OUT_BYTE(block>>=8,io_base+io_size*IDE_LCYL_OFFSET);
- + OUT_BYTE(block>>=8,io_base+io_size*IDE_HCYL_OFFSET);
- + OUT_BYTE(((block>>8)&0x0f)|drive->select.all,io_base+io_size*IDE_SELECT_OFFSET);
- + } else {
- + unsigned int sect,head,cyl,track;
- + track = block / drive->sect;
- + sect = block % drive->sect + 1;
- + OUT_BYTE(sect,io_base+io_size*IDE_SECTOR_OFFSET);
- + head = track % drive->head;
- + cyl = track / drive->head;
- + OUT_BYTE(cyl,io_base+io_size*IDE_LCYL_OFFSET);
- + OUT_BYTE(cyl>>8,io_base+io_size*IDE_HCYL_OFFSET);
- + OUT_BYTE(head|drive->select.all,io_base+io_size*IDE_SELECT_OFFSET);
- +#ifdef DEBUG
- + printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n",
- + drive->name, (rq->cmd==READ)?"read":"writ", cyl,
- + head, sect, rq->nr_sectors, (unsigned long) rq->buffer);
- +#endif
- + }
- +#ifdef CONFIG_BLK_DEV_PROMISE
- + if (use_promise_io) {
- + do_promise_io (drive, rq);
- + return;
- + }
- +#endif /* CONFIG_BLK_DEV_PROMISE */
- + if (rq->cmd == READ) {
- + ide_set_handler(drive, &read_intr, WAIT_CMD);
- + OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, io_base+io_size*IDE_COMMAND_OFFSET);
- + return;
- + }
- + if (rq->cmd == WRITE) {
- + OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, io_base+io_size*IDE_COMMAND_OFFSET);
- + if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
- + printk("%s: no DRQ after issuing %s\n", drive->name,
- + drive->mult_count ? "MULTWRITE" : "WRITE");
- + return;
- + }
- + if (!drive->unmask)
- + cli();
- + if (drive->mult_count) {
- + HWGROUP(drive)->wrq = *rq; /* scratchpad */
- + ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
- + ide_multwrite(drive, drive->mult_count);
- + } else {
- + ide_set_handler (drive, &write_intr, WAIT_CMD);
- + ide_output_data(drive, rq->buffer, SECTOR_WORDS);
- + }
- + return;
- + }
- + printk("%s: bad command: %d\n", drive->name, rq->cmd);
- + ide_end_request(0, HWGROUP(drive));
- +}
- +
- +/*
- + * execute_drive_cmd() issues a special drive command,
- + * usually initiated by ioctl() from the external hdparm program.
- + */
- +static void execute_drive_cmd (ide_drive_t *drive, struct request *rq)
- +{
- + ide_hwif_t *hwif = HWIF(drive);
- + unsigned long io_base = hwif->io.io_base;
- + unsigned long io_size = hwif->io.io_size;
- + byte *args = rq->buffer;
- + if (args) {
- +#ifdef DEBUG
- + printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n",
- + drive->name, args[0], args[1], args[2], args[3]);
- +#endif
- + OUT_BYTE(args[2],io_base+io_size*IDE_FEATURE_OFFSET);
- + ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
- + return;
- + } else {
- + /*
- + * NULL is actually a valid way of waiting for
- + * all current requests to be flushed from the queue.
- + */
- +#ifdef DEBUG
- + printk("%s: DRIVE_CMD (null)\n", drive->name);
- +#endif
- + ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
- + return;
- + }
- +}
- +
- +/*
- + * do_request() initiates handling of a new I/O request
- + */
- +static inline void do_request (ide_hwif_t *hwif, struct request *rq)
- +{
- + unsigned int minor, unit;
- + unsigned long block, blockend;
- + ide_drive_t *drive;
- +
- + sti();
- +#ifdef DEBUG
- + printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
- +#endif
- + minor = MINOR(rq->rq_dev);
- + unit = minor >> PARTN_BITS;
- + if (MAJOR(rq->rq_dev) != hwif->major || unit >= MAX_DRIVES) {
- + printk("%s: bad device number: %s\n",
- + hwif->name, kdevname(rq->rq_dev));
- + goto kill_rq;
- + }
- + drive = &hwif->drives[unit];
- +#ifdef DEBUG
- + if (rq->bh && !buffer_locked(rq->bh)) {
- + printk("%s: block not locked\n", drive->name);
- + goto kill_rq;
- + }
- +#endif
- + block = rq->sector;
- + blockend = block + rq->nr_sectors;
- + if ((blockend < block) || (blockend > drive->part[minor&PARTN_MASK].nr_sects)) {
- + printk("%s%c: bad access: block=%ld, count=%ld\n", drive->name,
- + (minor&PARTN_MASK)?'0'+(minor&PARTN_MASK):' ', block, rq->nr_sectors);
- + goto kill_rq;
- + }
- + block += drive->part[minor&PARTN_MASK].start_sect + drive->sect0;
- +#if FAKE_FDISK_FOR_EZDRIVE
- + if (block == 0 && drive->remap_0_to_1)
- + block = 1; /* redirect MBR access to EZ-Drive partn table */
- +#endif /* FAKE_FDISK_FOR_EZDRIVE */
- + ((ide_hwgroup_t *)hwif->hwgroup)->drive = drive;
- +#if (DISK_RECOVERY_TIME > 0)
- + while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
- +#endif
- +
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- + POLL_HWIF_TAPE_DRIVE; /* macro from ide-tape.h */
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- +
- + SELECT_DRIVE(hwif,drive);
- + if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
- + printk("%s: drive not ready for command\n", drive->name);
- + return;
- + }
- +
- + if (!drive->special.all) {
- + if (rq->cmd == IDE_DRIVE_CMD) {
- + execute_drive_cmd(drive, rq);
- + return;
- + }
- +#ifdef CONFIG_BLK_DEV_IDEATAPI
- + switch (drive->media) {
- + case ide_disk:
- + do_rw_disk (drive, rq, block);
- + return;
- +#ifdef CONFIG_BLK_DEV_IDECD
- + case ide_cdrom:
- + ide_do_rw_cdrom (drive, block);
- + return;
- +#endif /* CONFIG_BLK_DEV_IDECD */
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- + case ide_tape:
- + idetape_do_request (drive, rq, block);
- + return;
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- +
- + default:
- + printk("%s: media type %d not supported\n",
- + drive->name, drive->media);
- + goto kill_rq;
- + }
- +#else
- + do_rw_disk (drive, rq, block); /* simpler and faster */
- + return;
- +#endif /* CONFIG_BLK_DEV_IDEATAPI */;
- + }
- + do_special(drive);
- + return;
- +kill_rq:
- + ide_end_request(0, hwif->hwgroup);
- +}
- +
- +/*
- + * The driver enables interrupts as much as possible. In order to do this,
- + * (a) the device-interrupt is always masked before entry, and
- + * (b) the timeout-interrupt is always disabled before entry.
- + *
- + * If we enter here from, say irq14, and then start a new request for irq15,
- + * (possible with "serialize" option) then we cannot ensure that we exit
- + * before the irq15 hits us. So, we must be careful not to let this bother us.
- + *
- + * Interrupts are still masked (by default) whenever we are exchanging
- + * data/cmds with a drive, because some drives seem to have very poor
- + * tolerance for latency during I/O. For devices which don't suffer from
- + * this problem (most don't), the unmask flag can be set using the "hdparm"
- + * utility, to permit other interrupts during data/cmd transfers.
- + */
- +void ide_do_request (ide_hwgroup_t *hwgroup)
- +{
- + cli(); /* paranoia */
- + if (hwgroup->handler != NULL) {
- + printk("%s: EEeekk!! handler not NULL in ide_do_request()\n", hwgroup->hwif->name);
- + return;
- + }
- + do {
- + ide_hwif_t *hwif = hwgroup->hwif;
- + struct request *rq;
- + if ((rq = hwgroup->rq) == NULL) {
- + if (hwif->sharing_irq && hwgroup->drive) /* set nIEN */
- + OUT_BYTE(hwgroup->drive->ctl|2,hwif->io.ctl_port);
- + /*
- + * hwgroup->next_hwif is different from hwgroup->hwif
- + * only when a request is inserted using "ide_next".
- + * This saves wear and tear on IDE tapes.
- + */
- + hwif = hwgroup->next_hwif;
- + do {
- + rq = blk_dev[hwif->major].current_request;
- + if (rq != NULL && rq->rq_status != RQ_INACTIVE)
- + goto got_rq;
- + } while ((hwif = hwif->next) != hwgroup->next_hwif);
- + return; /* no work left for this hwgroup */
- + }
- + got_rq:
- + do_request(hwgroup->hwif = hwgroup->next_hwif = hwif, hwgroup->rq = rq);
- + cli();
- + } while (hwgroup->handler == NULL);
- +}
- +
- +/*
- + * do_hwgroup_request() invokes ide_do_request() after first masking
- + * all possible interrupts for the current hwgroup. This prevents race
- + * conditions in the event that an unexpected interrupt occurs while
- + * we are in the driver.
- + *
- + * Note that when an interrupt is used to reenter the driver, the first level
- + * handler will already have masked the irq that triggered, but any other ones
- + * for the hwgroup will still be unmasked. The driver tries to be careful
- + * about such things.
- + */
- +static void do_hwgroup_request (ide_hwgroup_t *hwgroup)
- +{
- + if (hwgroup->handler == NULL) {
- + ide_hwif_t *hgif = hwgroup->hwif;
- + ide_hwif_t *hwif = hgif;
- + do {
- + disable_irq(hwif->irq);
- + } while ((hwif = hwif->next) != hgif);
- + ide_do_request (hwgroup);
- + do {
- + enable_irq(hwif->irq);
- + } while ((hwif = hwif->next) != hgif);
- + }
- +}
- +
- +static void do_ide0_request (void) /* invoked with cli() */
- +{
- + do_hwgroup_request (ide_hwifs[0].hwgroup);
- +}
- +
- +#if MAX_HWIFS > 1
- +static void do_ide1_request (void) /* invoked with cli() */
- +{
- + do_hwgroup_request (ide_hwifs[1].hwgroup);
- +}
- +#endif
- +
- +#if MAX_HWIFS > 2
- +static void do_ide2_request (void) /* invoked with cli() */
- +{
- + do_hwgroup_request (ide_hwifs[2].hwgroup);
- +}
- +#endif
- +
- +#if MAX_HWIFS > 3
- +static void do_ide3_request (void) /* invoked with cli() */
- +{
- + do_hwgroup_request (ide_hwifs[3].hwgroup);
- +}
- +#endif
- +
- +static void timer_expiry (unsigned long data)
- +{
- + ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
- + ide_drive_t *drive = hwgroup->drive;
- + unsigned long flags;
- +
- + save_flags(flags);
- + cli();
- +
- + if (hwgroup->poll_timeout != 0) { /* polling in progress? */
- + ide_handler_t *handler = hwgroup->handler;
- + hwgroup->handler = NULL;
- + handler(drive);
- + } else if (hwgroup->handler == NULL) { /* not waiting for anything? */
- + sti(); /* drive must have responded just as the timer expired */
- + printk("%s: marginal timeout\n", drive->name);
- + } else {
- + hwgroup->handler = NULL; /* abort the operation */
- + if (hwgroup->hwif->dmaproc)
- + (void) hwgroup->hwif->dmaproc (ide_dma_abort, drive);
- + ide_error(drive, "irq timeout", GET_STAT());
- + }
- + if (hwgroup->handler == NULL)
- + do_hwgroup_request (hwgroup);
- + restore_flags(flags);
- +}
- +
- +/*
- + * There's nothing really useful we can do with an unexpected interrupt,
- + * other than reading the status register (to clear it), and logging it.
- + * There should be no way that an irq can happen before we're ready for it,
- + * so we needn't worry much about losing an "important" interrupt here.
- + *
- + * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
- + * drive enters "idle", "standby", or "sleep" mode, so if the status looks
- + * "good", we just ignore the interrupt completely.
- + *
- + * This routine assumes cli() is in effect when called.
- + *
- + * If an unexpected interrupt happens on irq15 while we are handling irq14
- + * and if the two interfaces are "serialized" (CMD640), then it looks like
- + * we could screw up by interfering with a new request being set up for irq15.
- + *
- + * In reality, this is a non-issue. The new command is not sent unless the
- + * drive is ready to accept one, in which case we know the drive is not
- + * trying to interrupt us. And ide_set_handler() is always invoked before
- + * completing the issuance of any new drive command, so we will not be
- + * accidently invoked as a result of any valid command completion interrupt.
- + *
- + */
- +static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
- +{
- + byte stat;
- + unsigned int unit;
- + ide_hwif_t *hwif = hwgroup->hwif;
- +
- + /*
- + * handle the unexpected interrupt
- + */
- + do {
- + if (hwif->irq == irq) {
- + for (unit = 0; unit < MAX_DRIVES; ++unit) {
- + ide_drive_t *drive = &hwif->drives[unit];
- + if (!drive->present)
- + continue;
- + SELECT_DRIVE(hwif,drive);
- + udelay(100); /* Ugly, but wait_stat() may not be safe here */
- + if (!OK_STAT(stat=GET_STAT(), drive->ready_stat, BAD_STAT)) {
- + /* Try to not flood the console with msgs */
- + static unsigned long last_msgtime = 0;
- + if ((last_msgtime + (HZ/2)) < jiffies) {
- + last_msgtime = jiffies;
- + (void) ide_dump_status(drive, "unexpected_intr", stat);
- + }
- + }
- + if ((stat & DRQ_STAT))
- + try_to_flush_leftover_data(drive);
- + }
- + }
- + } while ((hwif = hwif->next) != hwgroup->hwif);
- + SELECT_DRIVE(hwif,hwgroup->drive); /* Ugh.. probably interrupts current I/O */
- + udelay(100); /* Ugly, but wait_stat() may not be safe here */
- +}
- +
- +/*
- + * entry point for all interrupts, caller does cli() for us
- + */
- +void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
- +{
- + ide_hwgroup_t *hwgroup = dev_id;
- + ide_handler_t *handler;
- +
- + if (irq == hwgroup->hwif->irq && (handler = hwgroup->handler) != NULL) {
- + ide_drive_t *drive = hwgroup->drive;
- + hwgroup->handler = NULL;
- + del_timer(&(hwgroup->timer));
- + if (drive->unmask)
- + sti();
- + handler(drive);
- + cli(); /* this is necessary, as next rq may be different irq */
- + if (hwgroup->handler == NULL) {
- + SET_RECOVERY_TIMER(HWIF(drive));
- + ide_do_request(hwgroup);
- + }
- + } else {
- + unexpected_intr(irq, hwgroup);
- + }
- + cli();
- +}
- +
- +/*
- + * get_info_ptr() returns the (ide_drive_t *) for a given device number.
- + * It returns NULL if the given device number does not match any present drives.
- + */
- +static ide_drive_t *get_info_ptr (kdev_t i_rdev)
- +{
- + int major = MAJOR(i_rdev);
- + unsigned int h;
- +
- + for (h = 0; h < MAX_HWIFS; ++h) {
- + ide_hwif_t *hwif = &ide_hwifs[h];
- + if (hwif->present && major == hwif->major) {
- + unsigned unit = DEVICE_NR(i_rdev);
- + if (unit < MAX_DRIVES) {
- + ide_drive_t *drive = &hwif->drives[unit];
- + if (drive->present)
- + return drive;
- + } else if (major == IDE0_MAJOR && unit < 4) {
- + printk("ide: probable bad entry for /dev/hd%c\n", 'a'+unit);
- + printk("ide: to fix it, run: /usr/src/linux/scripts/MAKEDEV.ide\n");
- + }
- + break;
- + }
- + }
- + return NULL;
- +}
- +
- +/*
- + * This function is intended to be used prior to invoking ide_do_drive_cmd().
- + */
- +void ide_init_drive_cmd (struct request *rq)
- +{
- + rq->buffer = NULL;
- + rq->cmd = IDE_DRIVE_CMD;
- + rq->sector = 0;
- + rq->nr_sectors = 0;
- + rq->current_nr_sectors = 0;
- + rq->sem = NULL;
- + rq->bh = NULL;
- + rq->bhtail = NULL;
- + rq->next = NULL;
- +
- +#if 0 /* these are done each time through ide_do_drive_cmd() */
- + rq->errors = 0;
- + rq->rq_status = RQ_ACTIVE;
- + rq->rq_dev = ????;
- +#endif
- +}
- +
- +/*
- + * This function issues a special IDE device request
- + * onto the request queue.
- + *
- + * If action is ide_wait, then then rq is queued at the end of
- + * the request queue, and the function sleeps until it has been
- + * processed. This is for use when invoked from an ioctl handler.
- + *
- + * If action is ide_preempt, then the rq is queued at the head of
- + * the request queue, displacing the currently-being-processed
- + * request and this function returns immediately without waiting
- + * for the new rq to be completed. This is VERY DANGEROUS, and is
- + * intended for careful use by the ATAPI tape/cdrom driver code.
- + *
- + * If action is ide_next, then the rq is queued immediately after
- + * the currently-being-processed-request (if any), and the function
- + * returns without waiting for the new rq to be completed. As above,
- + * This is VERY DANGEROUS, and is intended for careful use by the
- + * ATAPI tape/cdrom driver code.
- + *
- + * If action is ide_end, then the rq is queued at the end of the
- + * request queue, and the function returns immediately without waiting
- + * for the new rq to be completed. This is again intended for careful
- + * use by the ATAPI tape/cdrom driver code. (Currently used by ide-tape.c,
- + * when operating in the pipelined operation mode).
- + */
- +int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action)
- +{
- + unsigned long flags;
- + unsigned int major = HWIF(drive)->major;
- + struct request *cur_rq;
- + struct blk_dev_struct *bdev = &blk_dev[major];
- + struct semaphore sem = MUTEX_LOCKED;
- +
- + if (IS_PROMISE_DRIVE && rq->buffer != NULL)
- + return -ENOSYS; /* special drive cmds not supported */
- + rq->errors = 0;
- + rq->rq_status = RQ_ACTIVE;
- + rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
- + if (action == ide_wait)
- + rq->sem = &sem;
- + unplug_device(bdev);
- +
- + save_flags(flags);
- + cli();
- + if (action == ide_next)
- + HWGROUP(drive)->next_hwif = HWIF(drive);
- + cur_rq = bdev->current_request;
- +
- + if (cur_rq == NULL || action == ide_preempt) {
- + rq->next = cur_rq;
- + bdev->current_request = rq;
- + if (action == ide_preempt) {
- + HWGROUP(drive)->rq = NULL;
- + } else
- + if (HWGROUP(drive)->rq == NULL) { /* is this necessary (?) */
- + bdev->request_fn();
- + cli();
- + }
- + } else {
- + if (action == ide_wait || action == ide_end) {
- + while (cur_rq->next != NULL) /* find end of list */
- + cur_rq = cur_rq->next;
- + }
- + rq->next = cur_rq->next;
- + cur_rq->next = rq;
- + }
- + if (action == ide_wait && rq->rq_status != RQ_INACTIVE)
- + down(&sem); /* wait for it to be serviced */
- + restore_flags(flags);
- + return rq->errors ? -EIO : 0; /* return -EIO if errors */
- +}
- +
- +static int ide_open(struct inode * inode, struct file * filp)
- +{
- + ide_drive_t *drive;
- + unsigned long flags;
- +
- + if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
- + return -ENXIO;
- + save_flags(flags);
- + cli();
- + while (drive->busy)
- + sleep_on(&drive->wqueue);
- + drive->usage++;
- + restore_flags(flags);
- +#ifdef CONFIG_BLK_DEV_IDECD
- + if (drive->media == ide_cdrom)
- + return ide_cdrom_open (inode, filp, drive);
- +#endif /* CONFIG_BLK_DEV_IDECD */
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- + if (drive->media == ide_tape)
- + return idetape_blkdev_open (inode, filp, drive);
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- + if (drive->removable) {
- + byte door_lock[] = {WIN_DOORLOCK,0,0,0};
- + struct request rq;
- + check_disk_change(inode->i_rdev);
- + ide_init_drive_cmd (&rq);
- + rq.buffer = door_lock;
- + /*
- + * Ignore the return code from door_lock,
- + * since the open() has already succeeded,
- + * and the door_lock is irrelevant at this point.
- + */
- + (void) ide_do_drive_cmd(drive, &rq, ide_wait);
- + }
- + return 0;
- +}
- +
- +/*
- + * Releasing a block device means we sync() it, so that it can safely
- + * be forgotten about...
- + */
- +static void ide_release(struct inode * inode, struct file * file)
- +{
- + ide_drive_t *drive;
- +
- + if ((drive = get_info_ptr(inode->i_rdev)) != NULL) {
- + fsync_dev(inode->i_rdev);
- + drive->usage--;
- +#ifdef CONFIG_BLK_DEV_IDECD
- + if (drive->media == ide_cdrom) {
- + ide_cdrom_release (inode, file, drive);
- + return;
- + }
- +#endif /* CONFIG_BLK_DEV_IDECD */
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- + if (drive->media == ide_tape) {
- + idetape_blkdev_release (inode, file, drive);
- + return;
- + }
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- + if (drive->removable && !drive->usage) {
- + byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0};
- + struct request rq;
- + invalidate_buffers(inode->i_rdev);
- + ide_init_drive_cmd (&rq);
- + rq.buffer = door_unlock;
- + (void) ide_do_drive_cmd(drive, &rq, ide_wait);
- + }
- + }
- +}
- +
- +/*
- + * This routine is called to flush all partitions and partition tables
- + * for a changed disk, and then re-read the new partition table.
- + * If we are revalidating a disk because of a media change, then we
- + * enter with usage == 0. If we are using an ioctl, we automatically have
- + * usage == 1 (we need an open channel to use an ioctl :-), so this
- + * is our limit.
- + */
- +static int revalidate_disk(kdev_t i_rdev)
- +{
- + ide_drive_t *drive;
- + unsigned int p, major, minor;
- + long flags;
- +
- + if ((drive = get_info_ptr(i_rdev)) == NULL)
- + return -ENODEV;
- +
- + major = MAJOR(i_rdev);
- + minor = drive->select.b.unit << PARTN_BITS;
- + save_flags(flags);
- + cli();
- + if (drive->busy || (drive->usage > 1)) {
- + restore_flags(flags);
- + return -EBUSY;
- + };
- + drive->busy = 1;
- + restore_flags(flags);
- +
- + for (p = 0; p < (1<<PARTN_BITS); ++p) {
- + if (drive->part[p].nr_sects > 0) {
- + kdev_t devp = MKDEV(major, minor+p);
- + sync_dev (devp);
- + invalidate_inodes (devp);
- + invalidate_buffers (devp);
- + }
- + drive->part[p].start_sect = 0;
- + drive->part[p].nr_sects = 0;
- + };
- +
- + drive->part[0].nr_sects = current_capacity(drive);
- + if (drive->media != ide_disk)
- + drive->part[0].start_sect = -1;
- + resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit);
- +
- + drive->busy = 0;
- + wake_up(&drive->wqueue);
- + return 0;
- +}
- +
- +static int write_fs_long (unsigned long useraddr, long value)
- +{
- + int err;
- +
- + if (NULL == (long *)useraddr)
- + return -EINVAL;
- + if ((err = verify_area(VERIFY_WRITE, (long *)useraddr, sizeof(long))))
- + return err;
- + put_user((unsigned)value, (long *) useraddr);
- + return 0;
- +}
- +
- +static int ide_ioctl (struct inode *inode, struct file *file,
- + unsigned int cmd, unsigned long arg)
- +{
- + int err;
- + ide_drive_t *drive;
- + unsigned long flags;
- + struct request rq;
- +
- + if (!inode || !(inode->i_rdev))
- + return -EINVAL;
- + if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
- + return -ENODEV;
- + ide_init_drive_cmd (&rq);
- + switch (cmd) {
- + case HDIO_GETGEO:
- + {
- + struct hd_geometry *loc = (struct hd_geometry *) arg;
- + if (!loc || drive->media != ide_disk) return -EINVAL;
- + err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
- + if (err) return err;
- + put_user(drive->bios_head, (byte *) &loc->heads);
- + put_user(drive->bios_sect, (byte *) &loc->sectors);
- + put_user(drive->bios_cyl, (unsigned short *) &loc->cylinders);
- + put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
- + (unsigned long *) &loc->start);
- + return 0;
- + }
- + case BLKFLSBUF:
- + if (!suser()) return -EACCES;
- + fsync_dev(inode->i_rdev);
- + invalidate_buffers(inode->i_rdev);
- + return 0;
- +
- + case BLKRASET:
- + if (!suser()) return -EACCES;
- + if(arg > 0xff) return -EINVAL;
- + read_ahead[MAJOR(inode->i_rdev)] = arg;
- + return 0;
- +
- + case BLKRAGET:
- + return write_fs_long(arg, read_ahead[MAJOR(inode->i_rdev)]);
- +
- + case BLKGETSIZE: /* Return device size */
- + return write_fs_long(arg, drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects);
- + case BLKRRPART: /* Re-read partition tables */
- + if (!suser()) return -EACCES;
- + return revalidate_disk(inode->i_rdev);
- +
- + case HDIO_GET_KEEPSETTINGS:
- + return write_fs_long(arg, drive->keep_settings);
- +
- + case HDIO_GET_UNMASKINTR:
- + return write_fs_long(arg, drive->unmask);
- +
- + case HDIO_GET_DMA:
- + return write_fs_long(arg, drive->using_dma);
- +
- + case HDIO_GET_32BIT:
- + return write_fs_long(arg, drive->io_32bit);
- +
- + case HDIO_GET_MULTCOUNT:
- + return write_fs_long(arg, drive->mult_count);
- +
- + case HDIO_GET_IDENTITY:
- + if (!arg || (MINOR(inode->i_rdev) & PARTN_MASK))
- + return -EINVAL;
- + if (drive->id == NULL)
- + return -ENOMSG;
- + err = verify_area(VERIFY_WRITE, (char *)arg, sizeof(*drive->id));
- + if (!err)
- + memcpy_tofs((char *)arg, (char *)drive->id, sizeof(*drive->id));
- + return err;
- +
- + case HDIO_GET_NOWERR:
- + return write_fs_long(arg, drive->bad_wstat == BAD_R_STAT);
- +
- + case HDIO_SET_DMA:
- + if (!suser()) return -EACCES;
- +#ifdef CONFIG_BLK_DEV_IDECD
- + if (drive->media == ide_cdrom)
- + return -EPERM;
- +#endif /* CONFIG_BLK_DEV_IDECD */
- + if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
- + return -EPERM;
- + case HDIO_SET_KEEPSETTINGS:
- + case HDIO_SET_UNMASKINTR:
- + case HDIO_SET_NOWERR:
- + if (arg > 1)
- + return -EINVAL;
- + case HDIO_SET_32BIT:
- + if (!suser()) return -EACCES;
- + if ((MINOR(inode->i_rdev) & PARTN_MASK))
- + return -EINVAL;
- + save_flags(flags);
- + cli();
- + switch (cmd) {
- + case HDIO_SET_DMA:
- + if (!(HWIF(drive)->dmaproc)) {
- + restore_flags(flags);
- + return -EPERM;
- + }
- + drive->using_dma = arg;
- + break;
- + case HDIO_SET_KEEPSETTINGS:
- + drive->keep_settings = arg;
- + break;
- + case HDIO_SET_UNMASKINTR:
- + if (arg && drive->no_unmask) {
- + restore_flags(flags);
- + return -EPERM;
- + }
- + drive->unmask = arg;
- + break;
- + case HDIO_SET_NOWERR:
- + drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
- + break;
- + case HDIO_SET_32BIT:
- + if (arg > 1) {
- + restore_flags (flags);
- + return -EINVAL;
- + }
- + if (arg && drive->no_io_32bit) {
- + restore_flags(flags);
- + return -EPERM;
- + }
- + drive->io_32bit = arg;
- +#ifdef CONFIG_BLK_DEV_DTC2278
- + if (HWIF(drive)->chipset == ide_dtc2278)
- + HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
- +#endif /* CONFIG_BLK_DEV_DTC2278 */
- + break;
- + }
- + restore_flags(flags);
- + return 0;
- +
- + case HDIO_SET_MULTCOUNT:
- + if (!suser()) return -EACCES;
- + if (MINOR(inode->i_rdev) & PARTN_MASK)
- + return -EINVAL;
- + if (drive->id && arg > drive->id->max_multsect)
- + return -EINVAL;
- + save_flags(flags);
- + cli();
- + if (drive->special.b.set_multmode) {
- + restore_flags(flags);
- + return -EBUSY;
- + }
- + drive->mult_req = arg;
- + drive->special.b.set_multmode = 1;
- + restore_flags(flags);
- + (void) ide_do_drive_cmd (drive, &rq, ide_wait);
- + return (drive->mult_count == arg) ? 0 : -EIO;
- +
- + case HDIO_DRIVE_CMD:
- + {
- + byte args[4], *argbuf = args;
- + int argsize = 4;
- + if (!suser()) return -EACCES;
- + if (NULL == (void *) arg) {
- + err = ide_do_drive_cmd(drive, &rq, ide_wait);
- + } else if (!(err = verify_area(VERIFY_READ,(void *)arg, 4))) {
- + memcpy_fromfs(args, (void *)arg, 4);
- + if (args[3]) {
- + argsize = 4 + (SECTOR_WORDS * 4 * args[3]);
- + argbuf = kmalloc(argsize, GFP_KERNEL);
- + if (argbuf == NULL)
- + return -ENOMEM;
- + argbuf[0] = args[0];
- + argbuf[1] = args[1];
- + argbuf[2] = args[2];
- + argbuf[3] = args[3];
- + }
- + if (!(err = verify_area(VERIFY_WRITE,(void *)arg, argsize))) {
- + rq.buffer = argbuf;
- + err = ide_do_drive_cmd(drive, &rq, ide_wait);
- + memcpy_tofs((void *)arg, argbuf, argsize);
- + }
- + if (argsize > 4)
- + kfree(argbuf);
- + }
- + return err;
- + }
- + case HDIO_SET_PIO_MODE:
- + if (!suser()) return -EACCES;
- + if (MINOR(inode->i_rdev) & PARTN_MASK)
- + return -EINVAL;
- + if (!HWIF(drive)->tuneproc)
- + return -ENOSYS;
- + save_flags(flags);
- + cli();
- + if (drive->special.b.set_tune) {
- + restore_flags(flags);
- + return -EBUSY;
- + }
- + drive->tune_req = (byte) arg;
- + drive->special.b.set_tune = 1;
- + restore_flags(flags);
- + (void) ide_do_drive_cmd (drive, &rq, ide_wait);
- + return 0;
- +
- + RO_IOCTLS(inode->i_rdev, arg);
- +
- + default:
- +#ifdef CONFIG_BLK_DEV_IDECD
- + if (drive->media == ide_cdrom)
- + return ide_cdrom_ioctl(drive, inode, file, cmd, arg);
- +#endif /* CONFIG_BLK_DEV_IDECD */
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- + if (drive->media == ide_tape)
- + return idetape_blkdev_ioctl(drive, inode, file, cmd, arg);
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- + return -EPERM;
- + }
- +}
- +
- +static int ide_check_media_change (kdev_t i_rdev)
- +{
- + ide_drive_t *drive;
- +
- + if ((drive = get_info_ptr(i_rdev)) == NULL)
- + return -ENODEV;
- +#ifdef CONFIG_BLK_DEV_IDECD
- + if (drive->media == ide_cdrom)
- + return ide_cdrom_check_media_change (drive);
- +#endif /* CONFIG_BLK_DEV_IDECD */
- + if (drive->removable) /* for disks */
- + return 1; /* always assume it was changed */
- + return 0;
- +}
- +
- +void ide_fixstring (byte *s, const int bytecount, const int byteswap)
- +{
- + byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
- +
- + if (byteswap) {
- + /* convert from big-endian to host byte order */
- + for (p = end ; p != s;) {
- + unsigned short *pp = (unsigned short *) (p -= 2);
- + *pp = ntohs(*pp);
- + }
- + }
- +
- + /* strip leading blanks */
- + while (s != end && *s == ' ')
- + ++s;
- +
- + /* compress internal blanks and strip trailing blanks */
- + while (s != end && *s) {
- + if (*s++ != ' ' || (s != end && *s && *s != ' '))
- + *p++ = *(s-1);
- + }
- +
- + /* wipe out trailing garbage */
- + while (p != end)
- + *p++ = '\0';
- +}
- +
- +static inline void do_identify (ide_drive_t *drive, byte cmd)
- +{
- + int bswap;
- + struct hd_driveid *id;
- + unsigned long capacity, check;
- +
- + id = drive->id = kmalloc (SECTOR_WORDS*4, GFP_KERNEL);
- + ide_input_data(drive, id, SECTOR_WORDS); /* read 512 bytes of id info */
- + sti();
- +
- + /*
- + * EATA SCSI controllers do a hardware ATA emulation: ignore them
- + */
- + if ((id->model[0] == 'P' && id->model[1] == 'M')
- + || (id->model[0] == 'S' && id->model[1] == 'K')) {
- + printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model);
- + drive->present = 0;
- + return;
- + }
- +
- + /*
- + * WIN_IDENTIFY returns little-endian info,
- + * WIN_PIDENTIFY *usually* returns little-endian info.
- + */
- + bswap = 1;
- + if (cmd == WIN_PIDENTIFY) {
- + if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */
- + || (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */
- + || (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */
- + bswap = 0; /* Vertos drives may still be weird */
- + }
- + ide_fixstring (id->model, sizeof(id->model), bswap);
- + ide_fixstring (id->fw_rev, sizeof(id->fw_rev), bswap);
- + ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);
- +
- +#ifdef CONFIG_BLK_DEV_IDEATAPI
- + /*
- + * Check for an ATAPI device
- + */
- + if (cmd == WIN_PIDENTIFY) {
- + byte type = (id->config >> 8) & 0x1f;
- + printk("%s: %s, ATAPI ", drive->name, id->model);
- +#ifdef CONFIG_BLK_DEV_PROMISE
- + if (HWIF(drive)->is_promise2) {
- + printk(" -- not supported on 2nd Promise port\n");
- + drive->present = 0;
- + return;
- + }
- +#endif /* CONFIG_BLK_DEV_PROMISE */
- + switch (type) {
- + case 0: /* Early cdrom models used zero */
- + case 5:
- +#ifdef CONFIG_BLK_DEV_IDECD
- + printk ("CDROM drive\n");
- + drive->media = ide_cdrom;
- + drive->present = 1;
- + drive->removable = 1;
- + return;
- +#else
- + printk ("CDROM ");
- + break;
- +#endif /* CONFIG_BLK_DEV_IDECD */
- + case 1:
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- + printk ("TAPE drive");
- + if (idetape_identify_device (drive,id)) {
- + drive->media = ide_tape;
- + drive->present = 1;
- + drive->removable = 1;
- + if (drive->autotune != 2 && HWIF(drive)->dmaproc != NULL) {
- + if (!HWIF(drive)->dmaproc(ide_dma_check, drive))
- + printk(", DMA");
- + }
- + printk("\n");
- + }
- + else {
- + drive->present = 0;
- + printk ("\nide-tape: the tape is not supported by this version of the driver\n");
- + }
- + return;
- +#else
- + printk ("TAPE ");
- + break;
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- + default:
- + drive->present = 0;
- + printk("Type %d - Unknown device\n", type);
- + return;
- + }
- + drive->present = 0;
- + printk("- not supported by this kernel\n");
- + return;
- + }
- +#endif /* CONFIG_BLK_DEV_IDEATAPI */
- +
- + /* check for removable disks (eg. SYQUEST), ignore 'WD' drives */
- + if (id->config & (1<<7)) { /* removable disk ? */
- + if (id->model[0] != 'W' || id->model[1] != 'D')
- + drive->removable = 1;
- + }
- +
- + /* SunDisk drives: treat as non-removable, force one unit */
- + if (id->model[0] == 'S' && id->model[1] == 'u') {
- + drive->removable = 0;
- + if (drive->select.all & (1<<4)) {
- + drive->present = 0;
- + return;
- + }
- + }
- +
- + drive->media = ide_disk;
- + /* Extract geometry if we did not already have one for the drive */
- + if (!drive->present) {
- + drive->present = 1;
- + drive->cyl = drive->bios_cyl = id->cyls;
- + drive->head = drive->bios_head = id->heads;
- + drive->sect = drive->bios_sect = id->sectors;
- + }
- + /* Handle logical geometry translation by the drive */
- + if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads
- + && (id->cur_heads <= 16) && id->cur_sectors)
- + {
- + /*
- + * Extract the physical drive geometry for our use.
- + * Note that we purposely do *not* update the bios info.
- + * This way, programs that use it (like fdisk) will
- + * still have the same logical view as the BIOS does,
- + * which keeps the partition table from being screwed.
- + *
- + * An exception to this is the cylinder count,
- + * which we reexamine later on to correct for 1024 limitations.
- + */
- + drive->cyl = id->cur_cyls;
- + drive->head = id->cur_heads;
- + drive->sect = id->cur_sectors;
- +
- + /* check for word-swapped "capacity" field in id information */
- + capacity = drive->cyl * drive->head * drive->sect;
- + check = (id->cur_capacity0 << 16) | id->cur_capacity1;
- + if (check == capacity) { /* was it swapped? */
- + /* yes, bring it into little-endian order: */
- + id->cur_capacity0 = (capacity >> 0) & 0xffff;
- + id->cur_capacity1 = (capacity >> 16) & 0xffff;
- + }
- + }
- + /* Use physical geometry if what we have still makes no sense */
- + if ((!drive->head || drive->head > 16) && id->heads && id->heads <= 16) {
- + drive->cyl = id->cyls;
- + drive->head = id->heads;
- + drive->sect = id->sectors;
- + }
- + /* Correct the number of cyls if the bios value is too small */
- + if (drive->sect == drive->bios_sect && drive->head == drive->bios_head) {
- + if (drive->cyl > drive->bios_cyl)
- + drive->bios_cyl = drive->cyl;
- + }
- +
- + (void) current_capacity (drive); /* initialize LBA selection */
- +
- + printk ("%s: %.40s, %ldMB w/%dkB Cache, %sCHS=%d/%d/%d",
- + drive->name, id->model, current_capacity(drive)/2048L, id->buf_size/2,
- + drive->select.b.lba ? "LBA, " : "",
- + drive->bios_cyl, drive->bios_head, drive->bios_sect);
- +
- + drive->mult_count = 0;
- + if (id->max_multsect) {
- + drive->mult_req = INITIAL_MULT_COUNT;
- + if (drive->mult_req > id->max_multsect)
- + drive->mult_req = id->max_multsect;
- + if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
- + drive->special.b.set_multmode = 1;
- + }
- + if (drive->autotune != 2 && HWIF(drive)->dmaproc != NULL) {
- + if (!(HWIF(drive)->dmaproc(ide_dma_check, drive)))
- + printk(", DMA");
- + }
- + printk("\n");
- +}
- +
- +/*
- + * Delay for *at least* 50ms. As we don't know how much time is left
- + * until the next tick occurs, we wait an extra tick to be safe.
- + * This is used only during the probing/polling for drives at boot time.
- + */
- +static void delay_50ms (void)
- +{
- + unsigned long timer = jiffies + (HZ + 19)/20 + 1;
- + while (timer > jiffies);
- +}
- +
- +/*
- + * try_to_identify() sends an ATA(PI) IDENTIFY request to a drive
- + * and waits for a response. It also monitors irqs while this is
- + * happening, in hope of automatically determining which one is
- + * being used by the interface.
- + *
- + * Returns: 0 device was identified
- + * 1 device timed-out (no response to identify request)
- + * 2 device aborted the command (refused to identify itself)
- + */
- +static int try_to_identify (ide_drive_t *drive, byte cmd)
- +{
- + int hd_status, rc;
- + unsigned long timeout;
- + int irqs = 0;
- +
- + if (!HWIF(drive)->irq) { /* already got an IRQ? */
- + probe_irq_off(probe_irq_on()); /* clear dangling irqs */
- + irqs = probe_irq_on(); /* start monitoring irqs */
- + OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* enable device irq */
- + }
- +
- + delay_50ms(); /* take a deep breath */
- + if ((IN_BYTE(IDE_ALTSTATUS_REG) ^ IN_BYTE(IDE_STATUS_REG)) & ~INDEX_STAT) {
- + printk("%s: probing with STATUS instead of ALTSTATUS\n", drive->name);
- + hd_status = IDE_STATUS_REG; /* ancient Seagate drives */
- + } else
- + hd_status = IDE_ALTSTATUS_REG; /* use non-intrusive polling */
- +
- +#if CONFIG_BLK_DEV_PROMISE
- + if (IS_PROMISE_DRIVE) {
- + if (promise_cmd(drive,PROMISE_IDENTIFY)) {
- + if (irqs)
- + (void) probe_irq_off(irqs);
- + return 1;
- + }
- + } else
- +#endif /* CONFIG_BLK_DEV_PROMISE */
- + OUT_BYTE(cmd,IDE_COMMAND_REG); /* ask drive for ID */
- + timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
- + timeout += jiffies;
- + do {
- + if (jiffies > timeout) {
- + if (irqs)
- + (void) probe_irq_off(irqs);
- + return 1; /* drive timed-out */
- + }
- + delay_50ms(); /* give drive a breather */
- + } while (IN_BYTE(hd_status) & BUSY_STAT);
- +
- + delay_50ms(); /* wait for IRQ and DRQ_STAT */
- + if (OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) {
- + unsigned long flags;
- + save_flags(flags);
- + cli(); /* some systems need this */
- + do_identify(drive, cmd); /* drive returned ID */
- + rc = 0; /* drive responded with ID */
- + (void) GET_STAT(); /* clear drive IRQ */
- + restore_flags(flags);
- + } else
- + rc = 2; /* drive refused ID */
- + if (!HWIF(drive)->irq) {
- + irqs = probe_irq_off(irqs); /* get our irq number */
- + if (irqs > 0) {
- + HWIF(drive)->irq = irqs; /* save it for later */
- + irqs = probe_irq_on();
- + OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* mask device irq */
- + udelay(5);
- + (void) probe_irq_off(irqs);
- + (void) probe_irq_off(probe_irq_on()); /* clear self-inflicted irq */
- + (void) GET_STAT(); /* clear drive IRQ */
- +
- + } else { /* Mmmm.. multiple IRQs.. don't know which was ours */
- + printk("%s: IRQ probe failed (%d)\n", drive->name, irqs);
- +#ifdef CONFIG_BLK_DEV_CMD640
- +#ifdef CMD640_DUMP_REGS
- + if (HWIF(drive)->chipset == ide_cmd640) {
- + printk("%s: Hmmm.. probably a driver problem.\n", drive->name);
- + CMD640_DUMP_REGS;
- + }
- +#endif /* CMD640_DUMP_REGS */
- +#endif /* CONFIG_BLK_DEV_CMD640 */
- + }
- + }
- + return rc;
- +}
- +
- +/*
- + * do_probe() has the difficult job of finding a drive if it exists,
- + * without getting hung up if it doesn't exist, without trampling on
- + * ethernet cards, and without leaving any IRQs dangling to haunt us later.
- + *
- + * If a drive is "known" to exist (from CMOS or kernel parameters),
- + * but does not respond right away, the probe will "hang in there"
- + * for the maximum wait time (about 30 seconds), otherwise it will
- + * exit much more quickly.
- + *
- + * Returns: 0 device was identified
- + * 1 device timed-out (no response to identify request)
- + * 2 device aborted the command (refused to identify itself)
- + * 3 bad status from device (possible for ATAPI drives)
- + * 4 probe was not attempted because failure was obvious
- + */
- +static int do_probe (ide_drive_t *drive, byte cmd)
- +{
- + int rc;
- + ide_hwif_t *hwif = HWIF(drive);
- +#ifdef CONFIG_BLK_DEV_IDEATAPI
- + if (drive->present) { /* avoid waiting for inappropriate probes */
- + if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
- + return 4;
- + }
- +#endif /* CONFIG_BLK_DEV_IDEATAPI */
- +#ifdef DEBUG
- + printk("probing for %s: present=%d, media=%d, probetype=%s\n",
- + drive->name, drive->present, drive->media,
- + (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
- +#endif
- + SELECT_DRIVE(hwif,drive);
- + OUT_BYTE(drive->select.all,IDE_SELECT_REG); /* select target drive */
- + delay_50ms();
- + if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) {
- + OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */
- + delay_50ms(); /* allow BUSY_STAT to assert & clear */
- + return 3; /* no i/f present: avoid killing ethernet cards */
- + }
- +
- + if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT)
- + || drive->present || cmd == WIN_PIDENTIFY)
- + {
- + if ((rc = try_to_identify(drive,cmd))) /* send cmd and wait */
- + rc = try_to_identify(drive,cmd); /* failed: try again */
- + if (rc == 1)
- + printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT());
- + (void) GET_STAT(); /* ensure drive irq is clear */
- + } else {
- + rc = 3; /* not present or maybe ATAPI */
- + }
- + if (drive->select.b.unit != 0) {
- + OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */
- + delay_50ms();
- + (void) GET_STAT(); /* ensure drive irq is clear */
- + }
- + return rc;
- +}
- +
- +/*
- + * probe_for_drive() tests for existence of a given drive using do_probe().
- + *
- + * Returns: 0 no device was found
- + * 1 device was found (note: drive->present might still be 0)
- + */
- +static inline byte probe_for_drive (ide_drive_t *drive)
- +{
- + if (drive->noprobe) /* skip probing? */
- + return drive->present;
- + if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
- +#ifdef CONFIG_BLK_DEV_IDEATAPI
- + (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
- +#endif /* CONFIG_BLK_DEV_IDEATAPI */
- + }
- + if (!drive->present)
- + return 0; /* drive not found */
- + if (drive->id == NULL) { /* identification failed? */
- + if (drive->media == ide_disk) {
- + printk ("%s: non-IDE drive, CHS=%d/%d/%d\n",
- + drive->name, drive->cyl, drive->head, drive->sect);
- + }
- +#ifdef CONFIG_BLK_DEV_IDECD
- + else if (drive->media == ide_cdrom) {
- + printk("%s: ATAPI cdrom (?)\n", drive->name);
- + }
- +#endif /* CONFIG_BLK_DEV_IDECD */
- + else {
- + drive->present = 0; /* nuke it */
- + }
- + }
- + return 1; /* drive was found */
- +}
- +
- +/*
- + * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
- + * controller that is BIOS compatible with ST-506, and thus showing up in our
- + * BIOS table, but not register compatible, and therefore not present in CMOS.
- + *
- + * Furthermore, we will assume that our ST-506 drives <if any> are the primary
- + * drives in the system -- the ones reflected as drive 1 or 2. The first
- + * drive is stored in the high nibble of CMOS byte 0x12, the second in the low
- + * nibble. This will be either a 4 bit drive type or 0xf indicating use byte
- + * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value
- + * means we have an AT controller hard disk for that drive.
- + *
- + * Of course, there is no guarantee that either drive is actually on the
- + * "primary" IDE interface, but we don't bother trying to sort that out here.
- + * If a drive is not actually on the primary interface, then these parameters
- + * will be ignored. This results in the user having to supply the logical
- + * drive geometry as a boot parameter for each drive not on the primary i/f.
- + *
- + * The only "perfect" way to handle this would be to modify the setup.[cS] code
- + * to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
- + * for us during initialization. I have the necessary docs -- any takers? -ml
- + */
- +static void probe_cmos_for_drives (ide_hwif_t *hwif)
- +{
- +#ifdef __i386__
- + extern struct drive_info_struct drive_info;
- + byte cmos_disks, *BIOS = (byte *) &drive_info;
- + int unit;
- +
- +#ifdef CONFIG_BLK_DEV_PROMISE
- + if (hwif->is_promise2)
- + return;
- +#endif /* CONFIG_BLK_DEV_PROMISE */
- + outb_p(0x12,0x70); /* specify CMOS address 0x12 */
- + cmos_disks = inb_p(0x71); /* read the data from 0x12 */
- + /* Extract drive geometry from CMOS+BIOS if not already setup */
- + for (unit = 0; unit < MAX_DRIVES; ++unit) {
- + ide_drive_t *drive = &hwif->drives[unit];
- + if ((cmos_disks & (0xf0 >> (unit*4))) && !drive->present && !drive->nobios) {
- + drive->cyl = drive->bios_cyl = *(unsigned short *)BIOS;
- + drive->head = drive->bios_head = *(BIOS+2);
- + drive->sect = drive->bios_sect = *(BIOS+14);
- + drive->ctl = *(BIOS+8);
- + drive->present = 1;
- + }
- + BIOS += 16;
- + }
- +#endif
- +}
- +
- +/*
- + * This routine only knows how to look for drive units 0 and 1
- + * on an interface, so any setting of MAX_DRIVES > 2 won't work here.
- + */
- +static void probe_hwif (ide_hwif_t *hwif)
- +{
- + unsigned int unit;
- +
- + if (hwif->noprobe)
- + return;
- + if (hwif->io.io_base == HD_DATA)
- + probe_cmos_for_drives (hwif);
- +#if CONFIG_BLK_DEV_PROMISE
- + if (!hwif->is_promise2 &&
- + (check_region(hwif->io_base,8) || check_region(hwif->ctl_port,1))) {
- +#else
- + if (check_region(hwif->io.io_base,8) || check_region(hwif->io.ctl_port,1)) {
- +#endif /* CONFIG_BLK_DEV_PROMISE */
- + int msgout = 0;
- + for (unit = 0; unit < MAX_DRIVES; ++unit) {
- + ide_drive_t *drive = &hwif->drives[unit];
- + if (drive->present) {
- + drive->present = 0;
- + printk("%s: ERROR, PORTS ALREADY IN USE\n", drive->name);
- + msgout = 1;
- + }
- + }
- + if (!msgout)
- + printk("%s: ports already in use, skipping probe\n", hwif->name);
- + } else {
- + unsigned long flags;
- + save_flags(flags);
- +
- + sti(); /* needed for jiffies and irq probing */
- + /*
- + * Second drive should only exist if first drive was found,
- + * but a lot of cdrom drives are configured as single slaves.
- + */
- + for (unit = 0; unit < MAX_DRIVES; ++unit) {
- + ide_drive_t *drive = &hwif->drives[unit];
- + (void) probe_for_drive (drive);
- + if (drive->present && drive->media == ide_disk) {
- + if ((!drive->head || drive->head > 16) && !drive->select.b.lba) {
- + printk("%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n",
- + drive->name, drive->head);
- + drive->present = 0;
- + }
- + }
- + if (drive->present && !hwif->present) {
- + hwif->present = 1;
- + request_region(hwif->io.io_base, 8, hwif->name);
- + request_region(hwif->io.ctl_port, 1, hwif->name);
- + }
- + }
- + restore_flags(flags);
- + for (unit = 0; unit < MAX_DRIVES; ++unit) {
- + ide_drive_t *drive = &hwif->drives[unit];
- + if (drive->present && drive->media != ide_tape) {
- + ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
- + if (tuneproc != NULL && drive->autotune == 1)
- + tuneproc(drive, 255); /* auto-tune PIO mode */
- + }
- + }
- + }
- +}
- +
- +/*
- + * stridx() returns the offset of c within s,
- + * or -1 if c is '\0' or not found within s.
- + */
- +static int stridx (const char *s, char c)
- +{
- + char *i = strchr(s, c);
- + return (i && c) ? i - s : -1;
- +}
- +
- +/*
- + * match_parm() does parsing for ide_setup():
- + *
- + * 1. the first char of s must be '='.
- + * 2. if the remainder matches one of the supplied keywords,
- + * the index (1 based) of the keyword is negated and returned.
- + * 3. if the remainder is a series of no more than max_vals numbers
- + * separated by commas, the numbers are saved in vals[] and a
- + * count of how many were saved is returned. Base10 is assumed,
- + * and base16 is allowed when prefixed with "0x".
- + * 4. otherwise, zero is returned.
- + */
- +static int match_parm (char *s, const char *keywords[], int vals[], int max_vals)
- +{
- + static const char *decimal = "0123456789";
- + static const char *hex = "0123456789abcdef";
- + int i, n;
- +
- + if (*s++ == '=') {
- + /*
- + * Try matching against the supplied keywords,
- + * and return -(index+1) if we match one
- + */
- + if (keywords != NULL) {
- + for (i = 0; *keywords != NULL; ++i) {
- + if (!strcmp(s, *keywords++))
- + return -(i+1);
- + }
- + }
- + /*
- + * Look for a series of no more than "max_vals"
- + * numeric values separated by commas, in base10,
- + * or base16 when prefixed with "0x".
- + * Return a count of how many were found.
- + */
- + for (n = 0; (i = stridx(decimal, *s)) >= 0;) {
- + vals[n] = i;
- + while ((i = stridx(decimal, *++s)) >= 0)
- + vals[n] = (vals[n] * 10) + i;
- + if (*s == 'x' && !vals[n]) {
- + while ((i = stridx(hex, *++s)) >= 0)
- + vals[n] = (vals[n] * 0x10) + i;
- + }
- + if (++n == max_vals)
- + break;
- + if (*s == ',')
- + ++s;
- + }
- + if (!*s)
- + return n;
- + }
- + return 0; /* zero = nothing matched */
- +}
- +
- +/*
- + * ide_setup() gets called VERY EARLY during initialization,
- + * to handle kernel "command line" strings beginning with "hdx="
- + * or "ide". Here is the complete set currently supported:
- + *
- + * "hdx=" is recognized for all "x" from "a" to "h", such as "hdc".
- + * "idex=" is recognized for all "x" from "0" to "3", such as "ide1".
- + *
- + * "hdx=noprobe" : drive may be present, but do not probe for it
- + * "hdx=none" : drive is NOT present, ignore cmos and do not probe
- + * "hdx=nowerr" : ignore the WRERR_STAT bit on this drive
- + * "hdx=cdrom" : drive is present, and is a cdrom drive
- + * "hdx=cyl,head,sect" : disk drive is present, with specified geometry
- + * "hdx=autotune" : driver will attempt to tune interface speed
- + * to the fastest PIO mode supported,
- + * if possible for this drive only.
- + * Not fully supported by all chipset types,
- + * and quite likely to cause trouble with
- + * older/odd IDE drives.
- + *
- + * "idebus=xx" : inform IDE driver of VESA/PCI bus speed in Mhz,
- + * where "xx" is between 20 and 66 inclusive,
- + * used when tuning chipset PIO modes.
- + * For PCI bus, 25 is correct for a P75 system,
- + * 30 is correct for P90,P120,P180 systems,
- + * and 33 is used for P100,P133,P166 systems.
- + * If in doubt, use idebus=33 for PCI.
- + * As for VLB, it is safest to not specify it.
- + *
- + * "idex=noprobe" : do not attempt to access/use this interface
- + * "idex=base" : probe for an interface at the addr specified,
- + * where "base" is usually 0x1f0 or 0x170
- + * and "ctl" is assumed to be "base"+0x206
- + * "idex=base,ctl" : specify both base and ctl
- + * "idex=base,ctl,irq" : specify base, ctl, and irq number
- + * "idex=autotune" : driver will attempt to tune interface speed
- + * to the fastest PIO mode supported,
- + * for all drives on this interface.
- + * Not fully supported by all chipset types,
- + * and quite likely to cause trouble with
- + * older/odd IDE drives.
- + * "idex=noautotune" : driver will NOT attempt to tune interface speed
- + * This is the default for most chipsets,
- + * except the cmd640.
- + * "idex=serialize" : do not overlap operations on idex and ide(x^1)
- + *
- + * The following are valid ONLY on ide0,
- + * and the defaults for the base,ctl ports must not be altered.
- + *
- + * "ide0=dtc2278" : probe/support DTC2278 interface
- + * "ide0=ht6560b" : probe/support HT6560B interface
- + * "ide0=cmd640_vlb" : *REQUIRED* for VLB cards with the CMD640 chip
- + * (not for PCI -- automatically detected)
- + * "ide0=qd6580" : probe/support qd6580 interface
- + * "ide0=ali14xx" : probe/support ali14xx chipsets (ALI M1439, M1443, M1445)
- + * "ide0=umc8672" : probe/support umc8672 chipsets
- + */
- +void ide_setup (char *s)
- +{
- + int i, vals[3];
- + ide_hwif_t *hwif;
- + ide_drive_t *drive;
- + unsigned int hw, unit;
- + const char max_drive = 'a' + ((MAX_HWIFS * MAX_DRIVES) - 1);
- + const char max_hwif = '0' + (MAX_HWIFS - 1);
- +
- + printk("ide_setup: %s", s);
- + init_ide_data ();
- +
- + /*
- + * Look for drive options: "hdx="
- + */
- + if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
- + const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom",
- + "serialize", "autotune", "noautotune", NULL};
- + unit = s[2] - 'a';
- + hw = unit / MAX_DRIVES;
- + unit = unit % MAX_DRIVES;
- + hwif = &ide_hwifs[hw];
- + drive = &hwif->drives[unit];
- + switch (match_parm(&s[3], hd_words, vals, 3)) {
- + case -1: /* "none" */
- + drive->nobios = 1; /* drop into "noprobe" */
- + case -2: /* "noprobe" */
- + drive->noprobe = 1;
- + goto done;
- + case -3: /* "nowerr" */
- + drive->bad_wstat = BAD_R_STAT;
- + hwif->noprobe = 0;
- + goto done;
- + case -4: /* "cdrom" */
- + drive->present = 1;
- + drive->media = ide_cdrom;
- + hwif->noprobe = 0;
- + goto done;
- + case -5: /* "serialize" */
- + printk(" -- USE \"ide%d=serialize\" INSTEAD", hw);
- + goto do_serialize;
- + case -6: /* "autotune" */
- + drive->autotune = 1;
- + goto done;
- + case -7: /* "noautotune" */
- + drive->autotune = 2;
- + goto done;
- + case 3: /* cyl,head,sect */
- + drive->media = ide_disk;
- + drive->cyl = drive->bios_cyl = vals[0];
- + drive->head = drive->bios_head = vals[1];
- + drive->sect = drive->bios_sect = vals[2];
- + drive->present = 1;
- + drive->forced_geom = 1;
- + hwif->noprobe = 0;
- + goto done;
- + default:
- + goto bad_option;
- + }
- + }
- +
- + if (s[0] != 'i' || s[1] != 'd' || s[2] != 'e')
- + goto bad_option;
- + /*
- + * Look for bus speed option: "idebus="
- + */
- + if (s[3] == 'b' && s[4] == 'u' && s[5] == 's') {
- + if (match_parm(&s[6], NULL, vals, 1) != 1)
- + goto bad_option;
- + if (vals[0] >= 20 && vals[0] <= 66)
- + idebus_parameter = vals[0];
- + else
- + printk(" -- BAD BUS SPEED! Expected value from 20 to 66");
- + goto done;
- + }
- + /*
- + * Look for interface options: "idex="
- + */
- + if (s[3] >= '0' && s[3] <= max_hwif) {
- + /*
- + * Be VERY CAREFUL changing this: note hardcoded indexes below
- + */
- + const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune",
- + "qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL};
- + hw = s[3] - '0';
- + hwif = &ide_hwifs[hw];
- + i = match_parm(&s[4], ide_words, vals, 3);
- +
- + /*
- + * Cryptic check to ensure chipset not already set for hwif:
- + */
- + if (i > 0 || i <= -5) {
- + if (hwif->chipset != ide_unknown)
- + goto bad_option;
- + if (i <= -5) {
- + if (ide_hwifs[1].chipset != ide_unknown)
- + goto bad_option;
- + /*
- + * Interface keywords work only for ide0:
- + */
- + if (hw != 0)
- + goto bad_hwif;
- + }
- + }
- +
- + switch (i) {
- +#ifdef CONFIG_BLK_DEV_PROMISE
- + case -11: /* "dc4030" */
- + {
- + setup_dc4030(hwif);
- + goto done;
- + }
- +#endif /* CONFIG_BLK_DEV_PROMISE */
- + case -4: /* "noautotune" */
- + hwif->drives[0].autotune = 2;
- + hwif->drives[1].autotune = 2;
- + goto done;
- + case -3: /* "autotune" */
- + hwif->drives[0].autotune = 1;
- + hwif->drives[1].autotune = 1;
- + goto done;
- + case -2: /* "serialize" */
- + do_serialize:
- + ide_hwifs[hw].serialized = 1; /* serialize */
- + ide_hwifs[hw^1].serialized = 1; /* with mate */
- + goto done;
- +
- + case -1: /* "noprobe" */
- + hwif->noprobe = 1;
- + goto done;
- +
- + case 1: /* base */
- + vals[1] = vals[0] + 0x206; /* default ctl */
- + case 2: /* base,ctl */
- + vals[2] = 0; /* default irq = probe for it */
- + case 3: /* base,ctl,irq */
- + hwif->io.io_base = vals[0];
- + hwif->io.ctl_port = vals[1];
- + hwif->io.io_size = 1;
- + hwif->irq = vals[2];
- + hwif->noprobe = 0;
- + hwif->chipset = ide_generic;
- + goto done;
- +
- + case 0: goto bad_option;
- + default:
- + printk(" -- SUPPORT NOT CONFIGURED IN THIS KERNEL\n");
- + return;
- + }
- + }
- +bad_option:
- + printk(" -- BAD OPTION\n");
- + return;
- +bad_hwif:
- + printk("-- NOT SUPPORTED ON ide%d", hw);
- +done:
- + printk("\n");
- +}
- +
- +/*
- + * This routine is called from the partition-table code in genhd.c
- + * to "convert" a drive to a logical geometry with fewer than 1024 cyls.
- + *
- + * The second parameter, "xparm", determines exactly how the translation
- + * will be handled:
- + * 0 = convert to CHS with fewer than 1024 cyls
- + * using the same method as Ontrack DiskManager.
- + * 1 = same as "0", plus offset everything by 63 sectors.
- + * -1 = similar to "0", plus redirect sector 0 to sector 1.
- + * >1 = convert to a CHS geometry with "xparm" heads.
- + *
- + * Returns 0 if the translation was not possible, if the device was not
- + * an IDE disk drive, or if a geometry was "forced" on the commandline.
- + * Returns 1 if the geometry translation was successful.
- + */
- +int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg)
- +{
- + ide_drive_t *drive;
- + static const byte head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
- + const byte *heads = head_vals;
- + unsigned long tracks;
- +
- + if ((drive = get_info_ptr(i_rdev)) == NULL || drive->forced_geom)
- + return 0;
- +
- + if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63)
- + return 0; /* we already have a translation */
- +
- + printk("%s ", msg);
- +
- + if (drive->id) {
- + drive->cyl = drive->id->cyls;
- + drive->head = drive->id->heads;
- + drive->sect = drive->id->sectors;
- + }
- + drive->bios_cyl = drive->cyl;
- + drive->bios_head = drive->head;
- + drive->bios_sect = drive->sect;
- + drive->special.b.set_geometry = 1;
- +
- + tracks = drive->bios_cyl * drive->bios_head * drive->bios_sect / 63;
- + drive->bios_sect = 63;
- + if (xparm > 1) {
- + drive->bios_head = xparm;
- + drive->bios_cyl = tracks / drive->bios_head;
- + } else {
- + while (drive->bios_cyl >= 1024) {
- + drive->bios_head = *heads;
- + drive->bios_cyl = tracks / drive->bios_head;
- + if (0 == *++heads)
- + break;
- + }
- +#if FAKE_FDISK_FOR_EZDRIVE
- + if (xparm == -1) {
- + drive->remap_0_to_1 = 1;
- + msg = "0->1";
- + } else
- +#endif /* FAKE_FDISK_FOR_EZDRIVE */
- + if (xparm == 1) {
- + drive->sect0 = 63;
- + drive->bios_cyl = (tracks - 1) / drive->bios_head;
- + msg = "+63";
- + }
- + printk("[remap %s] ", msg);
- + }
- + drive->part[0].nr_sects = current_capacity(drive);
- + printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect);
- + return 1;
- +}
- +
- +#if MAX_HWIFS > 1
- +/*
- + * save_match() is used to simplify logic in init_irq() below.
- + *
- + * A loophole here is that we may not know about a particular
- + * hwif's irq until after that hwif is actually probed/initialized..
- + * This could be a problem for the case where an hwif is on a
- + * dual interface that requires serialization (eg. cmd640) and another
- + * hwif using one of the same irqs is initialized beforehand.
- + *
- + * This routine detects and reports such situations, but does not fix them.
- + */
- +static void save_match (ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match)
- +{
- + ide_hwif_t *m = *match;
- +
- + if (m && m->hwgroup && m->hwgroup != new->hwgroup) {
- + if (!new->hwgroup)
- + return;
- + printk("%s: potential irq problem with %s and %s\n", hwif->name, new->name, m->name);
- + }
- + if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */
- + *match = new;
- +}
- +#endif /* MAX_HWIFS > 1 */
- +
- +/*
- + * This routine sets up the irq for an ide interface, and creates a new
- + * hwgroup for the irq/hwif if none was previously assigned.
- + *
- + * Much of the code is for correctly detecting/handling irq sharing
- + * and irq serialization situations. This is somewhat complex because
- + * it handles static as well as dynamic (PCMCIA) IDE interfaces.
- + *
- + * The SA_INTERRUPT in sa_flags means ide_intr() is always entered with
- + * interrupts completely disabled. This can be bad for interrupt latency,
- + * but anything else has led to problems on some machines. We re-enable
- + * interrupts as much as we can safely do in most places.
- + */
- +static int init_irq (ide_hwif_t *hwif)
- +{
- + unsigned long flags;
- +#if MAX_HWIFS > 1
- + unsigned int index;
- +#endif /* MAX_HWIFS > 1 */
- + ide_hwgroup_t *hwgroup;
- + ide_hwif_t *match = NULL;
- +
- + save_flags(flags);
- + cli();
- +
- + hwif->hwgroup = NULL;
- +#if MAX_HWIFS > 1
- + /*
- + * Group up with any other hwifs that share our irq(s).
- + */
- + for (index = 0; index < MAX_HWIFS; index++) {
- + ide_hwif_t *h = &ide_hwifs[index];
- + if (h->hwgroup) { /* scan only initialized hwif's */
- + if (hwif->irq == h->irq) {
- + hwif->sharing_irq = h->sharing_irq = 1;
- + save_match(hwif, h, &match);
- + }
- + if (hwif->serialized) {
- + ide_hwif_t *mate = &ide_hwifs[hwif->index^1];
- + if (index == mate->index || h->irq == mate->irq)
- + save_match(hwif, h, &match);
- + }
- + if (h->serialized) {
- + ide_hwif_t *mate = &ide_hwifs[h->index^1];
- + if (hwif->irq == mate->irq)
- + save_match(hwif, h, &match);
- + }
- + }
- + }
- +#endif /* MAX_HWIFS > 1 */
- + /*
- + * If we are still without a hwgroup, then form a new one
- + */
- + if (match) {
- + hwgroup = match->hwgroup;
- + } else {
- + hwgroup = kmalloc(sizeof(ide_hwgroup_t), GFP_KERNEL);
- + hwgroup->hwif = hwgroup->next_hwif = hwif->next = hwif;
- + hwgroup->rq = NULL;
- + hwgroup->handler = NULL;
- + if (hwif->drives[0].present)
- + hwgroup->drive = &hwif->drives[0];
- + else
- + hwgroup->drive = &hwif->drives[1];
- + hwgroup->poll_timeout = 0;
- + init_timer(&hwgroup->timer);
- + hwgroup->timer.function = &timer_expiry;
- + hwgroup->timer.data = (unsigned long) hwgroup;
- + }
- +
- + /*
- + * Allocate the irq, if not already obtained for another hwif
- + */
- + if (!match || match->irq != hwif->irq) {
- + if (request_irq(hwif->irq, ide_intr, SA_INTERRUPT, hwif->name, hwgroup)) {
- + if (!match)
- + kfree(hwgroup);
- + restore_flags(flags);
- + return 1;
- + }
- + }
- +
- + /*
- + * Everything is okay, so link us into the hwgroup
- + */
- + hwif->hwgroup = hwgroup;
- + hwif->next = hwgroup->hwif->next;
- + hwgroup->hwif->next = hwif;
- +
- + restore_flags(flags); /* safe now that hwif->hwgroup is set up */
- +
- + printk("%s at 0x%03lx-0x%03lx,0x%03lx on irq %d", hwif->name,
- + hwif->io.io_base, hwif->io.io_base+7, hwif->io.ctl_port, hwif->irq);
- + if (match)
- + printk(" (%sed with %s)", hwif->sharing_irq ? "shar" : "serializ", match->name);
- + printk("\n");
- + return 0;
- +}
- +
- +static struct file_operations ide_fops = {
- + NULL, /* lseek - default */
- + block_read, /* read - general block-dev read */
- + block_write, /* write - general block-dev write */
- + NULL, /* readdir - bad */
- + NULL, /* select */
- + ide_ioctl, /* ioctl */
- + NULL, /* mmap */
- + ide_open, /* open */
- + ide_release, /* release */
- + block_fsync /* fsync */
- + ,NULL, /* fasync */
- + ide_check_media_change, /* check_media_change */
- + revalidate_disk /* revalidate */
- +};
- +
- +/*
- + * ide_init_pci() finds/initializes "known" PCI IDE interfaces
- + *
- + * This routine should ideally be using pcibios_find_class() to find
- + * all IDE interfaces, but that function causes some systems to "go weird".
- + */
- +static void probe_for_hwifs (void)
- +{
- +}
- +
- +static int hwif_init (int h)
- +{
- + ide_hwif_t *hwif = &ide_hwifs[h];
- + void (*rfn)(void);
- +
- + if (!hwif->present)
- + return 0;
- + if (!hwif->irq) {
- + if (!(hwif->irq = default_irqs[h])) {
- + printk("%s: DISABLED, NO IRQ\n", hwif->name);
- + return (hwif->present = 0);
- + }
- + }
- +#ifdef CONFIG_BLK_DEV_HD
- + if (hwif->irq == HD_IRQ && hwif->io_base != HD_DATA) {
- + printk("%s: CANNOT SHARE IRQ WITH OLD HARDDISK DRIVER (hd.c)\n", hwif->name);
- + return (hwif->present = 0);
- + }
- +#endif /* CONFIG_BLK_DEV_HD */
- +
- + hwif->present = 0; /* we set it back to 1 if all is ok below */
- + switch (hwif->major) {
- + case IDE0_MAJOR: rfn = &do_ide0_request; break;
- +#if MAX_HWIFS > 1
- + case IDE1_MAJOR: rfn = &do_ide1_request; break;
- +#endif
- +#if MAX_HWIFS > 2
- + case IDE2_MAJOR: rfn = &do_ide2_request; break;
- +#endif
- +#if MAX_HWIFS > 3
- + case IDE3_MAJOR: rfn = &do_ide3_request; break;
- +#endif
- + default:
- + printk("%s: request_fn NOT DEFINED\n", hwif->name);
- + return (hwif->present = 0);
- + }
- + if (register_blkdev (hwif->major, hwif->name, &ide_fops)) {
- + printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", hwif->name, hwif->major);
- + } else if (init_irq (hwif)) {
- + printk("%s: UNABLE TO GET IRQ %d\n", hwif->name, hwif->irq);
- + (void) unregister_blkdev (hwif->major, hwif->name);
- + } else {
- + init_gendisk(hwif);
- + blk_dev[hwif->major].request_fn = rfn;
- + read_ahead[hwif->major] = 8; /* (4kB) */
- + hwif->present = 1; /* success */
- + }
- + return hwif->present;
- +}
- +
- +/*
- + * This is gets invoked once during initialization, to set *everything* up
- + */
- +int ide_init (void)
- +{
- + int index;
- +
- + init_ide_data ();
- + /*
- + * Probe for special "known" interface chipsets
- + */
- + probe_for_hwifs ();
- +
- + /*
- + * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
- + */
- + for (index = 0; index < MAX_HWIFS; ++index)
- + probe_hwif (&ide_hwifs[index]);
- + for (index = 0; index < MAX_HWIFS; ++index)
- + hwif_init (index);
- +
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- + idetape_register_chrdev(); /* Register character device interface to the ide tape */
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- +
- + return 0;
- +}
- +
- +#ifdef CONFIG_BLK_DEV_IDE_PCMCIA
- +int ide_register(int io_base, int ctl_port, int irq)
- +{
- + int index, i, rc = -1;
- + ide_hwif_t *hwif;
- + ide_drive_t *drive;
- + unsigned long flags;
- +
- + save_flags(flags);
- + cli();
- + for (index = 0; index < MAX_HWIFS; ++index) {
- + hwif = &ide_hwifs[index];
- + if (hwif->present) {
- + if (hwif->io_base == io_base || hwif->ctl_port == ctl_port)
- + break; /* this ide port already exists */
- + } else {
- + hwif->io_base = io_base;
- + hwif->ctl_port = ctl_port;
- + hwif->irq = irq;
- + hwif->noprobe = 0;
- + probe_hwif(hwif);
- + if (!hwif_init(index))
- + break;
- + for (i = 0; i < hwif->gd->nr_real; i++) {
- + drive = &hwif->drives[i];
- + revalidate_disk(MKDEV(hwif->major, i<<PARTN_BITS));
- +#ifdef CONFIG_BLK_DEV_IDECD
- + if (drive->present && drive->media == ide_cdrom)
- + ide_cdrom_setup(drive);
- +#endif /* CONFIG_BLK_DEV_IDECD */
- + }
- + rc = index;
- + break;
- + }
- + }
- + restore_flags(flags);
- + return rc;
- +}
- +
- +void ide_unregister (unsigned int index)
- +{
- + struct gendisk *gd, **gdp;
- + ide_hwif_t *hwif, *g;
- + ide_hwgroup_t *hwgroup;
- + int irq_count = 0;
- + unsigned long flags;
- +
- + if (index >= MAX_HWIFS)
- + return;
- + save_flags(flags);
- + cli();
- + hwif = &ide_hwifs[index];
- + if (!hwif->present || hwif->drives[0].busy || hwif->drives[1].busy) {
- + restore_flags(flags);
- + return;
- + }
- + hwif->present = 0;
- + hwgroup = hwif->hwgroup;
- +
- + /*
- + * free the irq if we were the only hwif using it
- + */
- + g = hwgroup->hwif;
- + do {
- + if (g->irq == hwif->irq)
- + ++irq_count;
- + g = g->next;
- + } while (g != hwgroup->hwif);
- + if (irq_count == 1)
- + free_irq(hwif->irq, hwgroup);
- +
- + /*
- + * Note that we only release the standard ports,
- + * and do not even try to handle any extra ports
- + * allocated for weird IDE interface chipsets.
- + */
- + release_region(hwif->io_base, 8);
- + release_region(hwif->ctl_port, 1);
- +
- + /*
- + * Remove us from the hwgroup, and free
- + * the hwgroup if we were the only member
- + */
- + while (hwgroup->hwif->next != hwif)
- + hwgroup->hwif = hwgroup->hwif->next;
- + hwgroup->hwif->next = hwif->next;
- + if (hwgroup->hwif == hwif)
- + hwgroup->hwif = hwif->next;
- + if (hwgroup->next_hwif == hwif)
- + hwgroup->next_hwif = hwif->next;
- + if (hwgroup->hwif == hwif)
- + kfree(hwgroup);
- +
- + /*
- + * Remove us from the kernel's knowledge
- + */
- + unregister_blkdev(hwif->major, hwif->name);
- + kfree(blksize_size[hwif->major]);
- + blk_dev[hwif->major].request_fn = NULL;
- + blksize_size[hwif->major] = NULL;
- + for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
- + if (*gdp == hwif->gd)
- + break;
- + if (*gdp == NULL)
- + printk("gd not in disk chain!\n");
- + else {
- + gd = *gdp; *gdp = gd->next;
- + kfree(gd->sizes);
- + kfree(gd->part);
- + kfree(gd);
- + }
- + init_hwif_data (index); /* restore hwif data to pristine status */
- + restore_flags(flags);
- +}
- +#endif /* CONFIG_BLK_DEV_IDE_PCMCIA */
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/ide.h linux/arch/arm/drivers/block/ide.h
- --- linux.orig/arch/arm/drivers/block/ide.h Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/ide.h Sat Sep 7 10:26:07 1996
- @@ -0,0 +1,678 @@
- +/*
- + * linux/drivers/block/ide.h
- + *
- + * Copyright (C) 1994, 1995 Linus Torvalds & authors
- + */
- +
- +#include <linux/config.h>
- +
- +/*
- + * This is the multiple IDE interface driver, as evolved from hd.c.
- + * It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
- + * There can be up to two drives per interface, as per the ATA-2 spec.
- + *
- + * Primary i/f: ide0: major=3; (hda) minor=0; (hdb) minor=64
- + * Secondary i/f: ide1: major=22; (hdc or hd1a) minor=0; (hdd or hd1b) minor=64
- + * Tertiary i/f: ide2: major=33; (hde) minor=0; (hdf) minor=64
- + * Quaternary i/f: ide3: major=34; (hdg) minor=0; (hdh) minor=64
- + */
- +
- +/******************************************************************************
- + * IDE driver configuration options (play with these as desired):
- + *
- + * REALLY_SLOW_IO can be defined in ide.c and ide-cd.c, if necessary
- + */
- +#undef REALLY_FAST_IO /* define if ide ports are perfect */
- +#define INITIAL_MULT_COUNT 0 /* off=0; on=2,4,8,16,32, etc.. */
- +
- +#ifndef DISK_RECOVERY_TIME /* off=0; on=access_delay_time */
- +#define DISK_RECOVERY_TIME 0 /* for hardware that needs it */
- +#endif
- +#ifndef OK_TO_RESET_CONTROLLER /* 1 needed for good error recovery */
- +#define OK_TO_RESET_CONTROLLER 1 /* 0 for use with AH2372A/B interface */
- +#endif
- +#ifndef FAKE_FDISK_FOR_EZDRIVE /* 1 to help linux fdisk with EZDRIVE */
- +#define FAKE_FDISK_FOR_EZDRIVE 1 /* 0 to reduce kernel size */
- +#endif
- +#ifndef FANCY_STATUS_DUMPS /* 1 for human-readable drive errors */
- +#define FANCY_STATUS_DUMPS 1 /* 0 to reduce kernel size */
- +#endif
- +
- +#if defined(CONFIG_BLK_DEV_IDECD) || defined(CONFIG_BLK_DEV_IDETAPE)
- +#define CONFIG_BLK_DEV_IDEATAPI 1
- +#endif
- +
- +/*
- + * IDE_DRIVE_CMD is used to implement many features of the hdparm utility
- + */
- +#define IDE_DRIVE_CMD 99 /* (magic) undef to reduce kernel size*/
- +
- +/*
- + * "No user-serviceable parts" beyond this point :)
- + *****************************************************************************/
- +
- +typedef unsigned char byte; /* used everywhere */
- +
- +/*
- + * Probably not wise to fiddle with these
- + */
- +#define ERROR_MAX 8 /* Max read/write errors per sector */
- +#define ERROR_RESET 3 /* Reset controller every 4th retry */
- +#define ERROR_RECAL 1 /* Recalibrate every 2nd retry */
- +
- +/*
- + * Ensure that various configuration flags have compatible settings
- + */
- +#ifdef REALLY_SLOW_IO
- +#undef REALLY_FAST_IO
- +#endif
- +
- +/*
- + * Definitions for accessing IDE controller registers
- + */
- +
- +#define HWIF(drive) ((ide_hwif_t *)((drive)->hwif))
- +#define HWGROUP(drive) ((ide_hwgroup_t *)(HWIF(drive)->hwgroup))
- +
- +#define IDE_DATA_OFFSET (0)
- +#define IDE_ERROR_OFFSET (1)
- +#define IDE_NSECTOR_OFFSET (2)
- +#define IDE_SECTOR_OFFSET (3)
- +#define IDE_LCYL_OFFSET (4)
- +#define IDE_HCYL_OFFSET (5)
- +#define IDE_SELECT_OFFSET (6)
- +#define IDE_STATUS_OFFSET (7)
- +#define IDE_FEATURE_OFFSET IDE_ERROR_OFFSET
- +#define IDE_COMMAND_OFFSET IDE_STATUS_OFFSET
- +
- +#define IDE_DATA_REG (HWIF(drive)->io.io_base+HWIF(drive)->io.io_size*IDE_DATA_OFFSET)
- +#define IDE_ERROR_REG (HWIF(drive)->io.io_base+HWIF(drive)->io.io_size*IDE_ERROR_OFFSET)
- +#define IDE_NSECTOR_REG (HWIF(drive)->io.io_base+HWIF(drive)->io.io_size*IDE_NSECTOR_OFFSET)
- +#define IDE_SECTOR_REG (HWIF(drive)->io.io_base+HWIF(drive)->io.io_size*IDE_SECTOR_OFFSET)
- +#define IDE_LCYL_REG (HWIF(drive)->io.io_base+HWIF(drive)->io.io_size*IDE_LCYL_OFFSET)
- +#define IDE_HCYL_REG (HWIF(drive)->io.io_base+HWIF(drive)->io.io_size*IDE_HCYL_OFFSET)
- +#define IDE_SELECT_REG (HWIF(drive)->io.io_base+HWIF(drive)->io.io_size*IDE_SELECT_OFFSET)
- +#define IDE_STATUS_REG (HWIF(drive)->io.io_base+HWIF(drive)->io.io_size*IDE_STATUS_OFFSET)
- +#define IDE_CONTROL_REG (HWIF(drive)->io.ctl_port)
- +#define IDE_FEATURE_REG IDE_ERROR_REG
- +#define IDE_COMMAND_REG IDE_STATUS_REG
- +#define IDE_ALTSTATUS_REG IDE_CONTROL_REG
- +
- +#ifdef REALLY_FAST_IO
- +#define OUT_BYTE(b,p) outb((b),(p))
- +#define IN_BYTE(p) (byte)inb(p)
- +#else
- +#define OUT_BYTE(b,p) outb_p((b),(p))
- +#define IN_BYTE(p) (byte)inb_p(p)
- +#endif /* REALLY_FAST_IO */
- +
- +#define GET_ERR() IN_BYTE(IDE_ERROR_REG)
- +#define GET_STAT() IN_BYTE(IDE_STATUS_REG)
- +#define OK_STAT(stat,good,bad) (((stat)&((good)|(bad)))==(good))
- +#define BAD_R_STAT (BUSY_STAT | ERR_STAT)
- +#define BAD_W_STAT (BAD_R_STAT | WRERR_STAT)
- +#define BAD_STAT (BAD_R_STAT | DRQ_STAT)
- +#define DRIVE_READY (READY_STAT | SEEK_STAT)
- +#define DATA_READY (DRIVE_READY | DRQ_STAT)
- +
- +/*
- + * Some more useful definitions
- + */
- +#define IDE_MAJOR_NAME "ide" /* the same for all i/f; see also genhd.c */
- +#define MAJOR_NAME IDE_MAJOR_NAME
- +#define PARTN_BITS 6 /* number of minor dev bits for partitions */
- +#define PARTN_MASK ((1<<PARTN_BITS)-1) /* a useful bit mask */
- +#define MAX_DRIVES 2 /* per interface; 2 assumed by lots of code */
- +#ifndef MAX_HWIFS
- +#define MAX_HWIFS 4 /* an arbitrary, but realistic limit */
- +#endif
- +#define SECTOR_WORDS (512 / 4) /* number of 32bit words per sector */
- +
- +/*
- + * Timeouts for various operations:
- + */
- +#define WAIT_DRQ (5*HZ/100) /* 50msec - spec allows up to 20ms */
- +#ifdef CONFIG_APM
- +#define WAIT_READY (5*HZ) /* 5sec - some laptops are very slow */
- +#else
- +#define WAIT_READY (3*HZ/100) /* 30msec - should be instantaneous */
- +#endif /* CONFIG_APM */
- +#define WAIT_PIDENTIFY (1*HZ) /* 1sec - should be less than 3ms (?) */
- +#define WAIT_WORSTCASE (30*HZ) /* 30sec - worst case when spinning up */
- +#define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */
- +
- +#define SELECT_DRIVE(hwif,drive) OUT_BYTE((drive)->select.all, hwif->io.io_base+hwif->io.io_size * IDE_SELECT_OFFSET);
- +
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- +#include "ide-tape.h"
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- +
- +#ifdef CONFIG_BLK_DEV_IDECD
- +
- +struct atapi_request_sense {
- + unsigned char error_code : 7;
- + unsigned char valid : 1;
- + byte reserved1;
- + unsigned char sense_key : 4;
- + unsigned char reserved2 : 1;
- + unsigned char ili : 1;
- + unsigned char reserved3 : 2;
- + byte info[4];
- + byte sense_len;
- + byte command_info[4];
- + byte asc;
- + byte ascq;
- + byte fru;
- + byte sense_key_specific[3];
- +};
- +
- +struct packet_command {
- + char *buffer;
- + int buflen;
- + int stat;
- + struct atapi_request_sense *sense_data;
- + unsigned char c[12];
- +};
- +
- +
- +/* Structure of a MSF cdrom address. */
- +struct atapi_msf {
- + byte reserved;
- + byte minute;
- + byte second;
- + byte frame;
- +};
- +
- +
- +/* Space to hold the disk TOC. */
- +
- +#define MAX_TRACKS 99
- +struct atapi_toc_header {
- + unsigned short toc_length;
- + byte first_track;
- + byte last_track;
- +};
- +
- +struct atapi_toc_entry {
- + byte reserved1;
- + unsigned control : 4;
- + unsigned adr : 4;
- + byte track;
- + byte reserved2;
- + union {
- + unsigned lba;
- + struct atapi_msf msf;
- + } addr;
- +};
- +
- +struct atapi_toc {
- + int last_session_lba;
- + int xa_flag;
- + unsigned capacity;
- + struct atapi_toc_header hdr;
- + struct atapi_toc_entry ent[MAX_TRACKS+1];
- + /* One extra for the leadout. */
- +};
- +
- +
- +/* This structure is annoyingly close to, but not identical with,
- + the cdrom_subchnl structure from cdrom.h. */
- +struct atapi_cdrom_subchnl
- +{
- + u_char acdsc_reserved;
- + u_char acdsc_audiostatus;
- + u_short acdsc_length;
- + u_char acdsc_format;
- +
- + u_char acdsc_adr: 4;
- + u_char acdsc_ctrl: 4;
- + u_char acdsc_trk;
- + u_char acdsc_ind;
- + union {
- + struct atapi_msf msf;
- + int lba;
- + } acdsc_absaddr;
- + union {
- + struct atapi_msf msf;
- + int lba;
- + } acdsc_reladdr;
- +};
- +
- +
- +/* Extra per-device info for cdrom drives. */
- +struct cdrom_info {
- +
- + /* Buffer for table of contents. NULL if we haven't allocated
- + a TOC buffer for this device yet. */
- +
- + struct atapi_toc *toc;
- +
- + /* Sector buffer. If a read request wants only the first part
- + of a cdrom block, we cache the rest of the block here,
- + in the expectation that that data is going to be wanted soon.
- + SECTOR_BUFFERED is the number of the first buffered sector,
- + and NSECTORS_BUFFERED is the number of sectors in the buffer.
- + Before the buffer is allocated, we should have
- + SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */
- +
- + unsigned long sector_buffered;
- + unsigned long nsectors_buffered;
- + char *sector_buffer;
- +
- + /* The result of the last successful request sense command
- + on this device. */
- + struct atapi_request_sense sense_data;
- +};
- +
- +#endif /* CONFIG_BLK_DEV_IDECD */
- +
- +/*
- + * Now for the data we need to maintain per-drive: ide_drive_t
- + */
- +
- +typedef enum {ide_disk, ide_cdrom, ide_tape} ide_media_t;
- +
- +typedef union {
- + unsigned all : 8; /* all of the bits together */
- + struct {
- + unsigned set_geometry : 1; /* respecify drive geometry */
- + unsigned recalibrate : 1; /* seek to cyl 0 */
- + unsigned set_multmode : 1; /* set multmode count */
- + unsigned set_tune : 1; /* tune interface for drive */
- + unsigned reserved : 4; /* unused */
- + } b;
- + } special_t;
- +
- +typedef union {
- + unsigned all : 8; /* all of the bits together */
- + struct {
- + unsigned head : 4; /* always zeros here */
- + unsigned unit : 1; /* drive select number, 0 or 1 */
- + unsigned bit5 : 1; /* always 1 */
- + unsigned lba : 1; /* using LBA instead of CHS */
- + unsigned bit7 : 1; /* always 1 */
- + } b;
- + } select_t;
- +
- +typedef struct ide_drive_s {
- + special_t special; /* special action flags */
- + unsigned present : 1; /* drive is physically present */
- + unsigned noprobe : 1; /* from: hdx=noprobe */
- + unsigned keep_settings : 1; /* restore settings after drive reset */
- + unsigned busy : 1; /* currently doing revalidate_disk() */
- + unsigned removable : 1; /* 1 if need to do check_media_change */
- + unsigned using_dma : 1; /* disk is using dma for read/write */
- + unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */
- + unsigned unmask : 1; /* flag: okay to unmask other irqs */
- + unsigned no_unmask : 1; /* disallow setting unmask bit */
- + unsigned no_io_32bit : 1; /* disallow enabling 32bit I/O */
- + unsigned nobios : 1; /* flag: do not probe bios for drive */
- + unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */
- +#if FAKE_FDISK_FOR_EZDRIVE
- + unsigned remap_0_to_1 : 1; /* flag: partitioned with ezdrive */
- +#endif /* FAKE_FDISK_FOR_EZDRIVE */
- + ide_media_t media; /* disk, cdrom, tape */
- + select_t select; /* basic drive/head select reg value */
- + byte ctl; /* "normal" value for IDE_CONTROL_REG */
- + byte ready_stat; /* min status value for drive ready */
- + byte mult_count; /* current multiple sector setting */
- + byte mult_req; /* requested multiple sector setting */
- + byte tune_req; /* requested drive pio setting */
- + byte io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
- + byte bad_wstat; /* used for ignoring WRERR_STAT */
- + byte sect0; /* offset of first sector for DM6:DDO */
- + byte usage; /* current "open()" count for drive */
- + byte head; /* "real" number of heads */
- + byte sect; /* "real" sectors per track */
- + byte bios_head; /* BIOS/fdisk/LILO number of heads */
- + byte bios_sect; /* BIOS/fdisk/LILO sectors per track */
- + unsigned short bios_cyl; /* BIOS/fdisk/LILO number of cyls */
- + unsigned short cyl; /* "real" number of cyls */
- + void *hwif; /* actually (ide_hwif_t *) */
- + struct wait_queue *wqueue; /* used to wait for drive in open() */
- + struct hd_driveid *id; /* drive model identification info */
- + struct hd_struct *part; /* drive partition table */
- + char name[4]; /* drive name, such as "hda" */
- +#ifdef CONFIG_BLK_DEV_IDECD
- + struct cdrom_info cdrom_info; /* for ide-cd.c */
- +#endif /* CONFIG_BLK_DEV_IDECD */
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- + idetape_tape_t tape; /* for ide-tape.c */
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- + } ide_drive_t;
- +
- +typedef struct hwreg_s {
- + unsigned long io_base;
- + unsigned long io_size; /* 1 = normal, 16 = ICS ide */
- + unsigned long ctl_port;
- + } hwreg_t;
- +/*
- + * An ide_dmaproc_t() initiates/aborts DMA read/write operations on a drive.
- + *
- + * The caller is assumed to have selected the drive and programmed the drive's
- + * sector address using CHS or LBA. All that remains is to prepare for DMA
- + * and then issue the actual read/write DMA/PIO command to the drive.
- + *
- + * Returns 0 if all went well.
- + * Returns 1 if DMA read/write could not be started, in which case the caller
- + * should either try again later, or revert to PIO for the current request.
- + */
- +typedef enum { ide_dma_read = 0, ide_dma_write = 1,
- + ide_dma_abort = 2, ide_dma_check = 3,
- + ide_dma_status_bad = 4, ide_dma_transferred = 5,
- + ide_dma_begin = 6 }
- + ide_dma_action_t;
- +
- +typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *);
- +
- +
- +/*
- + * An ide_tuneproc_t() is used to set the speed of an IDE interface
- + * to a particular PIO mode. The "byte" parameter is used
- + * to select the PIO mode by number (0,1,2,3,4,5), and a value of 255
- + * indicates that the interface driver should "auto-tune" the PIO mode
- + * according to the drive capabilities in drive->id;
- + *
- + * Not all interface types support tuning, and not all of those
- + * support all possible PIO settings. They may silently ignore
- + * or round values as they see fit.
- + */
- +typedef void (ide_tuneproc_t)(ide_drive_t *, byte);
- +
- +/*
- + * This is used to provide HT6560B & PROMISE interface support.
- + */
- +typedef void (ide_selectproc_t) (ide_drive_t *);
- +
- +/*
- + * hwif_chipset_t is used to keep track of the specific hardware
- + * chipset used by each IDE interface, if known.
- + */
- +typedef enum { ide_unknown, ide_generic, ide_triton,
- + ide_cmd640, ide_dtc2278, ide_ali14xx,
- + ide_qd6580, ide_umc8672, ide_ht6560b,
- + ide_promise }
- + hwif_chipset_t;
- +
- +typedef struct hwif_s {
- + struct hwif_s *next; /* for linked-list in ide_hwgroup_t */
- + void *hwgroup; /* actually (ide_hwgroup_t *) */
- + hwreg_t io;
- + ide_drive_t drives[MAX_DRIVES]; /* drive info */
- + struct gendisk *gd; /* gendisk structure */
- + ide_tuneproc_t *tuneproc; /* routine to tune PIO mode for drives */
- +#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PROMISE)
- + ide_selectproc_t *selectproc; /* tweaks hardware to select drive */
- +#endif
- + ide_dmaproc_t *dmaproc; /* dma read/write/abort routine */
- + unsigned long *dmatable; /* dma physical region descriptor table */
- + unsigned short dma_base; /* base addr for dma ports (triton) */
- + byte irq; /* our irq number */
- + byte major; /* our major number */
- + char name[5]; /* name of interface, eg. "ide0" */
- + byte index; /* 0 for ide0; 1 for ide1; ... */
- + hwif_chipset_t chipset; /* sub-module for tuning.. */
- + unsigned noprobe : 1; /* don't probe for this interface */
- + unsigned present : 1; /* this interface exists */
- + unsigned serialized : 1; /* serialized operation with mate hwif */
- + unsigned sharing_irq: 1; /* 1 = sharing irq with another hwif */
- +#ifdef CONFIG_BLK_DEV_PROMISE
- + unsigned is_promise2: 1; /* 2nd i/f on promise DC4030 */
- +#endif /* CONFIG_BLK_DEV_PROMISE */
- +#if (DISK_RECOVERY_TIME > 0)
- + unsigned long last_time; /* time when previous rq was done */
- +#endif
- +#ifdef CONFIG_BLK_DEV_IDECD
- + struct request request_sense_request; /* from ide-cd.c */
- + struct packet_command request_sense_pc; /* from ide-cd.c */
- +#endif /* CONFIG_BLK_DEV_IDECD */
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- + ide_drive_t *tape_drive; /* Pointer to the tape on this interface */
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- + } ide_hwif_t;
- +
- +/*
- + * internal ide interrupt handler type
- + */
- +typedef void (ide_handler_t)(ide_drive_t *);
- +
- +typedef struct hwgroup_s {
- + ide_handler_t *handler;/* irq handler, if active */
- + ide_drive_t *drive; /* current drive */
- + ide_hwif_t *hwif; /* ptr to current hwif in linked-list */
- + ide_hwif_t *next_hwif; /* next selected hwif (for tape) */
- + struct request *rq; /* current request */
- + struct timer_list timer; /* failsafe timer */
- + struct request wrq; /* local copy of current write rq */
- + unsigned long poll_timeout; /* timeout value during long polls */
- + } ide_hwgroup_t;
- +
- +/*
- + * ide_hwifs[] is the master data structure used to keep track
- + * of just about everything in ide.c. Whenever possible, routines
- + * should be using pointers to a drive (ide_drive_t *) or
- + * pointers to a hwif (ide_hwif_t *), rather than indexing this
- + * structure directly (the allocation/layout may change!).
- + *
- + */
- +#ifndef _IDE_C
- +extern ide_hwif_t ide_hwifs[]; /* master data repository */
- +#endif
- +
- +/*
- + * One final include file, which references some of the data/defns from above
- + */
- +#define IDE_DRIVER /* "parameter" for blk.h */
- +#include <linux/blk.h>
- +
- +#if (DISK_RECOVERY_TIME > 0)
- +void ide_set_recovery_timer (ide_hwif_t *);
- +#define SET_RECOVERY_TIMER(drive) ide_set_recovery_timer (drive)
- +#else
- +#define SET_RECOVERY_TIMER(drive)
- +#endif
- +
- +/*
- + * This is used for (nearly) all data transfers from the IDE interface
- + */
- +void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
- +
- +/*
- + * This is used for (nearly) all data transfers to the IDE interface
- + */
- +void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
- +
- +/*
- + * This is used on exit from the driver, to designate the next irq handler
- + * and also to start the safety timer.
- + */
- +void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout);
- +
- +/*
- + * Error reporting, in human readable form (luxurious, but a memory hog).
- + */
- +byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat);
- +
- +/*
- + * ide_error() takes action based on the error returned by the controller.
- + * The calling function must return afterwards, to restart the request.
- + */
- +void ide_error (ide_drive_t *drive, const char *msg, byte stat);
- +
- +/*
- + * ide_fixstring() cleans up and (optionally) byte-swaps a text string,
- + * removing leading/trailing blanks and compressing internal blanks.
- + * It is primarily used to tidy up the model name/number fields as
- + * returned by the WIN_[P]IDENTIFY commands.
- + */
- +void ide_fixstring (byte *s, const int bytecount, const int byteswap);
- +
- +/*
- + * This routine busy-waits for the drive status to be not "busy".
- + * It then checks the status for all of the "good" bits and none
- + * of the "bad" bits, and if all is okay it returns 0. All other
- + * cases return 1 after invoking ide_error() -- caller should return.
- + *
- + */
- +int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout);
- +
- +/*
- + * This routine is called from the partition-table code in genhd.c
- + * to "convert" a drive to a logical geometry with fewer than 1024 cyls.
- + *
- + * The second parameter, "xparm", determines exactly how the translation
- + * will be handled:
- + * 0 = convert to CHS with fewer than 1024 cyls
- + * using the same method as Ontrack DiskManager.
- + * 1 = same as "0", plus offset everything by 63 sectors.
- + * -1 = similar to "0", plus redirect sector 0 to sector 1.
- + * >1 = convert to a CHS geometry with "xparm" heads.
- + *
- + * Returns 0 if the translation was not possible, if the device was not
- + * an IDE disk drive, or if a geometry was "forced" on the commandline.
- + * Returns 1 if the geometry translation was successful.
- + */
- +int ide_xlate_1024 (kdev_t, int, const char *);
- +
- +/*
- + * Start a reset operation for an IDE interface.
- + * The caller should return immediately after invoking this.
- + */
- +void ide_do_reset (ide_drive_t *);
- +
- +/*
- + * This function is intended to be used prior to invoking ide_do_drive_cmd().
- + */
- +void ide_init_drive_cmd (struct request *rq);
- +
- +/*
- + * "action" parameter type for ide_do_drive_cmd() below.
- + */
- +typedef enum
- + {ide_wait, /* insert rq at end of list, and wait for it */
- + ide_next, /* insert rq immediately after current request */
- + ide_preempt, /* insert rq in front of current request */
- + ide_end} /* insert rq at end of list, but don't wait for it */
- + ide_action_t;
- +
- +/*
- + * This function issues a special IDE device request
- + * onto the request queue.
- + *
- + * If action is ide_wait, then then rq is queued at the end of
- + * the request queue, and the function sleeps until it has been
- + * processed. This is for use when invoked from an ioctl handler.
- + *
- + * If action is ide_preempt, then the rq is queued at the head of
- + * the request queue, displacing the currently-being-processed
- + * request and this function returns immediately without waiting
- + * for the new rq to be completed. This is VERY DANGEROUS, and is
- + * intended for careful use by the ATAPI tape/cdrom driver code.
- + *
- + * If action is ide_next, then the rq is queued immediately after
- + * the currently-being-processed-request (if any), and the function
- + * returns without waiting for the new rq to be completed. As above,
- + * This is VERY DANGEROUS, and is intended for careful use by the
- + * ATAPI tape/cdrom driver code.
- + *
- + * If action is ide_end, then the rq is queued at the end of the
- + * request queue, and the function returns immediately without waiting
- + * for the new rq to be completed. This is again intended for careful
- + * use by the ATAPI tape/cdrom driver code. (Currently used by ide-tape.c,
- + * when operating in the pipelined operation mode).
- + */
- +int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action);
- +
- +/*
- + * Clean up after success/failure of an explicit drive cmd.
- + * stat/err are used only when (HWGROUP(drive)->rq->cmd == IDE_DRIVE_CMD).
- + */
- +void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err);
- +
- +/*
- + * ide_system_bus_speed() returns what we think is the system VESA/PCI
- + * bus speed (in Mhz). This is used for calculating interface PIO timings.
- + * The default is 40 for known PCI systems, 50 otherwise.
- + * The "idebus=xx" parameter can be used to override this value.
- + */
- +int ide_system_bus_speed (void);
- +
- +/*
- + * ide_multwrite() transfers a block of up to mcount sectors of data
- + * to a drive as part of a disk multwrite operation.
- + */
- +void ide_multwrite (ide_drive_t *drive, unsigned int mcount);
- +
- +#ifdef CONFIG_BLK_DEV_IDECD
- +/*
- + * These are routines in ide-cd.c invoked from ide.c
- + */
- +void ide_do_rw_cdrom (ide_drive_t *, unsigned long);
- +int ide_cdrom_ioctl (ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long);
- +int ide_cdrom_check_media_change (ide_drive_t *);
- +int ide_cdrom_open (struct inode *, struct file *, ide_drive_t *);
- +void ide_cdrom_release (struct inode *, struct file *, ide_drive_t *);
- +void ide_cdrom_setup (ide_drive_t *);
- +#endif /* CONFIG_BLK_DEV_IDECD */
- +
- +#ifdef CONFIG_BLK_DEV_IDETAPE
- +
- +/*
- + * Functions in ide-tape.c which are invoked from ide.c:
- + */
- +
- +/*
- + * idetape_identify_device is called during device probing stage to
- + * probe for an ide atapi tape drive and to initialize global variables
- + * in ide-tape.c which provide the link between the character device
- + * and the corresponding block device.
- + *
- + * Returns 1 if an ide tape was detected and is supported.
- + * Returns 0 otherwise.
- + */
- +
- +int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id);
- +
- +/*
- + * idetape_setup is called a bit later than idetape_identify_device,
- + * during the search for disk partitions, to initialize various tape
- + * state variables in ide_drive_t *drive.
- + */
- +
- +void idetape_setup (ide_drive_t *drive);
- +
- +/*
- + * idetape_do_request is our request function. It is called by ide.c
- + * to process a new request.
- + */
- +
- +void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
- +
- +/*
- + * idetape_end_request is used to finish servicing a request, and to
- + * insert a pending pipeline request into the main device queue.
- + */
- +
- +void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
- +
- +/*
- + * Block device interface functions.
- + */
- +
- +int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
- + unsigned int cmd, unsigned long arg);
- +int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
- +void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
- +
- +/*
- + * idetape_register_chrdev initializes the character device interface to
- + * the ide tape drive.
- + */
- +
- +void idetape_register_chrdev (void);
- +
- +#endif /* CONFIG_BLK_DEV_IDETAPE */
- +
- +#ifdef CONFIG_BLK_DEV_TRITON
- +void ide_init_triton (byte, byte);
- +#endif /* CONFIG_BLK_DEV_TRITON */
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/ll_rw_blk.c linux/arch/arm/drivers/block/ll_rw_blk.c
- --- linux.orig/arch/arm/drivers/block/ll_rw_blk.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/ll_rw_blk.c Sat Aug 17 23:24:17 1996
- @@ -0,0 +1,651 @@
- +/*
- + * linux/drivers/block/ll_rw_blk.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + * Copyright (C) 1994, Karl Keyte: Added support for disk statistics
- + */
- +
- +/*
- + * This handles all read/write requests to block devices
- + */
- +#include <linux/sched.h>
- +#include <linux/kernel.h>
- +#include <linux/kernel_stat.h>
- +#include <linux/errno.h>
- +#include <linux/string.h>
- +#include <linux/config.h>
- +#include <linux/locks.h>
- +#include <linux/mm.h>
- +
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include "blk.h"
- +
- +extern int adfsimg_map (kdev_t *rdev, unsigned long *rsector, unsigned long size);
- +
- +/*
- + * The request-struct contains all necessary data
- + * to load a nr of sectors into memory
- + */
- +static struct request all_requests[NR_REQUEST];
- +
- +/*
- + * The "disk" task queue is used to start the actual requests
- + * after a plug
- + */
- +DECLARE_TASK_QUEUE(tq_disk);
- +
- +/*
- + * used to wait on when there are no free requests
- + */
- +struct wait_queue * wait_for_request;
- +
- +/* This specifies how many sectors to read ahead on the disk. */
- +
- +int read_ahead[MAX_BLKDEV];
- +
- +/* blk_dev_struct is:
- + * *request_fn
- + * *current_request
- + */
- +struct blk_dev_struct blk_dev[MAX_BLKDEV]; /* initialized by blk_dev_init() */
- +
- +/*
- + * blk_size contains the size of all block-devices in units of 1024 byte
- + * sectors:
- + *
- + * blk_size[MAJOR][MINOR]
- + *
- + * if (!blk_size[MAJOR]) then no minor size checking is done.
- + */
- +int * blk_size[MAX_BLKDEV];
- +
- +/*
- + * blksize_size contains the size of all block-devices:
- + *
- + * blksize_size[MAJOR][MINOR]
- + *
- + * if (!blksize_size[MAJOR]) then 1024 bytes is assumed.
- + */
- +int * blksize_size[MAX_BLKDEV];
- +
- +/*
- + * hardsect_size contains the size of the hardware sector of a device.
- + *
- + * hardsect_size[MAJOR][MINOR]
- + *
- + * if (!hardsect_size[MAJOR])
- + * then 512 bytes is assumed.
- + * else
- + * sector_size is hardsect_size[MAJOR][MINOR]
- + * This is currently set by some scsi device and read by the msdos fs driver
- + * This might be a some uses later.
- + */
- +int * hardsect_size[MAX_BLKDEV];
- +
- +/*
- + * remove the plug and let it rip..
- + */
- +void unplug_device(void * data)
- +{
- + struct blk_dev_struct * dev = (struct blk_dev_struct *) data;
- + unsigned long flags;
- +
- + save_flags_cli(flags);
- + if (dev->current_request == &dev->plug) {
- + struct request * next = dev->plug.next;
- + dev->current_request = next;
- + if (next) {
- + dev->plug.next = NULL;
- + (dev->request_fn)();
- + }
- + }
- + restore_flags(flags);
- +}
- +
- +/*
- + * "plug" the device if there are no outstanding requests: this will
- + * force the transfer to start only after we have put all the requests
- + * on the list.
- + *
- + * This is called with interrupts off and no requests on the queue.
- + */
- +static inline void plug_device(struct blk_dev_struct * dev)
- +{
- + dev->current_request = &dev->plug;
- + queue_task_irq_off(&dev->plug_tq, &tq_disk);
- +}
- +
- +/*
- + * look for a free request in the first N entries.
- + * NOTE: interrupts must be disabled on the way in, and will still
- + * be disabled on the way out.
- + */
- +static inline struct request * get_request(int n, kdev_t dev)
- +{
- + static struct request *prev_found = NULL, *prev_limit = NULL;
- + register struct request *req, *limit;
- +
- + if (n <= 0)
- + panic("get_request(%d): impossible!\n", n);
- +
- + limit = all_requests + n;
- + if (limit != prev_limit) {
- + prev_limit = limit;
- + prev_found = all_requests;
- + }
- + req = prev_found;
- + for (;;) {
- + req = ((req > all_requests) ? req : limit) - 1;
- + if (req->rq_status == RQ_INACTIVE)
- + break;
- + if (req == prev_found)
- + return NULL;
- + }
- + prev_found = req;
- + req->rq_status = RQ_ACTIVE;
- + req->rq_dev = dev;
- + return req;
- +}
- +
- +/*
- + * wait until a free request in the first N entries is available.
- + */
- +static struct request * __get_request_wait(int n, kdev_t dev)
- +{
- + register struct request *req;
- + struct wait_queue wait = { current, NULL };
- +
- + add_wait_queue(&wait_for_request, &wait);
- + for (;;) {
- + current->state = TASK_UNINTERRUPTIBLE;
- + cli();
- + req = get_request(n, dev);
- + sti();
- + if (req)
- + break;
- + run_task_queue(&tq_disk);
- + schedule();
- + }
- + remove_wait_queue(&wait_for_request, &wait);
- + current->state = TASK_RUNNING;
- + return req;
- +}
- +
- +static inline struct request * get_request_wait(int n, kdev_t dev)
- +{
- + register struct request *req;
- +
- + cli();
- + req = get_request(n, dev);
- + sti();
- + if (req)
- + return req;
- + return __get_request_wait(n, dev);
- +}
- +
- +/* RO fail safe mechanism */
- +
- +static long ro_bits[MAX_BLKDEV][8];
- +
- +int is_read_only(kdev_t dev)
- +{
- + int minor,major;
- +
- + major = MAJOR(dev);
- + minor = MINOR(dev);
- + if (major < 0 || major >= MAX_BLKDEV) return 0;
- + return ro_bits[major][minor >> 5] & (1 << (minor & 31));
- +}
- +
- +void set_device_ro(kdev_t dev,int flag)
- +{
- + int minor,major;
- +
- + major = MAJOR(dev);
- + minor = MINOR(dev);
- + if (major < 0 || major >= MAX_BLKDEV) return;
- + if (flag) ro_bits[major][minor >> 5] |= 1 << (minor & 31);
- + else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
- +}
- +
- +static inline void drive_stat_acct(int cmd, unsigned long nr_sectors,
- + short disk_index)
- +{
- + kstat.dk_drive[disk_index]++;
- + if (cmd == READ) {
- + kstat.dk_drive_rio[disk_index]++;
- + kstat.dk_drive_rblk[disk_index] += nr_sectors;
- + } else if (cmd == WRITE) {
- + kstat.dk_drive_wio[disk_index]++;
- + kstat.dk_drive_wblk[disk_index] += nr_sectors;
- + } else
- + printk(KERN_ERR "drive_stat_acct: cmd not R/W?\n");
- +}
- +
- +/*
- + * add-request adds a request to the linked list.
- + * It disables interrupts so that it can muck with the
- + * request-lists in peace.
- + *
- + * By this point, req->cmd is always either READ/WRITE, never READA/WRITEA,
- + * which is important for drive_stat_acct() above.
- + */
- +
- +void add_request(struct blk_dev_struct * dev, struct request * req)
- +{
- + struct request * tmp;
- + short disk_index;
- +
- + switch (MAJOR(req->rq_dev)) {
- + case SCSI_DISK_MAJOR:
- + disk_index = (MINOR(req->rq_dev) & 0x0070) >> 4;
- + if (disk_index < 4)
- + drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
- + break;
- + case IDE0_MAJOR: /* same as HD_MAJOR */
- + case XT_DISK_MAJOR:
- + disk_index = (MINOR(req->rq_dev) & 0x0040) >> 6;
- + drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
- + break;
- + case IDE1_MAJOR:
- + disk_index = ((MINOR(req->rq_dev) & 0x0040) >> 6) + 2;
- + drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
- + default:
- + break;
- + }
- +
- + req->next = NULL;
- + cli();
- + if (req->bh)
- + mark_buffer_clean(req->bh);
- + if (!(tmp = dev->current_request)) {
- + dev->current_request = req;
- + (dev->request_fn)();
- + sti();
- + return;
- + }
- + for ( ; tmp->next ; tmp = tmp->next) {
- + if ((IN_ORDER(tmp,req) ||
- + !IN_ORDER(tmp,tmp->next)) &&
- + IN_ORDER(req,tmp->next))
- + break;
- + }
- + req->next = tmp->next;
- + tmp->next = req;
- +
- +/* for SCSI devices, call request_fn unconditionally */
- + if (scsi_blk_major(MAJOR(req->rq_dev)))
- + (dev->request_fn)();
- +
- + sti();
- +}
- +
- +static void make_request(int major,int rw, struct buffer_head * bh)
- +{
- + unsigned int sector, count;
- + struct request * req;
- + int rw_ahead, max_req;
- +
- + count = bh->b_size >> 9;
- + sector = bh->b_rsector;
- +
- + /* Uhhuh.. Nasty dead-lock possible here.. */
- + if (buffer_locked(bh))
- + return;
- + /* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */
- +
- + lock_buffer(bh);
- +
- + if (blk_size[major])
- + if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) {
- + bh->b_state &= (1 << BH_Lock) | (1 << BH_FreeOnIO);
- + /* This may well happen - the kernel calls bread()
- + without checking the size of the device, e.g.,
- + when mounting a device. */
- + printk(KERN_INFO
- + "attempt to access beyond end of device\n");
- + printk(KERN_INFO "%s: rw=%d, want=%d, limit=%d\n",
- + kdevname(bh->b_rdev), rw,
- + (sector + count)>>1,
- + blk_size[major][MINOR(bh->b_rdev)]);
- + unlock_buffer(bh);
- + return;
- + }
- +
- + rw_ahead = 0; /* normal case; gets changed below for READA/WRITEA */
- + switch (rw) {
- + case READA:
- + rw_ahead = 1;
- + rw = READ; /* drop into READ */
- + case READ:
- + if (buffer_uptodate(bh)) {
- + unlock_buffer(bh); /* Hmmph! Already have it */
- + return;
- + }
- + kstat.pgpgin++;
- + max_req = NR_REQUEST; /* reads take precedence */
- + break;
- + case WRITEA:
- + rw_ahead = 1;
- + rw = WRITE; /* drop into WRITE */
- + case WRITE:
- + if (!buffer_dirty(bh)) {
- + unlock_buffer(bh); /* Hmmph! Nothing to write */
- + return;
- + }
- + /* We don't allow the write-requests to fill up the
- + * queue completely: we want some room for reads,
- + * as they take precedence. The last third of the
- + * requests are only for reads.
- + */
- + kstat.pgpgout++;
- + max_req = (NR_REQUEST * 2) / 3;
- + break;
- + default:
- + printk(KERN_ERR "make_request: bad block dev cmd,"
- + " must be R/W/RA/WA\n");
- + unlock_buffer(bh);
- + return;
- + }
- + /* look for a free request. */
- + if (major == ADFSIMG_MAJOR)
- + max_req = (max_req * 2) / 3;
- + /*
- + * Try to coalesce the new request with old requests
- + */
- + cli();
- + req = blk_dev[major].current_request;
- + if (!req) {
- + /* MD and loop can't handle plugging without deadlocking */
- + if (major != MD_MAJOR && major != LOOP_MAJOR && major != ADFSIMG_MAJOR)
- + plug_device(blk_dev + major);
- + } else switch (major) {
- + case IDE0_MAJOR: /* same as HD_MAJOR */
- + case IDE1_MAJOR:
- + case FLOPPY_MAJOR:
- + case IDE2_MAJOR:
- + case IDE3_MAJOR:
- + case XT_DISK_MAJOR:
- + case ADFSIMG_MAJOR:
- + /*
- + * The scsi disk and cdrom drivers completely remove the request
- + * from the queue when they start processing an entry. For this
- + * reason it is safe to continue to add links to the top entry for
- + * those devices.
- + *
- + * All other drivers need to jump over the first entry, as that
- + * entry may be busy being processed and we thus can't change it.
- + */
- + req = req->next;
- + if (!req)
- + break;
- + /* fall through */
- +
- + case SCSI_DISK_MAJOR:
- + case SCSI_CDROM_MAJOR:
- +
- + do {
- + if (req->sem)
- + continue;
- + if (req->cmd != rw)
- + continue;
- + if (req->nr_sectors >= 244)
- + continue;
- + if (req->rq_dev != bh->b_rdev)
- + continue;
- + /* Can we add it to the end of this request? */
- + if (req->sector + req->nr_sectors == sector) {
- + req->bhtail->b_reqnext = bh;
- + req->bhtail = bh;
- + /* or to the beginning? */
- + } else if (req->sector - count == sector) {
- + bh->b_reqnext = req->bh;
- + req->bh = bh;
- + req->buffer = bh->b_data;
- + req->current_nr_sectors = count;
- + req->sector = sector;
- + } else
- + continue;
- +
- + req->nr_sectors += count;
- + mark_buffer_clean(bh);
- + sti();
- + return;
- + } while ((req = req->next) != NULL);
- + }
- +
- +/* find an unused request. */
- + req = get_request(max_req, bh->b_rdev);
- + sti();
- +
- +/* if no request available: if rw_ahead, forget it; otherwise try again blocking.. */
- + if (!req) {
- + if (rw_ahead) {
- + unlock_buffer(bh);
- + return;
- + }
- + req = __get_request_wait(max_req, bh->b_rdev);
- + }
- +
- +/* fill up the request-info, and add it to the queue */
- + req->cmd = rw;
- + req->errors = 0;
- + req->sector = sector;
- + req->nr_sectors = count;
- + req->current_nr_sectors = count;
- + req->buffer = bh->b_data;
- + req->sem = NULL;
- + req->bh = bh;
- + req->bhtail = bh;
- + req->next = NULL;
- + add_request(major+blk_dev,req);
- +}
- +
- +/* This function can be used to request a number of buffers from a block
- + device. Currently the only restriction is that all buffers must belong to
- + the same device */
- +
- +void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
- +{
- + unsigned int major;
- + int correct_size;
- + struct blk_dev_struct * dev;
- + int i;
- +
- + /* Make sure that the first block contains something reasonable */
- + while (!*bh) {
- + bh++;
- + if (--nr <= 0)
- + return;
- + }
- +
- + dev = NULL;
- + if ((major = MAJOR(bh[0]->b_dev)) < MAX_BLKDEV)
- + dev = blk_dev + major;
- + if (!dev || !dev->request_fn) {
- + printk(KERN_ERR
- + "ll_rw_block: Trying to read nonexistent block-device %s (%ld)\n",
- + kdevname(bh[0]->b_dev), bh[0]->b_blocknr);
- + goto sorry;
- + }
- +
- + /* Determine correct block size for this device. */
- + correct_size = BLOCK_SIZE;
- + if (blksize_size[major]) {
- + i = blksize_size[major][MINOR(bh[0]->b_dev)];
- + if (i)
- + correct_size = i;
- + }
- +
- + /* Verify requested block sizes. */
- + for (i = 0; i < nr; i++) {
- + if (bh[i] && 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),
- + correct_size, bh[i]->b_size);
- + goto sorry;
- + }
- +
- + /* Md remaps blocks now */
- + bh[i]->b_rdev = bh[i]->b_dev;
- + bh[i]->b_rsector=bh[i]->b_blocknr*(bh[i]->b_size >> 9);
- +#ifdef CONFIG_BLK_DEV_MD
- + if (major==MD_MAJOR &&
- + md_map (MINOR(bh[i]->b_dev), &bh[i]->b_rdev,
- + &bh[i]->b_rsector, bh[i]->b_size >> 9)) {
- + printk (KERN_ERR
- + "Bad md_map in ll_rw_block\n");
- + goto sorry;
- + }
- +#endif
- + }
- +
- + if ((rw == WRITE || rw == WRITEA) && is_read_only(bh[0]->b_dev)) {
- + printk(KERN_NOTICE "Can't write to read-only device %s\n",
- + kdevname(bh[0]->b_dev));
- + goto sorry;
- + }
- +
- + for (i = 0; i < nr; i++) {
- + if (bh[i]) {
- + set_bit(BH_Req, &bh[i]->b_state);
- +
- + make_request(MAJOR(bh[i]->b_rdev), rw, bh[i]);
- + }
- + }
- + return;
- +
- + sorry:
- + for (i = 0; i < nr; i++) {
- + if (bh[i]) {
- + clear_bit(BH_Dirty, &bh[i]->b_state);
- + clear_bit(BH_Uptodate, &bh[i]->b_state);
- + }
- + }
- + return;
- +}
- +
- +void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buf)
- +{
- + int i, j;
- + int buffersize;
- + unsigned long rsector;
- + kdev_t rdev;
- + struct request * req[8];
- + unsigned int major = MAJOR(dev);
- + struct semaphore sem = MUTEX_LOCKED;
- +
- + if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
- + printk(KERN_NOTICE "ll_rw_swap_file: trying to swap to"
- + " nonexistent block-device\n");
- + return;
- + }
- + switch (rw) {
- + case READ:
- + break;
- + case WRITE:
- + if (is_read_only(dev)) {
- + printk(KERN_NOTICE
- + "Can't swap to read-only device %s\n",
- + kdevname(dev));
- + return;
- + }
- + break;
- + default:
- + panic("ll_rw_swap: bad block dev cmd, must be R/W");
- + }
- + buffersize = PAGE_SIZE / nb;
- +
- + for (j=0, i=0; i<nb;)
- + {
- + for (; j < 8 && i < nb; j++, i++, buf += buffersize)
- + {
- + rdev = dev;
- + rsector = (b[i] * buffersize) >> 9;
- +#ifdef CONFIG_BLK_DEV_MD
- + if (major==MD_MAJOR &&
- + md_map (MINOR(dev), &rdev,
- + &rsector, buffersize >> 9)) {
- + printk (KERN_ERR
- + "Bad md_map in ll_rw_swap_file\n");
- + return;
- + }
- +#endif
- + if (j == 0) {
- + req[j] = get_request_wait(NR_REQUEST, rdev);
- + } else {
- + cli();
- + req[j] = get_request(NR_REQUEST, rdev);
- + sti();
- + if (req[j] == NULL)
- + break;
- + }
- + req[j]->cmd = rw;
- + req[j]->errors = 0;
- + req[j]->sector = rsector;
- + req[j]->nr_sectors = buffersize >> 9;
- + req[j]->current_nr_sectors = buffersize >> 9;
- + req[j]->buffer = buf;
- + req[j]->sem = &sem;
- + req[j]->bh = NULL;
- + req[j]->next = NULL;
- + add_request(MAJOR(rdev)+blk_dev,req[j]);
- + }
- + run_task_queue(&tq_disk);
- + while (j > 0) {
- + j--;
- + down(&sem);
- + }
- + }
- +}
- +
- +int blk_dev_init(void)
- +{
- + struct request * req;
- + struct blk_dev_struct *dev;
- +
- + for (dev = blk_dev + MAX_BLKDEV; dev-- != blk_dev;) {
- + dev->request_fn = NULL;
- + dev->current_request = NULL;
- + dev->plug.rq_status = RQ_INACTIVE;
- + dev->plug.cmd = -1;
- + dev->plug.next = NULL;
- + dev->plug_tq.routine = &unplug_device;
- + dev->plug_tq.data = dev;
- + }
- +
- + req = all_requests + NR_REQUEST;
- + while (--req >= all_requests) {
- + req->rq_status = RQ_INACTIVE;
- + req->next = NULL;
- + }
- + memset(ro_bits,0,sizeof(ro_bits));
- +#ifdef CONFIG_BLK_DEV_RAM
- + rd_init();
- +#endif
- +#ifdef CONFIG_BLK_DEV_LOOP
- + loop_init();
- +#endif
- +#ifdef CONFIG_BLK_DEV_IDE
- + ide_init(); /* this MUST precede hd_init */
- +#endif
- +#ifdef CONFIG_BLK_DEV_HD
- + hd_init();
- +#endif
- +#ifdef CONFIG_BLK_DEV_XD
- + xd_init();
- +#endif
- +#ifdef CONFIG_BLK_DEV_FD
- + floppy_init();
- +#else
- +#ifndef CONFIG_ARCH_ARC
- + outb_p(0xc, 0x3f2);
- +#endif
- +#endif
- +#ifdef CONFIG_BLK_DEV_MD
- + md_init();
- +#endif CONFIG_BLK_DEV_MD
- + return 0;
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/mfm.S linux/arch/arm/drivers/block/mfm.S
- --- linux.orig/arch/arm/drivers/block/mfm.S Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/mfm.S Sat Sep 7 21:09:58 1996
- @@ -0,0 +1,159 @@
- +@ 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
- +
- +#include <asm/assembler.h>
- +
- +_hdc63463_irqdata:
- +@ Controller base address
- + .global _hdc63463_baseaddress
- +_hdc63463_baseaddress:
- + .word 0
- +
- + .global _hdc63463_irqpolladdress
- +_hdc63463_irqpolladdress:
- + .word 0
- +
- + .global _hdc63463_irqpollmask
- +_hdc63463_irqpollmask:
- + .word 0
- +
- +@ where to read/write data from the kernel data space
- + .global _hdc63463_dataptr
- +_hdc63463_dataptr:
- + .word 0
- +
- +@ Number of bytes left to transfer
- + .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:
- + stmfd sp!,{r4-r7}
- + adr r5,_hdc63463_irqdata
- + ldmia r5,{r0,r1,r2,r3,r4}
- +
- +
- +writedma_again:
- +
- + @ test number of remaining bytes to transfer
- + cmp r4,#0
- + beq writedma_end
- + bmi writedma_end
- +
- + @ Check the hdc is interrupting
- + ldrb r5,[r1,#0]
- + tst r5,r2
- + beq writedma_end
- +
- + @ Transfer a block of upto 256 bytes
- + cmp r4,#256
- + movlt r7,r4
- + movge r7,#256
- +
- + @ Check the hdc is still busy and command has not ended and no errors
- + ldr r5,[r0,#32] @ Status reg - 16 bit - its the top few bits which are status
- + tst r5,#0x3c00 @ Test for things which should be off
- + bne writedma_end
- + and r5,r5,#0x8000 @ This is test for things which should be on: Busy
- + cmp r5,#0x8000
- + bne writedma_end
- +
- + @ Bytes remaining at end
- + sub r4,r4,r7
- +
- + @ HDC Write register location
- + add r0,r0,#32+8
- +
- +writedma_loop:
- + @ OK - pretty sure we should be doing this
- +
- + ldr r5,[r3],#4 @ Get a word to be written
- + @ get bottom half to be sent first
- + mov r6,r5,lsl#16 @ Separate the first 2 bytes
- + orr r2,r6,r6,lsr #16 @ Duplicate them in the bottom half of the word
- + @ now the top half
- + mov r6,r5,lsr#16 @ Get 2nd 2 bytes
- + orr r6,r6,r6,lsl#16 @ Duplicate
- + @str r6,[r0] @ to hdc
- + stmia r0,{r2,r6}
- + subs r7,r7,#4 @ Dec. number of bytes left
- + bne writedma_loop
- +
- + @ 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
- + @ ldr r2,[r2,#8]
- + @ b writedma_again
- +
- +writedma_end:
- + adr r5,_hdc63463_irqdata+12
- + stmia r5,{r3,r4}
- + ldmfd sp!,{r4-r7}
- + RETINSTR(mov,pc,lr)
- +
- +@ -------------------------------------------------------------------------
- +@ 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:
- + stmfd sp!,{r4-r7}
- + adr r5,_hdc63463_irqdata
- + ldmia r5,{r0,r1,r2,r3,r4}
- +
- +readdma_again:
- + @ test number of remaining bytes to transfer
- + cmp r4,#0
- + beq readdma_end
- + bmi readdma_end
- +
- + @ Check the hdc is interrupting
- + ldrb r5,[r1,#0]
- + tst r5,r2
- + beq readdma_end
- +
- + @ Check the hdc is still busy and command has not ended and no errors
- + ldr r5,[r0,#32] @ Status reg - 16 bit - its the top few bits which are status
- + tst r5,#0x3c00 @ Test for things which should be off
- + bne readdma_end
- + and r5,r5,#0x8000 @ This is test for things which should be on: Busy
- + cmp r5,#0x8000
- + bne readdma_end
- +
- + @ Transfer a block of upto 256 bytes
- + cmp r4,#256
- + movlt r7,r4
- + movge r7,#256
- +
- + @ Bytes remaining at end
- + sub r4,r4,r7
- +
- + @ Set a pointer to the data register in the HDC
- + add r0,r0,#8
- +readdma_loop:
- + @ OK - pretty sure we should be doing this
- + ldmia r0,{r5,r6}
- + mov r5,r5,lsl#16
- + mov r6,r6,lsl#16
- + orr r6,r6,r5,lsr #16
- + str r6,[r3],#4
- + subs r7,r7,#4 @ Decrement bytes to go
- + bne readdma_loop
- +
- + @ Try reading multiple blocks - if this was fast enough then I dont think
- + @ this should help - NO taken out DAG - new interrupt handler has
- + @ non-consecutive memory blocks
- + @ sub r0,r0,#8
- + @ b readdma_again
- +
- +readdma_end:
- + adr r5,_hdc63463_irqdata+12
- + stmia r5,{r3,r4}
- + ldmfd sp!,{r4-r7}
- + RETINSTR(mov,pc,lr)
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/block/mfmhd.c linux/arch/arm/drivers/block/mfmhd.c
- --- linux.orig/arch/arm/drivers/block/mfmhd.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/block/mfmhd.c Sat Sep 7 21:09:09 1996
- @@ -0,0 +1,1379 @@
- +/*
- + * linux/arch/arm/drivers/block/mfmhd.c
- + *
- + * Copyright (C) 1995, 1996 Russell King, Dave Alan Gilbert (gilbertd@cs.man.ac.uk)
- + *
- + * MFM hard drive code [experimental]
- + */
- +
- +/*
- + * Change list:
- + *
- + * 3/2/96:DAG: Started a change list :-)
- + * Set the hardsect_size pointers up since we are running 256 byte
- + * sectors
- + * Added DMA code, put it into the rw_intr
- + * Moved RCAL out of generic interrupt code - don't want to do it
- + * while DMA'ing - its now in individual handlers.
- + * Took interrupt handlers off task queue lists and called
- + * directly - not sure of implications.
- + *
- + * 18/2/96:DAG: Well its reading OK I think, well enough for image file code
- + * to find the image file; but now I've discovered that I actually
- + * have to put some code in for image files.
- + *
- + * Added stuff for image files; seems to work, but I've not
- + * got a multisegment image file (I don't think!).
- + * Put in a hack (yep a real hack) for multiple cylinder reads.
- + * Not convinced its working.
- + *
- + * 5/4/96:DAG: Added asm/hardware.h and use IOC_ macros
- + * Rewrote dma code in mfm.S (again!) - now takes a word at a time
- + * from main RAM for speed; still doesn't feel speedy!
- + *
- + * 20/4/96:DAG: After rewriting mfm.S a heck of a lot of times and speeding
- + * things up, I've finally figured out why its so damn slow.
- + * Linux is only reading a block at a time, and so you never
- + * get more than 1K per disc revoloution ~=60K/second.
- + *
- + * 27/4/96:DAG: On Russell's advice I change ll_rw_blk.c to ask it to
- + * join adjacent blocks together. Everything falls flat on its
- + * face.
- + * Four hours of debugging later; I hadn't realised that
- + * ll_rw_blk would be so generous as to join blocks whose
- + * results aren't going into consecutive buffers.
- + *
- + * OK; severe rehacking of mfm_rw_interrupt; now end_request's
- + * as soon as its DMA'd each request. Odd thing is that
- + * we are sometimes getting interrupts where we are not transferring
- + * any data; why? Is that what happens when you miss? I doubt
- + * it; are we too fast? No - its just at command ends. Got 240K/s
- + * better than before, but RiscOS hits 480K/s
- + *
- + * 25/6/96:RMK: Fixed init code to allow the MFM podule to work. Increased the
- + * number of errors for my Miniscribe drive (8425).
- + *
- + * 30/6/96:DAG: Russell suggested that a check drive 0 might turn the LEDs off
- + * - so in request_done just before it clears Busy it sends a
- + * check drive 0 - and the LEDs go off!!!!
- + *
- + * Added test for mainboard controller. - Removes need for separate
- + * define.
- + *
- + * 13/7/96:DAG: Changed hardware sectore size to 512 in attempt to make
- + * IM drivers work.
- + * 21/7/96:DAG: Took out old image file stuff (accessing it now produces an IO
- + * error.)
- + *
- + * 17/8/96:DAG: Ran through indent -kr -i8; evil - all my nice 2 character indents
- + * gone :-( Hand modified afterwards.
- + * Took out last remains of the older image map system.
- + */
- +
- +#ifdef MODULE
- +#include <linux/module.h>
- +#include <linux/version.h>
- +#endif
- +
- +#include <linux/config.h>
- +#include <linux/sched.h>
- +#include <linux/fs.h>
- +#include <linux/interrupt.h>
- +#include <linux/kernel.h>
- +#include <linux/timer.h>
- +#include <linux/tqueue.h>
- +#include <linux/mm.h>
- +
- +#include <linux/errno.h>
- +#include <linux/genhd.h>
- +#include <linux/major.h>
- +#include <linux/xd.h>
- +
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <asm/irq-no.h>
- +#include <asm/segment.h>
- +#include <asm/delay.h>
- +#include <asm/dma.h>
- +#include <asm/hardware.h>
- +
- +#include <asm/ecard.h>
- +
- +static int lastspecifieddrive;
- +static unsigned Busy;
- +
- +static unsigned int PartFragRead; /* The number of sectors which have been read
- + during a partial read split over two
- + cylinders. If 0 it means a partial
- + read did not occur. */
- +
- +static unsigned int PartFragRead_RestartBlock; /* Where to restart on a split access */
- +static unsigned int PartFragRead_SectorsLeft; /* Where to restart on a split access */
- +
- +static int Sectors256LeftInCurrent; /* i.e. 256 byte sectors left in current */
- +static int SectorsLeftInRequest; /* i.e. blocks left in the thing mfm_request was called for */
- +static int Copy_Sector; /* The 256 byte sector we are currently at - fragments need to know
- + where to take over */
- +static char *Copy_buffer;
- +#define MFM_DISK_MAJOR 13
- +#undef XT_DISK_MAJOR
- +#define XT_DISK_MAJOR -1
- +#define MAJOR_NR MFM_DISK_MAJOR
- +#include "blk.h"
- +
- +XD_INFO mfm_info[XD_MAXDRIVES];
- +
- +/*#define DEBUG */
- +
- +#define MFM_COMMAND (mfm_addr + 0)
- +#define MFM_DATAOUT (mfm_addr + 1)
- +#define MFM_STATUS (mfm_addr + 8)
- +#define MFM_DATAIN (mfm_addr + 9)
- +
- +static void mfm_geninit(struct gendisk *dev);
- +static int mfm_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg);
- +static int mfm_open(struct inode *inode, struct file *file);
- +static void mfm_release(struct inode *inode, struct file *file);
- +static void mfm_interrupt_handler(int, struct pt_regs *);
- +static void mfm_seek(void);
- +static void mfm_unexpected_interrupt(void);
- +static void mfm_rerequest(void);
- +static void mfm_request(void);
- +static int mfm_reread_partitions(int dev);
- +static void mfm_specify(void);
- +
- +static void issue_request(int dev, unsigned int block, unsigned int nsect,
- + struct request *req);
- +#define mfm_init xd_init
- +#define mfm_setup xd_setup
- +
- +static struct hd_struct mfm[XD_MAXDRIVES << 6];
- +static int mfm_sizes[XD_MAXDRIVES << 6], mfm_access[XD_MAXDRIVES] =
- +{0, 0};
- +static int mfm_blocksizes[XD_MAXDRIVES << 6];
- +static unsigned char mfm_valid[XD_MAXDRIVES] =
- +{-1,};
- +static struct wait_queue *mfm_wait_open = NULL;
- +
- +static int mfm_irq; /* Interrupt number */
- +static int mfm_addr; /* Controller address */
- +static unsigned char *mfm_IRQPollLoc; /* Address to read for IRQ information */
- +static char mfm_IRQMask; /* AND with *mfm_IRQPollLoc to find if there is an interrupt */
- +static int mfm_drives = 0; /* drives available */
- +static int mfm_status = 0; /* interrupt status */
- +static int *errors;
- +
- +static struct rawcmd {
- + unsigned int flags;
- + unsigned int dev;
- + unsigned int cylinder;
- + unsigned int head;
- + unsigned int sector;
- + unsigned int cmdtype;
- + unsigned int cmdcode;
- + unsigned char cmddata[16];
- + unsigned int cmdlen;
- +} raw_cmd;
- +
- +#define NEED_SEEK 1
- +unsigned char result[16];
- +unsigned char resultlen;
- +
- +static struct cont {
- + void (*interrupt) (void); /* interrupt handler */
- + void (*error) (void); /* error handler */
- + void (*redo) (void); /* redo handler */
- + void (*done) (int st); /* done handler */
- +} *cont = NULL;
- +
- +static struct drvparam {
- + int cylinder;
- + unsigned int nocylinders;
- + unsigned int lowcurrentcylinder;
- + unsigned int precompcylinder;
- + unsigned char noheads;
- + unsigned char nosectors;
- + struct {
- + char recal;
- + char report;
- + char abort;
- + } errors;
- +} mfm_drive_param[8] = {
- + { -1, 616, 616, 129, 4, 32, { 0, 0, 4 } },
- + { -1, 616, 616, 129, 4, 32, { 0, 0, 4 } },
- + { -1, 616, 616, 129, 4, 32, { 0, 0, 4 } },
- + { -1, 616, 616, 129, 4, 32, { 0, 0, 4 } },
- + { -1, 616, 616, 129, 4, 32, { 0, 0, 4 } },
- + { -1, 616, 616, 129, 4, 32, { 0, 0, 4 } },
- + { -1, 616, 616, 129, 4, 32, { 0, 0, 4 } },
- + { -1, 616, 616, 129, 4, 32, { 0, 0, 4 } }
- +};
- +
- +#define MFM_DRV_PARAM mfm_drive_param[raw_cmd.dev]
- +
- +struct tq_struct mfm_tq =
- +{0, 0, (void (*)(void *)) mfm_unexpected_interrupt, 0};
- +
- +#define NO_TRACK -1
- +#define NEED_1_RECAL -2
- +#define NEED_2_RECAL -3
- +
- +int mfm_sectsizes[256]; /* We have to do this because we don't have 512 byte sectors */
- +
- +/* Stuff from the assembly routines */
- +extern unsigned int hdc63463_baseaddress; /* Controller base address */
- +extern unsigned int hdc63463_irqpolladdress; /* Address to read to test for int */
- +extern unsigned int hdc63463_irqpollmask; /* Mask for irq register */
- +extern unsigned int hdc63463_dataptr; /* Pointer to kernel data space to DMA */
- +extern int hdc63463_dataleft; /* Number of bytes left to transfer */
- +
- +#if 0
- +extern int number_mfm_drives; /* in setup.c */
- +#else
- +int number_mfm_drives = 1;
- +#endif
- +/* ------------------------------------------------------------------------------------------ */
- +/*
- + * From the HD63463 data sheet from Hitachi Ltd.
- + */
- +
- +#define CMD_ABT 0xF0 /* Abort */
- +#define CMD_SPC 0xE8 /* Specify */
- +#define CMD_TST 0xE0 /* Test */
- +#define CMD_RCLB 0xC8 /* Recalibrate */
- +#define CMD_SEK 0xC0 /* Seek */
- +#define CMD_WFS 0xAB /* Write Format Skew */
- +#define CMD_WFM 0xA3 /* Write Format */
- +#define CMD_MTB 0x90 /* Memory to buffer */
- +#define CMD_CMPD 0x88 /* Compare data */
- +#define CMD_WD 0x87 /* Write data */
- +#define CMD_RED 0x70 /* Read erroneous data */
- +#define CMD_RIS 0x68 /* Read ID skew */
- +#define CMD_FID 0x61 /* Find ID */
- +#define CMD_RID 0x60 /* Read ID */
- +#define CMD_BTM 0x50 /* Buffer to memory */
- +#define CMD_CKD 0x48 /* Check data */
- +#define CMD_RD 0x40 /* Read data */
- +#define CMD_OPBW 0x38 /* Open buffer write */
- +#define CMD_OPBR 0x30 /* Open buffer read */
- +#define CMD_CKV 0x28 /* Check drive */
- +#define CMD_CKE 0x20 /* Check ECC */
- +#define CMD_POD 0x18 /* Polling disable */
- +#define CMD_POL 0x10 /* Polling enable */
- +#define CMD_RCAL 0x08 /* Recall */
- +
- +#define STAT_BSY 0x8000 /* Busy */
- +#define STAT_CPR 0x4000 /* Command Parameter Rejection */
- +#define STAT_CED 0x2000 /* Command end */
- +#define STAT_SED 0x1000 /* Seek end */
- +#define STAT_DER 0x0800 /* Drive error */
- +#define STAT_ABN 0x0400 /* Abnormal end */
- +#define STAT_POL 0x0200 /* Polling */
- +
- +/* ------------------------------------------------------------------------------------------ */
- +#ifdef DEBUG
- +static void console_printf(const char *fmt,...)
- +{
- + static char buffer[2048]; /* Arbitary! */
- + extern void console_print(const char *);
- + va_list ap;
- + int flags;
- +
- + save_flags(flags);
- + cli();
- +
- + va_start(ap, fmt);
- +
- + vsprintf(buffer, fmt, ap);
- +
- + console_print(buffer);
- +
- + va_end(fmt);
- +
- + restore_flags(flags);
- +}; /* console_printf */
- +#endif
- +
- +/* ------------------------------------------------------------------------------------------ */
- +
- +static struct gendisk mfm_gendisk =
- +{
- + MAJOR_NR, /* Major number */
- + "mfm", /* Major name */
- + 6, /* Bits to shift to get real from partition */
- + 1 << 6, /* Number of partitions per real */
- + XD_MAXDRIVES, /* maximum number of real */
- + mfm_geninit, /* init function */
- + mfm, /* hd struct */
- + mfm_sizes, /* block sizes */
- + 0, /* number */
- + (void *) mfm_info, /* internal */
- + NULL /* next */
- +};
- +
- +static struct file_operations mfm_fops =
- +{
- + NULL, /* lseek - default */
- + block_read, /* read - general block-dev read */
- + block_write, /* write - general block-dev write */
- + NULL, /* readdir - bad */
- + NULL, /* select */
- + mfm_ioctl, /* ioctl */
- + NULL, /* mmap */
- + mfm_open, /* open */
- + mfm_release, /* release */
- + block_fsync /* fsync */
- +};
- +
- +static struct expansion_card *ecs;
- +static const int mfm_prods[] = {0x000b};
- +static const int mfm_manus[] = {0x0000};
- +
- +/* See if there is a controller at the address presently set in mfm_addr */
- +static int CheckController(void)
- +{
- + int result;
- +
- + /* Send any command */
- + outw(CMD_SEK, MFM_COMMAND);
- +
- + /* experimentation shows that at least BSY or CPR should be on if
- + the controller exists. On my 310 this returns 0 */
- + result = (inw(MFM_STATUS) & 0xc000) != 0;
- +
- + /* If we did find it we have got to clean up */
- + if (result) {
- + /* Wait until the busy flag disappears */
- + while (inw(MFM_STATUS) & STAT_BSY);
- +
- + outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
- + }
- + return result;
- +}; /* CheckController */
- +
- +
- +int mfm_init(void)
- +{
- + unsigned int i;
- + /* DAG */
- + mfm_irq = IRQ_HARDDISK;
- + mfm_addr = (0x32d0000 >> 2); /* Motherboard HDC address - thats medium (as RiscOS)
- + used to be slow */
- + mfm_IRQPollLoc = &(IOC_IRQSTATB); /* was ioc+0x2 */
- + mfm_IRQMask = 0x08; /* IL3 pin */
- + printk("mfm: Looking for onboard controller.....\n");
- + if (!CheckController()) {
- + printk("mfm: Hmm - no onboard controller - lets try podules....\n");
- + ecs = ecard_find(0, sizeof(mfm_prods), mfm_prods, mfm_manus);
- + if (!ecs)
- + return -1;
- +
- + mfm_irq = ecs->irq;
- + mfm_addr = ((int) ecard_address(ecs->slot_no, ECARD_IOC, ECARD_MEDIUM) + 0x2000) >> 2;
- + mfm_IRQMask = 0x08;
- + mfm_IRQPollLoc = (unsigned char *) ((int) ecard_address(ecs->slot_no, ECARD_IOC, ECARD_MEDIUM) + 0x3000);
- +
- + ecard_claim(ecs);
- + };
- +
- + /* Stuff for the assembler routines to get to */
- + hdc63463_baseaddress = (unsigned int) (mfm_addr << 2);
- + hdc63463_irqpolladdress = (int) mfm_IRQPollLoc;
- + hdc63463_irqpollmask = mfm_IRQMask;
- +
- + printk("mfm: controller found at address %X, interrupt %d\n", mfm_addr << 2, mfm_irq);
- +
- + if (register_blkdev(MAJOR_NR, "mfm", &mfm_fops)) {
- + printk("mfm_init: unable to get major number %d\n", MAJOR_NR);
- + return -1;
- + }
- + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB?) read ahread */
- +
- + /* This stops MSDOS trying to mount it - but ll_rw_blk has tons of >>9
- + presuming 512 byte sectors - its upto us to fix it! */
- + hardsect_size[MAJOR_NR] = mfm_sectsizes;
- +
- + /* OK - well actually I'm going to leave it at 512 - this lets the
- + adfs image drivers work */
- + for (i = 0; i < 256; i++)
- + mfm_sectsizes[i] = 512;
- +
- +#ifndef MODULE
- + mfm_gendisk.next = gendisk_head;
- + gendisk_head = &mfm_gendisk;
- +#endif
- +
- + Busy = 0;
- + lastspecifieddrive = -1;
- +
- + return 0;
- +}
- +
- +/* DAG: Gets called by the genhd code to set up the number of sectors etc. */
- +void xd_set_geometry(int dev, unsigned char secsptrack, unsigned char heads,
- + unsigned long discsize, unsigned int secsize)
- +{
- + dev = MINOR(dev);
- + /* The HD version checks to see if the number of cylinders are unset */
- + /* and then causes them to be set... */
- + /* Need to set up the precomp etc. */
- + if (mfm_info[dev >> 6].cylinders == 1) {
- + mfm_info[dev >> 6].sectors = secsptrack;
- + mfm_info[dev >> 6].heads = heads;
- + mfm_info[dev >> 6].cylinders = discsize / (secsptrack * heads * secsize);
- +
- + mfm_drive_param[dev >> 6].nosectors = secsptrack;
- + mfm_drive_param[dev >> 6].noheads = heads;
- + mfm_drive_param[dev >> 6].nocylinders = discsize / (secsptrack * heads * secsize);
- +
- + printk("mfm%c: %d cylinders, %d heads, %d sectors secsize=%d (%ld total blocks)\n", 'a' + (dev >> 6),
- + mfm_info[dev >> 6].cylinders, heads, secsptrack, secsize, discsize);
- +
- + /* Should probably do a specify command here.... */
- + if ((heads < 1) || (mfm_drive_param[dev >> 6].nocylinders > 1024)) {
- + printk("mfm%c: Insane disc shape! Setting to 512/4/32\n",'a' + (dev >> 6));
- +
- + /* These values are fairly arbitary, but are there so that if your
- + lucky you can pick apart your disc to find out what is going on -
- + I reckon these figures won't hurt MOST drives */
- + mfm_info[dev >> 6].sectors = 32;
- + mfm_info[dev >> 6].heads = 4;
- + mfm_info[dev >> 6].cylinders = 512;
- +
- + mfm_drive_param[dev >> 6].nosectors = 32;
- + mfm_drive_param[dev >> 6].noheads = 4;
- + mfm_drive_param[dev >> 6].nocylinders = 512;
- + };
- + mfm_specify();
- + };
- +
- + mfm[dev].start_sect = 0;
- + /* This is 512 byte sectors - I want 256 but where does this get set for image files? */
- + mfm[dev].nr_sects = (discsize / secsize) / 2;
- +
- +}
- +
- +static int mfm_initdrives(void)
- +{
- + mfm_info[0].heads = 4;
- + mfm_info[0].cylinders = 1; /* its going to have to figure it out from the drive */
- + mfm_info[0].sectors = 32;
- + mfm_info[0].control = 0;
- + mfm_info[1].heads = 4;
- + mfm_info[1].cylinders = 1; /* its going to have to figure it out from the drive */
- + mfm_info[1].sectors = 32;
- + mfm_info[1].control = 0;
- + if (number_mfm_drives > XD_MAXDRIVES) {
- + number_mfm_drives = XD_MAXDRIVES;
- + printk("No. of ADFS MFM drives is greater than XD_MAXDRIVES - you can't have that many!\n");
- + };
- + return number_mfm_drives;
- +}
- +
- +static void mfm_geninit(struct gendisk *dev)
- +{
- + int i;
- +
- + mfm_drives = mfm_initdrives();
- + printk("mfm: detected %d hard drive%s\n", mfm_drives, mfm_drives == 1 ? "" : "s");
- + for (i = 0; i < mfm_drives; i++)
- + printk("mfm%d: heads = %d, cylinders = %d, sectors = %d\n", i,
- + mfm_info[i].heads, mfm_info[i].cylinders, mfm_info[i].sectors);
- +
- + if (request_irq(mfm_irq, mfm_interrupt_handler, SA_INTERRUPT, "MFM harddisk"))
- + printk("mfm: unable to get IRQ%d\n", mfm_irq);
- +
- + if (mfm_addr != (0x32d0000 >> 2))
- + outw(0x80, mfm_addr + (0x1000 >> 2)); /* Required to enable IRQs from MFM podule */
- +
- + for (i = 0; i < mfm_drives; i++) {
- + mfm[i << 6].nr_sects = mfm_info[i].heads * mfm_info[i].cylinders *
- + mfm_info[i].sectors;
- + mfm[i << 6].start_sect = 0;
- + mfm_valid[i] = 1;
- + }
- + mfm_gendisk.nr_real = mfm_drives;
- +
- + for (i = 0; i < (XD_MAXDRIVES << 6); i++)
- + mfm_blocksizes[i] = 1024; /* Can't increase this - if you do all hell breaks loose */
- + blksize_size[MAJOR_NR] = mfm_blocksizes;
- +}
- +
- +static int mfm_open(struct inode *inode, struct file *file)
- +{
- + int dev = DEVICE_NR(MINOR(inode->i_rdev));
- +
- + if ((dev) < mfm_drives) {
- + while (!mfm_valid[dev])
- + sleep_on(&mfm_wait_open);
- +
- + mfm_access[dev]++;
- +
- + return (0);
- + } else
- + return (-ENODEV);
- +}
- +
- +static int mfm_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
- +{
- + XD_GEOMETRY *geometry = (XD_GEOMETRY *) arg;
- + int dev = DEVICE_NR(MINOR(inode->i_rdev)), err;
- +
- + if (inode && (dev < mfm_drives))
- + switch (cmd) {
- + case HDIO_GETGEO:
- + if (arg) {
- + if ((err = verify_area(VERIFY_WRITE, geometry, sizeof(*geometry))))
- + return (err);
- + put_fs_byte(mfm_info[dev].heads, (char *) &geometry->heads);
- + put_fs_byte(mfm_info[dev].sectors, (char *) &geometry->sectors);
- + put_fs_word(mfm_info[dev].cylinders, (short *) &geometry->cylinders);
- + put_fs_long(mfm[MINOR(inode->i_rdev)].start_sect, (long *) &geometry->start);
- +
- + return (0);
- + }
- + break;
- + case BLKRASET:
- + if (!suser())
- + return -EACCES;
- + if (!inode->i_rdev)
- + return -EINVAL;
- + if (arg > 0xff)
- + return -EINVAL;
- + read_ahead[MAJOR(inode->i_rdev)] = arg;
- + return 0;
- + case BLKGETSIZE:
- + if (arg) {
- + if ((err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long))))
- + return (err);
- + put_fs_long(mfm[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
- +
- + return (0);
- + }
- + break;
- + case BLKFLSBUF:
- + if (!suser())
- + return -EACCES;
- + if (!inode->i_rdev)
- + return -EINVAL;
- + fsync_dev(inode->i_rdev);
- + invalidate_buffers(inode->i_rdev);
- + return 0;
- +
- + case BLKRRPART:
- + return (mfm_reread_partitions(inode->i_rdev));
- + RO_IOCTLS(inode->i_rdev, arg);
- + }
- + return (-EINVAL);
- +}
- +
- +static void mfm_release(struct inode *inode, struct file *file)
- +{
- + int dev = DEVICE_NR(MINOR(inode->i_rdev));
- +
- + if (dev < mfm_drives) {
- + sync_dev(dev);
- + mfm_access[dev]--;
- + }
- +}
- +
- +static int mfm_reread_partitions(int dev)
- +{
- + int target = DEVICE_NR(MINOR(dev)), start = target << mfm_gendisk.minor_shift,
- + partition;
- +
- + cli();
- +
- + mfm_valid[target] = (mfm_access[target] != 1);
- +
- + sti();
- +
- + if (mfm_valid[target])
- + return -EBUSY;
- +
- + for (partition = mfm_gendisk.max_p - 1; partition >= 0; partition--) {
- + sync_dev(MAJOR_NR << 8 | start | partition);
- + invalidate_inodes(MAJOR_NR << 8 | start | partition);
- + invalidate_buffers(MAJOR_NR << 8 | start | partition);
- + mfm_gendisk.part[start + partition].start_sect = 0;
- + mfm_gendisk.part[start + partition].nr_sects = 0;
- + };
- +
- + mfm_gendisk.part[start].nr_sects = mfm_info[target].heads *
- + mfm_info[target].cylinders * mfm_info[target].sectors;
- + resetup_one_dev(&mfm_gendisk, target);
- +
- + mfm_valid[target] = 1;
- + wake_up(&mfm_wait_open);
- +
- + return 0;
- +}
- +
- +static void print_status(void)
- +{
- + char *error;
- + static char *errors[] = {
- + "no error",
- + "command aborted",
- + "invalid command",
- + "parameter error",
- + "not initialised",
- + "rejected TEST",
- + "no useld",
- + "write fault",
- + "not ready",
- + "no scp",
- + "in seek",
- + "invalid NCA",
- + "invalid step rate",
- + "seek error",
- + "over run",
- + "invalid PHA",
- + "data field EEC error",
- + "data field CRC error",
- + "error corrected",
- + "data field fatal error",
- + "no data am",
- + "not hit",
- + "ID field CRC error",
- + "time over",
- + "no ID am",
- + "not writable"
- + };
- + if (result[1] < 0x65)
- + error = errors[result[1] >> 2];
- + else
- + error = "unknown";
- + printk("(");
- + if (mfm_status & STAT_BSY) printk("BSY ");
- + if (mfm_status & STAT_CPR) printk("CPR ");
- + if (mfm_status & STAT_CED) printk("CED ");
- + if (mfm_status & STAT_SED) printk("SED ");
- + if (mfm_status & STAT_DER) printk("DER ");
- + if (mfm_status & STAT_ABN) printk("ABN ");
- + if (mfm_status & STAT_POL) printk("POL ");
- + printk(") SSB = %X (%s)\n", result[1], error);
- +
- +}
- +
- +/* ------------------------------------------------------------------------------------- */
- +
- +static void issue_command(int command, unsigned char *cmdb, int len)
- +{
- + int status;
- +#ifdef DEBUG
- + int i;
- + console_printf("issue_command: %02X: ", command);
- + for (i = 0; i < len; i++)
- + console_printf("%02X ", cmdb[i]);
- + console_printf("\n");
- +#endif
- +
- + do {
- + status = inw(MFM_STATUS);
- + } while (status & (STAT_BSY | STAT_POL));
- +#ifdef DEBUG
- + console_printf("issue_command: status after pol/bsy loop: %02X:\n ", status >> 8);
- +#endif
- + if (status & (STAT_CPR | STAT_CED | STAT_SED | STAT_DER | STAT_ABN)) {
- + outw(CMD_RCAL, MFM_COMMAND);
- + while (inw(MFM_STATUS) & STAT_BSY);
- + }
- + status = inw(MFM_STATUS);
- +#ifdef DEBUG
- + console_printf("issue_command: status before parameter issue: %02X:\n ", status >> 8);
- +#endif
- + while (len > 0) {
- + outw(cmdb[1] | (cmdb[0] << 8), MFM_DATAOUT);
- + len -= 2;
- + cmdb += 2;
- + }
- + status = inw(MFM_STATUS);
- +#ifdef DEBUG
- + console_printf("issue_command: status before command issue: %02X:\n ", status >> 8);
- +#endif
- + outw(command, MFM_COMMAND);
- + status = inw(MFM_STATUS);
- +#ifdef DEBUG
- + console_printf("issue_command: status immediatly after command issue: %02X:\n ", status >> 8);
- +#endif
- +}
- +
- +static void wait_for_completion(void)
- +{
- + while ((mfm_status = inw(MFM_STATUS)) & STAT_BSY);
- +}
- +
- +/* ------------------------------------------------------------------------------------- */
- +
- +static void mfm_rw_intr(void)
- +{
- + int old_status; /* Holds status on entry, we read to see if the command just finished */
- +#ifdef DEBUG
- + console_printf("mfm_rw_intr...dataleft=%d\n", hdc63463_dataleft);
- + print_status();
- +#endif
- +
- + if (mfm_status & (STAT_DER | STAT_ABN)) {
- + /* Something has gone wrong - lets try that again */
- + outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
- + if (cont) {
- +#ifdef DEBUG
- + console_printf("mfm_rw_intr: DER/ABN err\n");
- +#endif
- + cont->error();
- + cont->redo();
- + };
- + return;
- + };
- +
- + /* OK so what ever happend its not an error, now I reckon we are left between
- + a choice of command end or some data which is ready to be collected */
- + /* I think we have to transfer data while the interrupt line is on and its
- + not any other type of interrupt */
- + if (CURRENT->cmd == WRITE) {
- + extern void hdc63463_writedma(void);
- + if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) {
- + printk("mfm_rw_intr: Apparent DMA write request when no more to DMA\n");
- + if (cont) {
- + cont->error();
- + cont->redo();
- + };
- + return;
- + };
- + hdc63463_writedma();
- + } else {
- + extern void hdc63463_readdma(void);
- + if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) {
- + printk("mfm_rw_intr: Apparent DMA read request when no more to DMA\n");
- + if (cont) {
- + cont->error();
- + cont->redo();
- + };
- + return;
- + };
- +#ifdef DEBUG
- + console_printf("Going to try read dma..............status=0x%x, buffer=%p\n", mfm_status, hdc63463_dataptr);
- +#endif
- + hdc63463_readdma();
- + }; /* Read */
- +
- + if (hdc63463_dataptr != ((unsigned int) Copy_buffer + 256)) {
- + /* If we didn't actually manage to get any data on this interrupt - but why? We got the interrupt */
- + /* Ah - well looking at the status its just when we get command end; so no problem */
- + /*console_printf("mfm: dataptr mismatch. dataptr=0x%08x Copy_buffer+256=0x%08p\n",
- + hdc63463_dataptr,Copy_buffer+256);
- + print_status(); */
- + } else {
- + Sectors256LeftInCurrent--;
- + Copy_buffer += 256;
- + Copy_Sector++;
- +
- + /* We have come to the end of this request */
- + if (!Sectors256LeftInCurrent) {
- +#ifdef DEBUG
- + console_printf("mfm: end_request for CURRENT=0x%p CURRENT(sector=%d current_nr_sectors=%d nr_sectors=%d)\n",
- + CURRENT, CURRENT->sector, CURRENT->current_nr_sectors, CURRENT->nr_sectors);
- +#endif
- +
- + CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
- + CURRENT->sector += CURRENT->current_nr_sectors;
- + SectorsLeftInRequest -= CURRENT->current_nr_sectors;
- +
- + end_request(1);
- + if (SectorsLeftInRequest) {
- + hdc63463_dataptr = (unsigned int) CURRENT->buffer;
- + Copy_buffer = CURRENT->buffer;
- + Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2;
- + errors = &(CURRENT->errors);
- + /* These should match the present calculations of the next logical sector
- + on the device
- + Copy_Sector=CURRENT->sector*2; */
- +
- + if (Copy_Sector != CURRENT->sector * 2)
- +#ifdef DEBUG
- + console_printf("mfm: Copy_Sector mismatch. Copy_Sector=%d CURRENT->sector*2=%d\n",
- + Copy_Sector, CURRENT->sector * 2);
- +#else
- + printk("mfm: Copy_Sector mismatch! Eek!\n");
- +#endif
- + }; /* CURRENT */
- + }; /* Sectors256LeftInCurrent */
- + };
- +
- + old_status = mfm_status;
- + mfm_status = inw(MFM_STATUS);
- + if (mfm_status & (STAT_DER | STAT_ABN)) {
- + /* Something has gone wrong - lets try that again */
- + if (cont) {
- +#ifdef DEBUG
- + console_printf("mfm_rw_intr: DER/ABN error\n");
- +#endif
- + cont->error();
- + cont->redo();
- + };
- + return;
- + };
- +
- + /* If this code wasn't entered due to command_end but there is
- + now a command end we must read the command results out. If it was
- + entered like this then mfm_interrupt_handler would have done the
- + job. */
- + if ((!((old_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) &&
- + ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) {
- + int len = 0;
- + while (len < 16) {
- + int in;
- + in = inw(MFM_DATAIN);
- + result[len++] = in >> 8;
- + result[len++] = in;
- + };
- + }; /* Result read */
- +
- + /*console_printf ("mfm_rw_intr nearexit [%02X]\n", (unsigned int)*mfm_IRQPollLoc); */
- +
- + /* If end of command move on */
- + if (mfm_status & (STAT_CED)) {
- + outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
- + /* End of command - trigger the next command */
- + if (cont) {
- + cont->done(1);
- + }
- +#ifdef DEBUG
- + console_printf("mfm_rw_intr: returned from cont->done\n");
- +#endif
- + } else {
- + /* Its going to generate another interrupt */
- + SET_INTR(mfm_rw_intr);
- + };
- +}
- +
- +static void mfm_setup_rw(void)
- +{
- +#ifdef DEBUG
- + console_printf("setting up for rw...\n");
- +#endif
- +#if 1
- + SET_INTR(mfm_rw_intr);
- + issue_command(raw_cmd.cmdcode, raw_cmd.cmddata, raw_cmd.cmdlen);
- +#else
- + if (cont) {
- + cont->done(0);
- + cont->redo();
- + }
- +#endif
- +}
- +
- +static void mfm_recal_intr(void)
- +{
- +#ifdef DEBUG
- + console_printf("recal intr - status = ");
- + print_status();
- +#endif
- + outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
- + if (mfm_status & (STAT_DER | STAT_ABN)) {
- + printk("recal failed\n");
- + MFM_DRV_PARAM.cylinder = NEED_2_RECAL;
- + if (cont) {
- + cont->error();
- + cont->redo();
- + }
- + return;
- + }
- + /* Thats seek end - we are finished */
- + if (mfm_status & STAT_SED) {
- + issue_command(CMD_POD, NULL, 0);
- + MFM_DRV_PARAM.cylinder = 0;
- + mfm_seek();
- + return;
- + }
- + /* Command end without seek end (see data sheet p.20) for parallel seek
- + - we have to send a POL command to wait for the seek */
- + if (mfm_status & STAT_CED) {
- + SET_INTR(mfm_recal_intr);
- + issue_command(CMD_POL, NULL, 0);
- + return;
- + }
- + printk("recal: unknown status\n");
- +}
- +
- +static void mfm_seek_intr(void)
- +{
- +#ifdef DEBUG
- + console_printf("seek intr - status = ");
- + print_status();
- +#endif
- + outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
- + if (mfm_status & (STAT_DER | STAT_ABN)) {
- + printk("seek failed\n");
- + MFM_DRV_PARAM.cylinder = NEED_2_RECAL;
- + if (cont) {
- + cont->error();
- + cont->redo();
- + }
- + return;
- + }
- + if (mfm_status & STAT_SED) {
- + issue_command(CMD_POD, NULL, 0);
- + MFM_DRV_PARAM.cylinder = raw_cmd.cylinder;
- + mfm_seek();
- + return;
- + }
- + if (mfm_status & STAT_CED) {
- + SET_INTR(mfm_seek_intr);
- + issue_command(CMD_POL, NULL, 0);
- + return;
- + }
- + printk("seek: unknown status\n");
- +}
- +
- +static void mfm_specify(void)
- +{
- + /* IDEA2 seems to work better - its what RiscOS sets my
- + disc to - on its SECOND call to specify! */
- +#define IDEA2
- + unsigned char cmdb[16];
- +#ifdef DEBUG
- + console_printf("specify...dev=%d lastspecified=%d\n", raw_cmd.dev, lastspecifieddrive);
- +#endif
- + cmdb[0] = 0x1F; /* OM0 - !SECT,!MOD,!DIF,PADP,ECD,CRCP,CRCI,ACOR */
- + cmdb[1] = 0xC3; /* OM1 - DTM,BRST,!CEDM,!SEDM,!DERM,0,AMEX,PSK */
- +#ifndef IDEA2
- + cmdb[2] = 0x16; /* OM2 - SL - step pulse low */
- +#else
- + cmdb[2] = 0; /* OM2 - SL - step pulse low */
- +#endif
- + cmdb[3] = (number_mfm_drives == 1) ? 0x02 : 0x06; /* 1 or 2 drives */
- + cmdb[4] = 0xFC | ((MFM_DRV_PARAM.nocylinders - 1) >> 8); /* RW time over/high part of number of cylinders */
- + cmdb[5] = MFM_DRV_PARAM.nocylinders - 1; /* low part of number of cylinders */
- + cmdb[6] = MFM_DRV_PARAM.noheads - 1; /* Number of heads */
- + cmdb[7] = MFM_DRV_PARAM.nosectors - 1; /* Number of sectors */
- +#ifndef IDEA2
- + cmdb[8] = 0xA9; /* Step pulse high=21, Record Length=001 (256 bytes) */
- +#else
- + cmdb[8] = 0x21; /* Step pulse high=4, Record Length=001 (256 bytes) */
- +#endif
- + cmdb[9] = 0x0A; /* gap length 1 */
- + cmdb[10] = 0x0D; /* gap length 2 */
- + cmdb[11] = 0x0C; /* gap length 3 */
- + /* DAG: Note my data sheet shows precomp and low current the other way around */
- + cmdb[12] = (MFM_DRV_PARAM.precompcylinder - 1) >> 8; /* pre comp cylinder */
- + cmdb[13] = MFM_DRV_PARAM.precompcylinder - 1;
- + cmdb[14] = (MFM_DRV_PARAM.lowcurrentcylinder - 1) >> 8; /* Low current cylinder */
- + cmdb[15] = MFM_DRV_PARAM.lowcurrentcylinder - 1;
- +
- + issue_command(CMD_SPC, cmdb, 16);
- + /* Ensure that we will do another specify if we move to the other drive */
- + lastspecifieddrive = raw_cmd.dev;
- + wait_for_completion();
- +}
- +
- +static void mfm_seek(void)
- +{
- + unsigned char cmdb[4];
- +#ifdef DEBUG
- + console_printf("seeking...\n");
- +#endif
- + if (MFM_DRV_PARAM.cylinder < 0) {
- + SET_INTR(mfm_recal_intr);
- +#ifdef DEBUG
- + console_printf("mfm_seek: about to call specify\n");
- +#endif
- + mfm_specify(); /* DAG added this */
- +
- + cmdb[0] = raw_cmd.dev + 1;
- + cmdb[1] = 0; /*raw_cmd.head; */
- +
- + issue_command(CMD_RCLB, cmdb, 2);
- + return;
- + }
- + /*mfm_specify(); *//* DAG took this out */
- + if (MFM_DRV_PARAM.cylinder != raw_cmd.cylinder) {
- + cmdb[0] = raw_cmd.dev + 1;
- + cmdb[1] = 0; /* raw_cmd.head; DAG: My data sheet says this should be 0 */
- + cmdb[2] = raw_cmd.cylinder >> 8;
- + cmdb[3] = raw_cmd.cylinder;
- +
- + SET_INTR(mfm_seek_intr);
- + issue_command(CMD_SEK, cmdb, 4);
- + } else
- + mfm_setup_rw();
- +}
- +
- +static void mfm_initialise(void)
- +{
- +#ifdef DEBUG
- + console_printf("init...\n");
- +#endif
- + if (raw_cmd.flags & NEED_SEEK)
- + mfm_seek();
- + else
- + mfm_setup_rw();
- +}
- +
- +/* ------------------------------------------------------------------------------------- */
- +
- +static void request_done(int uptodate)
- +{
- +#ifdef DEBUG
- + console_printf("mfm:request_done\n");
- +#endif
- + if (uptodate) {
- + unsigned char block[2] = {0, 0};
- +
- + /* Apparently worked - lets check bytes left to DMA */
- + if (hdc63463_dataleft != (PartFragRead_SectorsLeft * 256)) {
- + printk("mfm: request_done - dataleft=%d - should be %d - Eek!\n", hdc63463_dataleft, PartFragRead_SectorsLeft * 256);
- + end_request(0);
- + Busy = 0;
- + };
- + /* Potentially this means that we've done; but we might be doing
- + a partial access, (over two cylinders) or we may have a number
- + of fragments in an image file. First lets deal with partial accesss
- + */
- + if (PartFragRead) {
- + /* Yep - a partial access */
- +
- + /* and issue the remainder */
- + issue_request(MINOR(CURRENT->rq_dev), PartFragRead_RestartBlock, PartFragRead_SectorsLeft, CURRENT);
- + return;
- + };
- +
- + /* ah well - perhaps there is another fragment to go */
- +
- + /* Increment pointers/counts to start of next fragment */
- + if (SectorsLeftInRequest > 0) printk("mfm: SectorsLeftInRequest>0 - Eek! Shouldn't happen!\n");
- +
- + /* No - its the end of the line */
- + /* end_request's should have happened at the end of sector DMAs */
- + /* Turns Drive LEDs off - may slow it down? */
- + issue_command(CMD_CKV, block, 2);
- +
- + Busy = 0;
- +#ifdef DEBUG
- + console_printf("request_done: About to mfm_request\n");
- +#endif
- + /* Next one please */
- + mfm_request(); /* Moved from mfm_rw_intr */
- +#ifdef DEBUG
- + console_printf("request_done: returned from mfm_request\n");
- +#endif
- + } else {
- + end_request(0);
- + Busy = 0;
- + }
- +}
- +
- +static void error_handler(void)
- +{
- + int i;
- + printk("error detected... status = ");
- + print_status();
- + (*errors)++;
- + if (*errors > MFM_DRV_PARAM.errors.abort)
- + cont->done(0);
- + if (*errors > MFM_DRV_PARAM.errors.recal)
- + MFM_DRV_PARAM.cylinder = NEED_2_RECAL;
- +}
- +
- +static void rw_interrupt(void)
- +{
- + printk("rw_interrupt\n");
- +}
- +
- +static struct cont rw_cont =
- +{
- + rw_interrupt,
- + error_handler,
- + mfm_rerequest,
- + request_done
- +};
- +
- +/*
- + * Actually gets round to issuing the request - note everything at this
- + * point is in 256 byte sectors not Linux 512 byte blocks
- + */
- +static void issue_request(int dev, unsigned int block, unsigned int nsect,
- + struct request *req)
- +{
- + int track, start_head, start_sector;
- + int sectors_to_next_cyl;
- +
- + dev >>= 6;
- +
- + track = block / mfm_info[dev].sectors;
- + start_sector = block % mfm_info[dev].sectors;
- + start_head = track % mfm_info[dev].heads;
- +
- + /* First get the number of whole tracks which are free before the next
- + track */
- + sectors_to_next_cyl = (mfm_info[dev].heads - (start_head + 1)) * mfm_info[dev].sectors;
- + /* Then add in the number of sectors left on this track */
- + sectors_to_next_cyl += (mfm_info[dev].sectors - start_sector);
- +
- +#ifdef DEBUG
- + console_printf("issue_request: mfm_info[dev].sectors=%d track=%d\n", mfm_info[dev].sectors, track);
- +#endif
- +
- + raw_cmd.flags = NEED_SEEK;
- + raw_cmd.dev = dev;
- + raw_cmd.sector = start_sector;
- + raw_cmd.head = start_head;
- + raw_cmd.cylinder = track / mfm_info[dev].heads;
- + raw_cmd.cmdtype = CURRENT->cmd;
- + raw_cmd.cmdcode = CURRENT->cmd == WRITE ? CMD_WD : CMD_RD;
- + raw_cmd.cmddata[0] = dev + 1; /* DAG: +1 to get US */
- + raw_cmd.cmddata[1] = raw_cmd.head;
- + raw_cmd.cmddata[2] = raw_cmd.cylinder >> 8;
- + raw_cmd.cmddata[3] = raw_cmd.cylinder;
- + raw_cmd.cmddata[4] = raw_cmd.head;
- + raw_cmd.cmddata[5] = raw_cmd.sector;
- +
- + /* Was == and worked - how the heck??? */
- + if (lastspecifieddrive != raw_cmd.dev) {
- + mfm_specify();
- + };
- +
- + if (nsect <= sectors_to_next_cyl) {
- + raw_cmd.cmddata[6] = nsect >> 8;
- + raw_cmd.cmddata[7] = nsect;
- + PartFragRead = 0; /* All in one */
- + PartFragRead_SectorsLeft = 0; /* Must set this - used in DMA calcs */
- + } else {
- + raw_cmd.cmddata[6] = sectors_to_next_cyl >> 8;
- + raw_cmd.cmddata[7] = sectors_to_next_cyl;
- + PartFragRead = sectors_to_next_cyl; /* only do this many this time */
- + PartFragRead_RestartBlock = block + sectors_to_next_cyl; /* Where to restart from */
- + PartFragRead_SectorsLeft = nsect - sectors_to_next_cyl;
- + };
- + raw_cmd.cmdlen = 8;
- +
- + /* Setup DMA pointers */
- + hdc63463_dataptr = (unsigned int) Copy_buffer;
- + hdc63463_dataleft = nsect * 256; /* Better way? */
- +
- +
- +#ifdef DEBUG
- + console_printf("mfm%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx (%p)\n",
- + raw_cmd.dev + 'a', (CURRENT->cmd == READ) ? "read" : "writ",
- + raw_cmd.cylinder,
- + raw_cmd.head,
- + raw_cmd.sector, nsect, (unsigned long) Copy_buffer, CURRENT);
- +#endif
- + cont = &rw_cont;
- + errors = &(CURRENT->errors);
- + mfm_tq.routine = (void (*)(void *)) mfm_initialise;
- + queue_task(&mfm_tq, &tq_immediate);
- + mark_bh(IMMEDIATE_BH);
- +}; /* issue_request */
- +
- +static void mfm_rerequest(void)
- +{
- + /* Called when an error has just happened - need to trick mfm_request
- + into thinking we weren't busy */
- + /* Turn off ints - mfm_request expects no interrupts anyway */
- +#ifdef DEBUG
- + console_printf("mfm_rerequest\n");
- +#endif
- + cli();
- + Busy = 0;
- + mfm_request();
- +};
- +
- +static void mfm_request(void)
- +{
- + unsigned int dev, block, nsect;
- +
- +#ifdef DEBUG
- + console_printf("mfm_request CURRENT=%p Busy=%d\n", CURRENT, Busy);
- +#endif
- + if (!CURRENT) {
- +#ifdef DEBUG
- + console_printf("mfm_request: Exited due to NULL Current 1\n");
- +#endif
- + return;
- + };
- + if (CURRENT->rq_status == RQ_INACTIVE) {
- + /* Hmm - seems to be happening a lot on 1.3.45 */
- + /*console_printf("mfm_request: Exited due to INACTIVE Current\n"); */
- + return;
- + };
- +
- + /* If we are still processing then return; we will get called again */
- + if (Busy) {
- + /* Again seems to be common in 1.3.45 */
- + /*console_printf("mfm_request: Exiting due to busy\n"); */
- + return;
- + };
- + Busy = 1;
- +#if 0
- + if (CURRENT && CURRENT->rq_dev < 0) {
- + console_printf("mfm_request: exit due to (CURRENT && CURRENT->rq_dev < 0)\n");
- + Busy = 0;
- + return;
- + };
- +#endif
- +
- + while (1) {
- +#ifdef DEBUG
- + console_printf("mfm_request: loop start\n");
- +#endif
- + /* DAG: I wonder if there wshould be a store flags here? */
- + sti();
- +
- +#ifdef DEBUG
- + console_printf("mfm_request: before INIT_REQUEST\n");
- +#endif
- + if (!CURRENT) {
- + printk("mfm_request: Exiting due to !CURRENT (pre)\n");
- + Busy = 0;
- + return;
- + };
- +
- + INIT_REQUEST;
- +
- + /* DAG: Should this be here - e.g. if we run out of valid requests */
- + if (!CURRENT) {
- + printk("mfm_request: Exiting due to !CURRENT (post)\n");
- + Busy = 0;
- + return;
- + };
- +
- +#ifdef DEBUG
- + console_printf("mfm_request: before arg extraction\n");
- +#endif
- + dev = MINOR(CURRENT->rq_dev);
- + Copy_buffer = CURRENT->buffer;
- +
- + block = CURRENT->sector;
- + nsect = CURRENT->nr_sectors;
- + SectorsLeftInRequest = nsect;
- + Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2;
- + Copy_buffer = CURRENT->buffer;
- + Copy_Sector = CURRENT->sector * 2;
- +
- +#ifdef DEBUG
- + /*if ((dev>>6)==1) */ console_printf("mfm_request: raw vals: dev=%d (block=512 bytes) block=%d nblocks=%d\n", dev, block, nsect);
- +#endif
- +
- + /* DAG: Linux doesn't cope with this - even though it has an array telling
- + it the hardware block size - silly */
- + block *= 2; /* Now in 256 byte sectors */
- + nsect *= 2; /* Ditto */
- +
- + if (dev >= (mfm_drives << 6) ||
- + block >= (mfm[dev].nr_sects * 2) || ((block + nsect) > (mfm[dev].nr_sects * 2))) {
- + printk("mfm: oops dev=0x%04x mfm_drives=%d block=%d mfm[dev].nr_sects=%ld nsect=%d\n",
- + dev, mfm_drives, block, mfm[dev].nr_sects, nsect);
- + if (dev >= (mfm_drives << 6))
- + printk("mfm: bad minor number: device=0x%04x\n", CURRENT->rq_dev);
- + else
- + printk("mfm%c: bad access: block=%d, count=%d\n", (dev >> 6) + 'a',
- + block, nsect);
- + printk("mfm: continue 1\n");
- + end_request(0);
- + Busy = 0;
- + continue;
- + }
- + block += mfm[dev].start_sect;
- +
- + if (dev & 0x3f) {
- + printk("mfm: Accessing old image file - not allowed!\n");
- + end_request(0);
- + Busy = 0;
- + continue;
- + }
- +#ifdef DEBUG
- + console_printf("mfm_request: block after offset=%d\n", block);
- +#endif
- +
- + if (CURRENT->cmd != READ && CURRENT->cmd != WRITE) {
- + printk("unknown mfm-command %d\n", CURRENT->cmd);
- + end_request(0);
- + Busy = 0;
- + printk("mfm: continue 4\n");
- + continue;
- + }
- + issue_request(dev, block, nsect, CURRENT);
- +
- + break;
- + }
- +#ifdef DEBUG
- + console_printf("mfm_request: Dropping out bottom\n");
- +#endif
- +}
- +
- +static void do_mfm_request(void)
- +{
- +#ifdef DEBUG
- + console_printf("do_mfm_request: about to mfm_request\n");
- +#endif
- + mfm_request();
- +}
- +
- +static void mfm_unexpected_interrupt(void)
- +{
- + printk("mfm: unexpected interrupt - status = ");
- + print_status();
- + while (1);
- +}
- +
- +static void mfm_interrupt_handler(int unused, struct pt_regs *regs)
- +{
- + int i;
- + void (*handler) (void) = DEVICE_INTR;
- +
- +#ifdef DEBUG
- + console_printf("mfm_interrupt_handler (handler=0x%p)\n", handler);
- +#endif
- + CLEAR_INTR;
- + mfm_status = inw(MFM_STATUS);
- +
- + /* If CPR (Command Parameter Reject) and not busy it means that the command
- + has some return message to give us */
- + if ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR) {
- + int len = 0;
- + while (len < 16) {
- + int in;
- + in = inw(MFM_DATAIN);
- + result[len++] = in >> 8;
- + result[len++] = in;
- + }
- + }
- + if (!handler) {
- + outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
- + mfm_unexpected_interrupt();
- + return;
- + }
- + handler();
- +}
- +
- +/* DAG: Called presumably to accept command line parameters */
- +/* Ignore for the moment! */
- +void mfm_setup(char *str, int *ints)
- +{
- + return;
- +}
- +
- +#ifdef MODULE
- +
- +int init_module(void)
- +{
- + mfm_init();
- + mfm_geninit(&mfm_gendisk);
- + return 0;
- +}
- +
- +void cleanup_module(void)
- +{
- + if (ecs) {
- + if (mfm_addr != (0x32d0000 >> 2))
- + outw(0, mfm_addr + (0x1000 >> 2)); /* Required to enable IRQs from MFM podule */
- + free_irq(mfm_irq);
- + unregister_blkdev(MAJOR_NR, "mfm");
- + ecard_release(ecs);
- + }
- +}
- +
- +#endif
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/Config.in linux/arch/arm/drivers/char/Config.in
- --- linux.orig/arch/arm/drivers/char/Config.in Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/Config.in Tue Jul 30 17:45:42 1996
- @@ -0,0 +1,29 @@
- +#
- +# Character device configuration
- +#
- +mainmenu_option next_comment
- +comment 'Character devices'
- +
- +bool 'Echo console messages on /dev/ttyS0' CONFIG_SERIAL_ECHO
- +tristate 'Standard/generic serial support' CONFIG_SERIAL
- +if [ $CONFIG_SERIAL != 'n' ]; then
- + tristate 'Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL
- + tristate 'Dual serial port support' CONFIG_DUALSP_SERIAL
- +fi
- +tristate 'Parallel printer support' CONFIG_PRINTER
- +
- +bool 'Mouse support' CONFIG_MOUSE
- +if [ "$CONFIG_MOUSE" = "y" ]; then
- + tristate 'Keyboard mouse (pre-RiscPC)' CONFIG_KBDMOUSE
- +fi
- +
- +bool 'Support for user misc device modules' CONFIG_UMISC
- +
- +bool 'Watchdog Timer Support' CONFIG_WATCHDOG
- +if [ "$CONFIG_WATCHDOG" != "n" ]; then
- + bool ' Disable watchdog shutdown on close' CONFIG_WATCHDOG_NOWAYOUT
- + define_bool CONFIG_SOFT_WATCHDOG y
- +fi
- +#bool 'Enhanced Real Time Clock Support' CONFIG_RTC
- +endmenu
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/Makefile linux/arch/arm/drivers/char/Makefile
- --- linux.orig/arch/arm/drivers/char/Makefile Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/Makefile Tue Jul 30 17:39:37 1996
- @@ -0,0 +1,149 @@
- +#
- +# Makefile for the kernel character device drivers.
- +#
- +# Note! Dependencies are done automagically by 'make dep', which also
- +# removes any old dependencies. DON'T put your own dependencies here
- +# unless it's something special (ie not a .c file).
- +#
- +# Note 2! The CFLAGS definitions are now inherited from the
- +# parent makes..
- +#
- +
- +#
- +# This file contains the font map for the default font
- +#
- +
- +FONTMAPFILE = cp437.uni
- +
- +# Links to make
- +
- +LK = conmakehash.c consolemap.c consolemap.h cp437.uni \
- + kbd_kern.h lp.c random.c tty_ioctl.c softdog.c
- +
- +all: links first_rule
- +
- +L_TARGET := char.a
- +M_OBJS :=
- +L_OBJS := tty_io.o n_tty.o console.o keyboard.o \
- + tty_ioctl.o pty.o vt.o mem.o vc_screen.o random.o \
- + defkeymap.o consolemap.o selection.o
- +
- +# architecture options
- +ifeq ($(MACHINE),a5k)
- + ifeq ($(CONFIG_SERIAL),y)
- + SL=y
- + else
- + ifeq ($(CONFIG_SERIAL),m)
- + SM=m
- + endif
- + endif
- +else
- + ifeq ($(MACHINE),arc)
- + ifeq ($(CONFIG_SERIAL),y)
- + LX_OBJS += serial6850.o
- + else
- + ifeq ($(CONFIG_SERIAL),m)
- + LX_OBJS += serial6850.o
- + endif
- + endif
- + endif
- +endif
- +
- +# Common dependencies
- +
- +ifeq ($(CONFIG_ATOMWIDE_SERIAL),y)
- + L_OBJS += serial-atomwide.o
- + SL=y
- +else
- + ifeq ($(CONFIG_ATOMWIDE_SERIAL),m)
- + M_OBJS += serial-atomwide.o
- + SM=m
- + endif
- +endif
- +
- +ifeq ($(CONFIG_DUALSP_SERIAL),y)
- + L_OBJS += serial-dualsp.o
- + SL=y
- +else
- + ifeq ($(CONFIG_DUALSP_SERIAL),m)
- + M_OBJS += serial-dualsp.o
- + SM=m
- + endif
- +endif
- +
- +ifdef SL
- + L_OBJS += serial.o
- +else
- + ifdef SM
- + M_OBJS += serial.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_PRINTER),y)
- + L_OBJS += lp.o
- +else
- + ifeq ($(CONFIG_PRINTER),m)
- + M_OBJS += lp.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_UMISC),y)
- +# To support third-party modules, msic.c must reside in the kernel
- +M = y
- +endif
- +
- +ifeq ($(CONFIG_KBDMOUSE),y)
- + M = y
- + L_OBJS += kbdmouse.o
- +else
- + ifeq ($(CONFIG_KBDMOUSE),m)
- + MM = m
- + M_OBJS += kbdmouse.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_SOFT_WATCHDOG),y)
- +M = y
- +L_OBJS += softdog.o
- +else
- + ifeq ($(CONFIG_SOFT_WATCHDOG),m)
- + M_OBJS += softdog.o
- + MM = m
- + endif
- +endif
- +
- +ifdef M
- + L_OBJS += misc.o
- +else
- + ifdef MM
- + M_OBJS += misc.o
- + endif
- +endif
- +
- +include $(TOPDIR)/Rules.make
- +
- +fastdep: links uni_hash.tbl
- +
- +conmakehash: conmakehash.c
- + $(HOSTCC) -o conmakehash conmakehash.c
- +
- +uni_hash.tbl: $(FONTMAPFILE) conmakehash
- + ./conmakehash $(FONTMAPFILE) > uni_hash.tbl
- +
- +.PHONY: links
- +links:
- + -@for f in $(LK); do \
- + if [ ! -e $$f ]; then \
- + echo "ln -s ../../../../drivers/char/$$f .";\
- + ln -s ../../../../drivers/char/$$f .; \
- + fi \
- + done
- +
- +mrproper:
- + -@for f in $(LK); do \
- + if [ -L $$f ]; then \
- + echo $(RM) $$f; \
- + $(RM) $$f; \
- + fi; \
- + done
- + $(RM) conmakehash
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/console/font.h linux/arch/arm/drivers/char/console/font.h
- --- linux.orig/arch/arm/drivers/char/console/font.h Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/console/font.h Sat Apr 20 14:37:42 1996
- @@ -0,0 +1,102 @@
- +/*
- + * Font definition
- + */
- +const unsigned char cmap_80[][8]= {
- + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* */
- + {0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00}, /* ! */
- + {0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00}, /* " */
- + {0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, /* # */
- + {0x0C, 0x3F, 0x68, 0x3E, 0x0B, 0x7E, 0x18, 0x00}, /* $ */
- + {0x60, 0x66, 0x0C, 0x18, 0x30, 0x66, 0x06, 0x00}, /* % */
- + {0x38, 0x6C, 0x6C, 0x38, 0x6D, 0x66, 0x3B, 0x00}, /* & */
- + {0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ' */
- + {0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00}, /* ( */
- + {0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00}, /* ) */
- + {0x00, 0x18, 0x7E, 0x3C, 0x7E, 0x18, 0x00, 0x00}, /* * */
- + {0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00}, /* + */
- + {0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30}, /* , */
- + {0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00}, /* - */
- + {0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00}, /* . */
- + {0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00}, /* / */
- + {0x3C, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x3C, 0x00}, /* 0 */
- + {0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00}, /* 1 */
- + {0x3C, 0x66, 0x06, 0x0C, 0x18, 0x30, 0x7E, 0x00}, /* 2 */
- + {0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00}, /* 3 */
- + {0x0C, 0x1C, 0x3C, 0x6C, 0x7E, 0x0C, 0x0C, 0x00}, /* 4 */
- + {0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00}, /* 5 */
- + {0x1C, 0x30, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00}, /* 6 */
- + {0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00}, /* 7 */
- + {0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00}, /* 8 */
- + {0x3C, 0x66, 0x66, 0x3E, 0x06, 0x0C, 0x38, 0x00}, /* 9 */
- + {0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00}, /* : */
- + {0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30}, /* ; */
- + {0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00}, /* < */
- + {0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00}, /* = */
- + {0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x00}, /* > */
- + {0x3C, 0x66, 0x0C, 0x18, 0x18, 0x00, 0x18, 0x00}, /* ? */
- + {0x3C, 0x66, 0x6E, 0x6A, 0x6E, 0x60, 0x3C, 0x00}, /* @ */
- + {0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00}, /* A */
- + {0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00}, /* B */
- + {0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00}, /* C */
- + {0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00}, /* D */
- + {0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00}, /* E */
- + {0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x00}, /* F */
- + {0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00}, /* G */
- + {0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00}, /* H */
- + {0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00}, /* I */
- + {0x3E, 0x0C, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00}, /* J */
- + {0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00}, /* K */
- + {0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00}, /* L */
- + {0x63, 0x77, 0x7F, 0x6B, 0x6B, 0x63, 0x63, 0x00}, /* M */
- + {0x66, 0x66, 0x76, 0x7E, 0x6E, 0x66, 0x66, 0x00}, /* N */
- + {0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00}, /* O */
- + {0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00}, /* P */
- + {0x3C, 0x66, 0x66, 0x66, 0x6A, 0x6C, 0x36, 0x00}, /* Q */
- + {0x7C, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x00}, /* R */
- + {0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00}, /* S */
- + {0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, /* T */
- + {0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00}, /* U */
- + {0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00}, /* V */
- + {0x63, 0x63, 0x6B, 0x6B, 0x7F, 0x77, 0x63, 0x00}, /* W */
- + {0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00}, /* X */
- + {0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00}, /* Y */
- + {0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00}, /* Z */
- + {0x7C, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7C, 0x00}, /* [ */
- + {0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00}, /* \ */
- + {0x3E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3E, 0x00}, /* ] */
- + {0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ^ */
- + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, /* _ */
- + {0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ` */
- + {0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00}, /* a */
- + {0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x00}, /* b */
- + {0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C, 0x00}, /* c */
- + {0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00}, /* d */
- + {0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00}, /* e */
- + {0x1C, 0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x00}, /* f */
- + {0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x3C}, /* g */
- + {0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00}, /* h */
- + {0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00}, /* i */
- + {0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x70}, /* j */
- + {0x60, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x00}, /* k */
- + {0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00}, /* l */
- + {0x00, 0x00, 0x36, 0x7F, 0x6B, 0x6B, 0x63, 0x00}, /* m */
- + {0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00}, /* n */
- + {0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00}, /* o */
- + {0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60}, /* p */
- + {0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x07}, /* q */
- + {0x00, 0x00, 0x6C, 0x76, 0x60, 0x60, 0x60, 0x00}, /* r */
- + {0x00, 0x00, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x00}, /* s */
- + {0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x1C, 0x00}, /* t */
- + {0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00}, /* u */
- + {0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00}, /* v */
- + {0x00, 0x00, 0x63, 0x6B, 0x6B, 0x7F, 0x36, 0x00}, /* w */
- + {0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00}, /* x */
- + {0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x3C}, /* y */
- + {0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00}, /* z */
- + {0x0C, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0C, 0x00}, /* { */
- + {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, /* | */
- + {0x30, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x30, 0x00}, /* } */
- + {0x31, 0x6B, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ~ */
- + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} /* */
- +};
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/console/serialecho.c linux/arch/arm/drivers/char/console/serialecho.c
- --- linux.orig/arch/arm/drivers/char/console/serialecho.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/console/serialecho.c Fri May 10 13:34:51 1996
- @@ -0,0 +1,116 @@
- +/*
- + * linux/arch/arm/drivers/char/serialecho.c
- + *
- + * Serial echoing for kernel console messages.
- + */
- +#if defined (CONFIG_ARCH_A5K) || defined (CONFIG_ARCH_RPC)
- +
- +#include <linux/serial_reg.h>
- +
- +extern int serial_echo_init (int base);
- +extern int serial_echo_print (const char *s);
- +
- +/*
- + * this defines the address for the port to which printk echoing is done
- + * when CONFIG_SERIAL_ECHO is defined
- + */
- +#define SERIAL_ECHO_PORT 0x3f8 /* internal serial port */
- +
- +static int serial_echo_port = 0;
- +
- +#define serial_echo_outb(v,a) outb((v),(a)+serial_echo_port)
- +#define serial_echo_inb(a) inb((a)+serial_echo_port)
- +
- +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
- +
- +/* wait for the transmitter & holding register to empty */
- +#define WAIT_FOR_XMITR \
- + do { \
- + lsr = serial_echo_inb (UART_LSR); \
- + } while ((lsr & BOTH_EMPTY) != BOTH_EMPTY)
- +
- +/*
- + * These two functions abstract the actual communications with the
- + * debug port. This is so we can change the underlying communications
- + * mechanism without modifying the rest of the code.
- + */
- +int serial_echo_print (const char *s)
- +{
- + int lsr, ier;
- + int i;
- +
- + if (!serial_echo_port)
- + return;
- +
- + /*
- + * First save the IER then disable interrupts
- + */
- + ier = serial_echo_inb (UART_IER);
- + serial_echo_outb (0x00, UART_IER);
- +
- + /*
- + * Now do each character
- + */
- + for (i = 0; *s; i++, s++) {
- + WAIT_FOR_XMITR;
- +
- + /* send the character out. */
- + serial_echo_outb (*s, UART_TX);
- +
- + /* if a LF, also do CR... */
- + if (*s == 10) {
- + WAIT_FOR_XMITR;
- + serial_echo_outb (13, UART_TX);
- + }
- + }
- +
- + /*
- + * Finally, wait for transmitter & holding register to empty
- + * and restore the IER.
- + */
- + WAIT_FOR_XMITR;
- + serial_echo_outb (ier, UART_IER);
- +
- + return 0;
- +}
- +
- +int serial_echo_init (int base)
- +{
- + int comstat, hi, lo;
- +
- + if (base != 0x3f8) {
- + serial_echo_port = 0;
- + return 0;
- + } else
- + serial_echo_port = base;
- +
- + /*
- + * Read the Divisor Latch
- + */
- + comstat = serial_echo_inb (UART_LCR);
- + serial_echo_outb (comstat | UART_LCR_DLAB, UART_LCR);
- + hi = serial_echo_inb (UART_DLM);
- + lo = serial_echo_outb (UART_DLL);
- + serial_echo_outb (comstat);
- +
- + /*
- + * now do a hardwired init
- + */
- + serial_echo_outb (0x03, UART_LCR); /* No parity, 8 bits, 1 stop */
- + serial_echo_outb (0x83, UART_LCR); /* Access divisor latch */
- + serial_echo_outb (0x00, UART_DLM); /* 9600 baud */
- + serial_echo_outb (0x0c, UART_DLL); /* Done with divisor */
- +
- + /*
- + * Prior to disabling interrupts, read the LSR and RBR
- + * registers
- + */
- + comstat = serial_echo_inb (UART_LSR); /* COM? LSR */
- + comstat = serial_echo_inb (UART_RX); /* COM? RBR */
- + serial_echo_outb (0x00, UART_IER); /* Disable all interrupts */
- +
- + return 0;
- +}
- +
- +#endif
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/console.c linux/arch/arm/drivers/char/console.c
- --- linux.orig/arch/arm/drivers/char/console.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/console.c Fri Sep 6 21:11:28 1996
- @@ -0,0 +1,2339 @@
- +/*
- + * linux/arch/arm/drivers/char/console.c
- + *
- + * Modifications (C) 1995, 1996 Russell King
- + */
- +
- +/*
- + * This module exports the console io functions:
- + *
- + * 'int vcd_init (struct vt *vt, int kmallocok, unsigned long *kmem)'
- + * 'unsigned long vcd_pre_init (unsigned long kmem, struct vt *vt)'
- + * 'void vcd_disallocate (struct vt *vt)'
- + * 'int vcd_resize (unsigned long lines, unsigned long cols)'
- + * 'void vcd_blankscreen (int nopowersave)'
- + * 'void vcd_unblankscreen (void)'
- + * 'void vcd_savestate (const struct vt *vt, int blanked)'
- + * 'void vcd_restorestate (const struct vt *vt)'
- + * 'void vcd_setup_graphics (const struct vt *vt)'
- + * 'int vcd_write (const struct vt *vt, int from_user, const unsigned char *buf, int count)'
- + * 'int vcd_ioctl (const struct vt *vt, int cmd, unsigned long arg)'
- + *
- + *
- + * 'int vc_allocate(unsigned int console)'
- + * 'int vc_cons_allocated (unsigned int console)'
- + * 'int vc_resize(unsigned long lines,unsigned long cols)'
- + * 'void vc_disallocate(unsigned int currcons)'
- + *
- + * 'unsigned long con_init(unsigned long)'
- + * S 'int con_open(struct tty_struct *tty,struct file *filp)'
- + * S 'void con_write(struct tty_struct *tty)'
- + * S 'void console_print(const char *b)'
- + * 'void update_screen(int new_console)'
- + *
- + * 'void blank_screen(void)'
- + * 'void unblank_screen(void)'
- + * 'void scrollback(int lines)' *
- + * 'void scrollfront(int lines)' *
- + * 'int do_screendump(int arg)'
- + *
- + * 'int con_get_font(char *)'
- + * 'int con_set_font(char *)'
- + * 'int con_get_trans(char *)'
- + * 'int con_set_trans(char *)'
- + *
- + * 'int mouse_reporting(void)'
- + */
- +
- +#define BLANK 0x0020
- +/* A bitmap for codes <32. A bit of 1 indicates that the code
- + * corresponding to that bit number invokes a special action
- + * (such as cursor movement) and should not be displayed as a
- + * glyph unless the disp_ctrl mode is explicitly enabled.
- + */
- +#define CTRL_ACTION 0x0d00ff81
- +#define CTRL_ALWAYS 0x0800f501 /* Cannot be overriden by disp_ctrl */
- +
- +#include <linux/config.h>
- +#include <linux/sched.h>
- +#include <linux/timer.h>
- +#include <linux/interrupt.h>
- +#include <linux/tty.h>
- +#include <linux/tty_flip.h>
- +#include <linux/kernel.h>
- +#include <linux/errno.h>
- +#include <linux/kd.h>
- +#include <linux/major.h>
- +#include <linux/mm.h>
- +#include <linux/malloc.h>
- +
- +#include <asm/segment.h>
- +#include <asm/irq-no.h>
- +
- +#define DEBUG
- +
- +#include "kbd_kern.h"
- +#include "consolemap.h"
- +#include "vt_kern.h"
- +#include "selection.h"
- +
- +#define set_kbd(x) set_vc_kbd_mode (vt->kbd, x)
- +#define clr_kbd(x) clr_vc_kbd_mode (vt->kbd, x)
- +#define is_kbd(x) vc_kbd_mode (vt->kbd, x)
- +
- +#define decarm VC_REPEAT
- +#define decckm VC_CKMODE
- +#define kbdapplic VC_APPLIC
- +#define lnm VC_CRLF
- +
- +/*
- + * this is what the terminal answers to a ESC-Z or csi0c query.
- + */
- +#define VT100ID "\033[?1;2c"
- +#define VT102ID "\033[?6c"
- +
- +extern int setup_arm_irq(int, struct irqaction *);
- +
- +int bytes_per_char_h, bytes_per_char_v, video_num_columns;
- +
- +/*
- + * routines to load custom translation table, EGA/VGA font and
- + * VGA colour palette from console.c
- + */
- +extern int con_set_trans_old(unsigned char * table);
- +extern int con_get_trans_old(unsigned char * table);
- +extern int con_set_trans_new(unsigned short * table);
- +extern int con_get_trans_new(unsigned short * table);
- +extern void con_clear_unimap(struct unimapinit *ui);
- +extern int con_set_unimap(ushort ct, struct unipair *list);
- +extern int con_get_unimap(ushort ct, ushort *uct, struct unipair *list);
- +extern void con_set_default_unimap(void);
- +extern int con_set_font(char * fontmap);
- +extern int con_get_font(char * fontmap);
- +
- +
- +/* ARM Extensions */
- +extern void ll_write_char(unsigned long ps, unsigned long chinfo);
- +extern void memc_write (int reg, int val);
- +extern void vidc_write (int reg, int val);
- +extern void memsetl (void *ptr, unsigned long word, int length);
- +extern void map_screen_mem (unsigned long vid_base, int remap);
- +static void ll_erase (const struct vt * const vt, unsigned char sx, unsigned char sy,
- + unsigned char cx, unsigned char cy);
- +
- +#include <linux/ctype.h>
- +
- +#ifndef MIN
- +#define MIN(a,b) ((a) < (b) ? (a) : (b))
- +#endif
- +
- +static void gotoxy (const struct vt * const vt, int new_x, int new_y);
- +static inline void set_cursor (const struct vt * const vt);
- +extern void reset_vc(const struct vt * const vt);
- +extern void register_console(void (*proc)(const char *));
- +extern void compute_shiftstate(void);
- +extern void con_reset_palette (const struct vt * const vt);
- +extern void con_set_palette (const struct vt * const vt);
- +
- +static int can_do_color = 1;
- +static int printable; /* Is console ready for printing? */
- +
- +#define console_charmask 0xff
- +#ifndef console_charmask
- +static unsigned short console_charmask = 0x0ff;
- +#endif
- +
- +#include "console/font.h"
- +
- +static const unsigned long palette_1[] = {
- + 0x1000, /* Black */
- + 0x1fff, /* White */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000 /* Black */
- +};
- +
- +static const unsigned long palette_4[] = {
- + 0x1000, /* Black */
- + 0x100c, /* Red */
- + 0x10c0, /* Green */
- + 0x10cc, /* Yellow */
- + 0x1c00, /* Blue */
- + 0x1c0c, /* Magenta */
- + 0x1cc0, /* Cyan */
- + 0x1ccc, /* White */
- + 0x1000,
- + 0x100f,
- + 0x10f0,
- + 0x10ff,
- + 0x1f00,
- + 0x1f0F,
- + 0x1ff0,
- + 0x1fff,
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000
- +};
- +
- +static const unsigned long palette_8[] = {
- + 0x1000,
- + 0x1111,
- + 0x1222,
- + 0x1333,
- + 0x1400,
- + 0x1511,
- + 0x1622,
- + 0x1733,
- + 0x1004,
- + 0x1115,
- + 0x1226,
- + 0x1337,
- + 0x1404,
- + 0x1515,
- + 0x1626,
- + 0x1737,
- + 0x1000, /* Black */
- + 0x1000, /* Black */
- + 0x1000
- +};
- +
- +static const unsigned long *default_palette_entries = palette_4;
- +
- +static const unsigned char color_1[] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 };
- +static const unsigned char color_4[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 };
- +static const unsigned char color_8[] = {0x00, 0x18, 0x60, 0x78, 0x84, 0x9C, 0xE4, 0xFC,
- + 0x00, 0x1B, 0x63, 0x7B, 0x87, 0x9F, 0xE7, 0xFF};
- +static const unsigned char *color_table = color_4;
- +
- +/*===================================================================================*/
- +
- +#ifdef CONFIG_SERIAL_ECHO
- +#include "console/serialecho.c"
- +#endif
- +
- +#ifdef DEBUG
- +static int vcd_validate (struct con_struct *vcd, const char *msg)
- +{
- + unsigned long old_origin, old_pos, w = 0;
- +
- + old_origin = vcd->screen.origin;
- + if (vcd->screen.origin < 0x01f80000 || vcd->screen.origin > 0x02000000) {
- + vcd->screen.origin = 0x02000000;
- + w = 1;
- + }
- + old_pos = vcd->screen.pos;
- + if (vcd->screen.pos < 0x01f80000 || vcd->screen.pos > 0x02080000) {
- + vcd->screen.pos = 0x02000000;
- + w = 1;
- + }
- + if (w)
- + printk ("*** CONSOLE ERROR: %s: (%p): origin = %08lX pos = %08lX ***\n", msg, vcd, old_origin, old_pos);
- + return w;
- +}
- +#endif
- +
- +void no_scroll(char *str, int *ints)
- +{
- +}
- +
- +static unsigned long __real_origin;
- +static unsigned long __origin; /* Offset of currently displayed screen */
- +
- +static void __set_origin(unsigned long offset)
- +{
- + unsigned long flags;
- +
- + clear_selection ();
- +
- + save_flags_cli (flags);
- + __origin = offset;
- +
- + memc_write(0, offset >> 2);
- + memc_write(1, 0);
- + memc_write(2, vtdata.screen.memmask >> 2);
- + restore_flags(flags);
- +}
- +
- +void scrollback(int lines)
- +{
- +}
- +
- +void scrollfront(int lines)
- +{
- +}
- +
- +static void set_origin (const struct vt * const vt)
- +{
- + if (vtdata.fgconsole != vt || vt->vtd->vc_mode == KD_GRAPHICS)
- + return;
- + __real_origin = vt->vcd->screen.origin - vtdata.screen.memstart;
- + if (__real_origin > vtdata.screen.memmask)
- + __real_origin -= vtdata.screen.memmask + 1;
- + __set_origin(__real_origin);
- +}
- +
- +/* -------------------------------------------------------------------------------
- + * Cursor
- + * ------------------------------------------------------------------------------- */
- +
- +static char cursor_on = 0;
- +static unsigned long cp;
- +
- +static void put_cursor(char on_off,unsigned long newcp)
- +{
- + static char con;
- + unsigned long cp_p = cp;
- + int c = vtdata.screen.bytespercharh == 8 ? color_table[15] : 0x11 * color_table[15];
- + int i;
- +
- + if (vtdata.fgconsole->vtd->vc_mode == KD_GRAPHICS)
- + return;
- +
- + cp = newcp;
- +
- + if (con != on_off) {
- + if (cp_p != -1)
- + for (i = 0; i < vtdata.screen.bytespercharh; i++)
- + ((unsigned char*)cp_p)[i]^=c;
- + con = on_off;
- + }
- +}
- +
- +static void vsync_irq(int irq, void *dev_id, struct pt_regs *regs)
- +{
- + static char cursor_flash;
- +
- + if(++cursor_flash==16) {
- + cursor_flash=0;
- + cursor_on = cursor_on ? 0 : 1;
- + if (vtdata.fgconsole->vcd->screen.cursoron > 0)
- + put_cursor(cursor_on,cp);
- + }
- +}
- +
- +static void hide_cursor(void)
- +{
- + put_cursor (0, -1);
- +}
- +
- +static void set_cursor (const struct vt * const vt)
- +{
- + unsigned long flags;
- +
- + if (vt != vtdata.fgconsole || vt->vtd->vc_mode == KD_GRAPHICS)
- + return;
- +
- + if(__real_origin != __origin)
- + __set_origin (__real_origin);
- +
- + save_flags_cli (flags);
- + if (vt->vcd->deccm) {
- +#ifdef DEBUG
- + vcd_validate (vt->vcd, "set_cursor");
- +#endif
- + cp = vt->vcd->screen.pos + vtdata.numcolumns *
- + vtdata.screen.bytespercharh * (vtdata.screen.bytespercharv - 1);
- + } else
- + hide_cursor();
- + restore_flags(flags);
- +}
- +
- +static void vcd_removecursors (const struct vt *vt)
- +{
- + unsigned long flags;
- +
- + save_flags_cli (flags);
- +
- + if (--vt->vcd->screen.cursoron == 0 && vtdata.fgconsole == vt)
- + put_cursor (0, cp);
- +
- + restore_flags (flags);
- +}
- +
- +static void vcd_restorecursors(const struct vt *vt)
- +{
- + unsigned long flags;
- +
- + save_flags_cli (flags);
- +
- + if (++vt->vcd->screen.cursoron == 1 && cursor_on && vtdata.fgconsole == vt)
- + put_cursor (1, cp);
- +
- + restore_flags (flags);
- +}
- +
- +/* -----------------------------------------------------------------------------------------
- + * VC stuff
- + * ----------------------------------------------------------------------------------------- */
- +/*
- + * gotoxy() must verify all boundaries, because the arguments
- + * might also be negative. If a given position is out of
- + * bounds, the cursor is placed at the nearest margin.
- + */
- +static void gotoxy (const struct vt * const vt, int new_x, int new_y)
- +{
- + struct con_struct *vcd = vt->vcd;
- + int min_y, max_y;
- +
- + if (new_x < 0)
- + vcd->curstate.x = 0;
- + else
- + if (new_x >= vtdata.numcolumns)
- + vcd->curstate.x = vtdata.numcolumns - 1;
- + else
- + vcd->curstate.x = new_x;
- +
- + if (vcd->decom) {
- + new_y += vcd->top;
- + min_y = vcd->top;
- + max_y = vcd->bottom;
- + } else {
- + min_y = 0;
- + max_y = vtdata.numrows;
- + }
- +
- + if (new_y < min_y)
- + vcd->curstate.y = min_y;
- + else if (new_y >= max_y)
- + vcd->curstate.y = max_y - 1;
- + else
- + vcd->curstate.y = new_y;
- +
- + vcd->screen.pos = vcd->screen.origin +
- + vcd->curstate.y * vtdata.screen.sizerow +
- + vcd->curstate.x * vtdata.screen.bytespercharh;
- + vcd->buffer.pos = vcd->curstate.y * vtdata.buffer.sizerow +
- + vcd->curstate.x;
- + vcd->need_wrap = 0;
- +#ifdef DEBUG
- + vcd_validate (vcd, "gotoxy");
- +#endif
- +}
- +
- +/* for absolute user moves, when decom is set */
- +static void gotoxay (const struct vt * const vt, int new_x, int new_y)
- +{
- + gotoxy(vt, new_x, vt->vcd->decom ? (vt->vcd->top+new_y) : new_y);
- +}
- +
- +/* --------------------------------------------------------------------------------
- + * Screen scrolling
- + * -------------------------------------------------------------------------------- */
- +
- +static void scrollup (const struct vt * const vt, unsigned int t, unsigned int b, unsigned int l)
- +{
- + struct con_struct *vcd = vt->vcd;
- + int old_top, new_top, old_end, new_end, displayed;
- +
- + if (b > vtdata.numrows || t >= b || l == 0)
- + return;
- +
- + displayed = vtdata.fgconsole == vt;
- +
- + if (t == 0 && b == vtdata.numrows && displayed) {
- + /*
- + * we can hardware scroll...
- + */
- + memsetl ((void *)(vcd->screen.origin + vtdata.screen.sizerow * vtdata.numrows),
- + vcd->cached_backcolwrd, l * vtdata.screen.sizerow);
- +
- + vcd->screen.pos -= vcd->screen.origin;
- + vcd->screen.origin += l * vtdata.screen.sizerow;
- + if (vcd->screen.origin >= vtdata.screen.memend)
- + vcd->screen.origin = vcd->screen.origin - vtdata.screen.memend + vtdata.screen.memstart;
- + vcd->screen.pos += vcd->screen.origin;
- + set_origin (vt);
- + displayed = 0;
- + }
- +
- + old_top = t;
- + new_top = old_top + l;
- + old_end = b - l;
- + new_end = b;
- +
- + if (new_top < new_end) { /* we have something to move */
- + memmove (vcd->buffer.buffer + old_top * vtdata.buffer.sizerow,
- + vcd->buffer.buffer + new_top * vtdata.buffer.sizerow,
- + (new_end - new_top) * vtdata.buffer.sizerow * 4);
- +
- + if (displayed)
- + memmove ((void *)(vcd->screen.origin + old_top * vtdata.screen.sizerow),
- + (void *)(vcd->screen.origin + new_top * vtdata.screen.sizerow),
- + (new_end - new_top) * vtdata.screen.sizerow);
- + }
- +
- + if (old_end < new_end) {
- + if (old_end < old_top)
- + old_end = old_top;
- + memsetl (vcd->buffer.buffer + old_end * vtdata.buffer.sizerow,
- + vcd->combined_state | 32, (new_end - old_end) * vtdata.buffer.sizerow * 4);
- +
- + if (displayed)
- + memsetl ((void *)(vcd->screen.origin + old_end * vtdata.screen.sizerow),
- + vcd->cached_backcolwrd, (new_end - old_end) * vtdata.screen.sizerow);
- + }
- +}
- +
- +static void scrolldown (const struct vt * const vt, unsigned int t, unsigned int b, unsigned int l)
- +{
- + struct con_struct *vcd = vt->vcd;
- + int old_top, new_top, old_end, new_end, displayed;
- +
- + if (b > vtdata.numrows || t >= b)
- + return;
- +
- + displayed = vtdata.fgconsole == vt;
- +
- + if (t == 0 && b == vtdata.numrows && displayed) {
- + /*
- + * we can hardware scroll...
- + */
- + vcd->screen.pos -= vcd->screen.origin;
- + vcd->screen.origin -= l * vtdata.screen.sizerow;
- + if (vcd->screen.origin < vtdata.screen.memstart)
- + vcd->screen.origin = vcd->screen.origin - vtdata.screen.memstart + vtdata.screen.memend;
- + vcd->screen.pos += vcd->screen.origin;
- + memsetl ((void *)vcd->screen.origin, vcd->cached_backcolwrd, l * vtdata.screen.sizerow);
- + set_origin (vt);
- + displayed = 0;
- + }
- +
- + new_top = t;
- + old_top = new_top + l;
- + new_end = b - l;
- + old_end = b;
- +
- + if (new_top < new_end) { /* we have something to move */
- + memmove (vcd->buffer.buffer + old_top * vtdata.buffer.sizerow,
- + vcd->buffer.buffer + new_top * vtdata.buffer.sizerow,
- + (new_end - new_top) * vtdata.buffer.sizerow * 4);
- +
- + if (displayed)
- + memmove ((void *)(vcd->screen.origin + old_top * vtdata.screen.sizerow),
- + (void *)(vcd->screen.origin + new_top * vtdata.screen.sizerow),
- + (new_end - new_top) * vtdata.screen.sizerow);
- + }
- +
- + if (new_top < old_top) { /* we have something to clear */
- + if (old_top > old_end)
- + old_top = old_end;
- + memsetl (vcd->buffer.buffer + new_top * vtdata.buffer.sizerow,
- + vcd->combined_state | 32, (old_top - new_top) * vtdata.buffer.sizerow * 4);
- +
- + if (displayed)
- + memsetl ((void *)(vcd->screen.origin + new_top * vtdata.screen.sizerow),
- + vcd->cached_backcolwrd, (old_top - new_top) * vtdata.screen.sizerow);
- + }
- +}
- +
- +static void csi_J (const struct vt * const vt, int vpar)
- +{
- + unsigned char endx, endy;
- + unsigned char startx, starty;
- +
- + switch (vpar) {
- + case 0: /* erase from cursor to bottom of screen (including char at (x, y) */
- + startx = vt->vcd->curstate.x;
- + starty = vt->vcd->curstate.y;
- + endx = vtdata.numcolumns - 1;
- + endy = vtdata.numrows - 1;
- + break;
- + case 1: /* erase from top of screen to cursor (including char at (x, y) */
- + startx = 0;
- + starty = 0;
- + endx = vt->vcd->curstate.x;
- + endy = vt->vcd->curstate.y;
- + break;
- + case 2: /* erase entire screen */
- + startx = 0;
- + starty = 0;
- + endx = vtdata.numcolumns - 1;
- + endy = vtdata.numrows - 1;
- +#if TODO
- + origin = video_mem_base;
- + set_origin (currcons);
- + gotoxy (currcons, x, y);
- +#endif
- + break;
- + default:
- + return;
- + }
- + ll_erase (vt, startx, starty, endx, endy);
- + /*vt->vcd->need_wrap = 0; why? We don't move the cursor... */
- +}
- +
- +static void csi_K (const struct vt * const vt, int vpar)
- +{
- + unsigned char endx;
- + unsigned char startx;
- +
- + switch(vpar) {
- + case 0: /* erase from cursor to end of line */
- + startx = vt->vcd->curstate.x;
- + endx = vtdata.numcolumns - 1;
- + break;
- + case 1: /* erase from beginning of line to cursor */
- + startx = 0;
- + endx = vt->vcd->curstate.x;
- + break;
- + case 2: /* erase entire line */
- + startx = 0;
- + endx = vtdata.numcolumns - 1;
- + break;
- + default:
- + return;
- + }
- + ll_erase (vt, startx, vt->vcd->curstate.y, endx, vt->vcd->curstate.y);
- + /*vt->vcd->need_wrap = 0; why? We don't move the cursor... */
- +}
- +
- +static void csi_X (const struct vt * const vt, int vpar) /* erase the following vpar positions */
- +{ /* not vt100? */
- + unsigned char countx, county;
- + unsigned char startx, starty;
- +
- + if (!vpar)
- + vpar++;
- +
- + startx = vt->vcd->curstate.x;
- + starty = vt->vcd->curstate.y;
- + countx = 0;
- + county = 0;
- +#if TODO
- + ll_erase(currcons,startx,starty,countx,county);
- +#endif
- + /*vt->vcd->need_wrap = 0; why? We don't move the cursor... */
- +}
- +
- +static void update_attr (const struct vt * const vt)
- +{
- + vt->vcd->combined_state = (vt->vcd->curstate.flags << 24) |
- + (vt->vcd->curstate.backcol << 16) |
- + (vt->vcd->curstate.forecol << 8);
- + switch (vtdata.screen.bitsperpix) {
- + case 1:
- + vt->vcd->cached_backcolwrd = 0;
- + break;
- + default:
- + case 4:
- + vt->vcd->cached_backcolwrd = 0x11111111 * vt->vcd->curstate.backcol;
- + break;
- + case 8:
- + vt->vcd->cached_backcolwrd = 0x01010101 * vt->vcd->curstate.backcol;
- + break;
- + }
- +}
- +
- +static void default_attr (const struct vt * const vt)
- +{
- + vt->vcd->curstate.flags &= ~(FLG_INVERSE|FLG_FLASH|FLG_UNDERLINE|FLG_ITALIC|FLG_BOLD);
- + vt->vcd->curstate.forecol = vt->vcd->def_forecol;
- + vt->vcd->curstate.backcol = vt->vcd->def_backcol;
- +}
- +
- +static int csi_m (const struct vt * const vt)
- +{
- + struct con_struct *vcd = vt->vcd;
- + int i;
- +
- + for (i = 0; i <= vcd->npar; i++) {
- + switch (vcd->par[i]) {
- + case 0:
- + default_attr (vt);
- + break;
- + case 1:
- + vcd->curstate.flags |= FLG_BOLD;
- + break; /* Bold */
- + case 2:
- + vcd->curstate.flags &= ~FLG_BOLD;
- + break; /* Feint */
- + case 3:
- + vcd->curstate.flags |= FLG_ITALIC;
- + break; /* Italic */
- + case 4:
- + vcd->curstate.flags |= FLG_UNDERLINE;
- + break; /* Underline */
- + case 5:
- + case 6:
- + vcd->curstate.flags |= FLG_FLASH;
- + break; /* Flash */
- + case 7:
- + vcd->curstate.flags |= FLG_INVERSE;
- + break; /* Inverse chars */
- +
- + case 10:
- + /* ANSI X3.64-1979 (SCO-ish?)
- + * Select primary font, don't display
- + * control chars if defined, don't set
- + * bit 8 on output.
- + */
- + vcd->translate = set_translate(vcd->curstate.flags & FLG_CHRSET
- + ? vcd->curstate.G1_charset
- + : vcd->curstate.G0_charset);
- + vcd->disp_ctrl = 0;
- + vcd->toggle_meta = 0;
- + return 1;
- + case 11:
- + /* ANSI X3.64-1979 (SCO-ish?)
- + * Select first alternate font, let's
- + * chars < 32 be displayed as ROM chars.
- + */
- + vcd->translate = set_translate(IBMPC_MAP);
- + vcd->disp_ctrl = 1;
- + vcd->toggle_meta = 0;
- + return 1;
- + case 12:
- + /* ANSI X3.64-1979 (SCO-ish?)
- + * Select second alternate font, toggle
- + * high bit before displaying as ROM char.
- + */
- + vcd->translate = set_translate(IBMPC_MAP);
- + vcd->disp_ctrl = 1;
- + vcd->toggle_meta = 1;
- + return 1;
- + case 21:
- + case 22:
- + vcd->curstate.flags &= ~FLG_BOLD;
- + break;
- + case 24:
- + vcd->curstate.flags &= ~FLG_UNDERLINE;
- + break;
- + case 25:
- + vcd->curstate.flags &= ~FLG_FLASH;
- + break;
- + case 27:
- + vcd->curstate.flags &= ~FLG_INVERSE;
- + break;
- + case 30:
- + case 31:
- + case 32:
- + case 33:
- + case 34:
- + case 35:
- + case 36:
- + case 37:
- + vcd->curstate.forecol = color_table[vcd->par[i]-30];
- + break; /* Foreground colour */
- + case 38:
- + vcd->curstate.forecol = vcd->def_forecol;
- + vcd->curstate.flags &= ~FLG_UNDERLINE;
- + break;
- + case 39:
- + vcd->curstate.forecol = vcd->def_forecol;
- + vcd->curstate.flags &= ~FLG_UNDERLINE;
- + break; /* Default foreground colour */
- + case 40:
- + case 41:
- + case 42:
- + case 43:
- + case 44:
- + case 45:
- + case 46:
- + case 47:
- + vcd->curstate.backcol = color_table[vcd->par[i]-40];
- + break; /* Background colour */
- + case 49:
- + vcd->curstate.backcol = vcd->def_backcol;
- + break; /* Default background colour */
- + }
- + }
- + update_attr (vt);
- + return 0;
- +}
- +
- +static void respond_string (char *p, struct tty_struct *tty)
- +{
- + while (*p)
- + tty_insert_flip_char (tty, *p++, 0);
- + tty_schedule_flip (tty);
- +}
- +
- +static void cursor_report (const struct vt *vt)
- +{
- + char buf[40];
- +
- + sprintf (buf, "\033[%d;%dR", vt->vcd->curstate.y + (vt->vcd->decom ? vt->vcd->top + 1 : 1),
- + vt->vcd->curstate.x + 1);
- + respond_string (buf, *vt->tty);
- +}
- +
- +static void status_report (struct tty_struct *tty)
- +{
- + respond_string ("\033[0n", tty);
- +}
- +
- +static void respond_ID (struct tty_struct *tty)
- +{
- + respond_string(VT102ID, tty);
- +}
- +
- +void mouse_report (struct tty_struct *tty, int butt, int mrx, int mry)
- +{
- + char buf[8];
- +
- + sprintf (buf,"\033[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
- + (char)('!'+mry));
- + respond_string(buf, tty);
- +}
- +
- +/* invoked by ioctl(TIOCLINUX) */
- +int mouse_reporting (void)
- +{
- + return vtdata.fgconsole->vcd->report_mouse;
- +}
- +
- +static inline unsigned long *bufferpos (const struct vt * const vt, int offset)
- +{
- + return vt->vcd->buffer.buffer + offset;
- +}
- +
- +static inline unsigned long screenpos (const struct vt * const vt, int offset)
- +{
- + int hx, hy;
- +
- + hx = offset % vtdata.numcolumns;
- + hy = offset / vtdata.numcolumns;
- + return vt->vcd->screen.origin + hy * vtdata.screen.sizerow + hx * vtdata.screen.bytespercharh;
- +}
- +
- +void invert_screen (const struct vt * const vt, int offset, int count)
- +{
- + int i;
- + unsigned long *buffer = vt->vcd->buffer.buffer + offset;
- + unsigned long p, pp;
- +
- + for (i = 0; i <= count; i++)
- + buffer[i] ^= FLG_INVERSE << 24;
- +
- + if (vt == vtdata.fgconsole) {
- + int hx, hy, hex, hey;
- +
- + hx = offset % vtdata.numcolumns;
- + hy = offset / vtdata.numcolumns;
- + hex = (offset + count) % vtdata.numcolumns;
- + hey = (offset + count) / vtdata.numcolumns;
- +
- + p = vt->vcd->screen.origin + hy * vtdata.screen.sizerow;
- + for (;hy <= hey; hy ++) {
- + pp = p + hx * vtdata.screen.bytespercharh;
- + for (; hx < ((hy == hey) ? hex + 1 : vtdata.numcolumns); hx ++) {
- + ll_write_char (pp, *buffer++);
- + pp += vtdata.screen.bytespercharh;
- + }
- + hx = 0;
- + p += vtdata.screen.sizerow;
- + }
- + }
- +}
- +
- +void complement_pos (const struct vt * const vt, int offset)
- +{
- + static unsigned long p = 0;
- + static unsigned long old = 0;
- + unsigned long complement = 0;
- +
- + switch (vtdata.screen.bitsperpix) {
- + case 4:
- + complement = 0x00070700;
- + break;
- + case 8:
- + complement = 0x00fcfc00;
- + break;
- + }
- +
- + if (p)
- + ll_write_char (p, old);
- + if (offset == -1)
- + p = 0;
- + else {
- + p = screenpos (vt, offset);
- + old = vt->vcd->buffer.buffer[offset];
- + ll_write_char (p, old ^ complement);
- + }
- +}
- +
- +unsigned long screen_word (const struct vt * const vt, int offset)
- +{
- + return *bufferpos (vt, offset);
- +}
- +
- +int scrw2glyph (unsigned long scr_word)
- +{
- + return scr_word & 255;
- +}
- +
- +unsigned long *screen_pos (const struct vt * const vt, int offset)
- +{
- + return bufferpos (vt, offset);
- +}
- +
- +void getconsxy (const struct vt * const vt, char *p)
- +{
- + p[0] = vt->vcd->curstate.x;
- + p[1] = vt->vcd->curstate.y;
- +}
- +
- +void putconsxy (const struct vt * const vt, char *p)
- +{
- + gotoxy (vt, p[0], p[1]);
- + set_cursor (vt);
- +}
- +
- +static void set_mode (const struct vt * const vt, int on_off)
- +{
- + struct con_struct *vcd = vt->vcd;
- + int i;
- +
- + for (i = 0; i <= vcd->npar; i++)
- + if (vcd->ques)
- + switch(vcd->par[i]) { /* DEC private modes set/reset */
- + case 1: /* Cursor keys send ^[Ox/^[[x */
- + if (on_off)
- + set_kbd(decckm);
- + else
- + clr_kbd(decckm);
- + break;
- + case 3: /* 80/132 mode switch unimplemented */
- + vcd->deccolm = on_off;
- + csi_J (vt, 2);
- + gotoxy (vt, 0, 0);
- + break;
- + case 5: /* Inverted screen on/off */
- + if (vcd->decscnm != on_off) {
- + vcd->decscnm = on_off;
- + invert_screen (vt, 0, vtdata.screen.totsize);
- + update_attr (vt);
- + }
- + break;
- + case 6: /* Origin relative/absolute */
- + vcd->decom = on_off;
- + gotoxay (vt, 0, 0);
- + break;
- + case 7: /* Autowrap on/off */
- + vcd->decawm = on_off;
- + break;
- + case 8: /* Autorepeat on/off */
- + if (on_off)
- + set_kbd(decarm);
- + else
- + clr_kbd(decarm);
- + break;
- + case 9:
- + vcd->report_mouse = on_off ? 1 : 0;
- + break;
- + case 25: /* Cursor on/off */
- + vcd->deccm = on_off;
- + set_cursor (vt);
- + break;
- + case 1000:
- + vcd->report_mouse = on_off ? 2 : 0;
- + break;
- + }
- + else
- + switch(vcd->par[i]) { /* ANSI modes set/reset */
- + case 3: /* Monitor (display ctrls) */
- + vcd->disp_ctrl = on_off;
- + break;
- + case 4: /* Insert mode on/off */
- + vcd->decim = on_off;
- + break;
- + case 20: /* Lf, Enter = CrLf/Lf */
- + if (on_off)
- + set_kbd(lnm);
- + else
- + clr_kbd(lnm);
- + break;
- + }
- +}
- +
- +static void setterm_command (const struct vt * const vt)
- +{
- + switch (vt->vcd->par[0]) {
- + case 1: /* Set colour for underline mode (implemented as an underline) */
- + break;
- + case 2: /* set colour for half intensity mode (implemented as half) */
- + break;
- + case 8:
- + vt->vcd->def_forecol = vt->vcd->curstate.forecol;
- + vt->vcd->def_backcol = vt->vcd->curstate.backcol;
- + break;
- + case 9:
- + vtdata.screen.blankinterval = ((vt->vcd->par[1] < 60) ? vt->vcd->par[1] : 60) * 60 * HZ;
- + vt_pokeblankedconsole ();
- + break;
- + }
- +}
- +
- +static void insert_char (const struct vt * const vt)
- +{
- + register unsigned char *c,*cc;
- + register unsigned int row;
- + register int mcol, bpr, n = 1;
- +
- + /*
- + * Should really convert this to memmoves...
- + */
- + if (vtdata.fgconsole == vt) {
- + c = (unsigned char *)vt->vcd->screen.pos;
- + cc = (unsigned char *)c + vtdata.screen.bytespercharh * n;
- + mcol = (vtdata.numcolumns - vt->vcd->curstate.x - n) * vtdata.screen.bytespercharh - 1;
- + bpr = vtdata.numcolumns * vtdata.screen.bytespercharh;
- +
- + for (row = vtdata.screen.bytespercharv; row > 0; row --) {
- + register int col;
- + for(col = mcol; col >= 0; col--)
- + cc[col] = c[col];
- + for (col = n * vtdata.screen.bytespercharh - 1; col >= 0; col --)
- + c[col] = 0x11 * vt->vcd->curstate.backcol;
- + c += bpr;
- + cc += bpr;
- + }
- + }
- +
- + if (vt->vcd->curstate.x < vtdata.numcolumns - n)
- + memmove ((void *)vt->vcd->buffer.buffer + (vt->vcd->curstate.y * vtdata.numcolumns + vt->vcd->curstate.x + n),
- + (void *)vt->vcd->buffer.buffer + (vt->vcd->curstate.y * vtdata.numcolumns + vt->vcd->curstate.x),
- + (vtdata.numcolumns - vt->vcd->curstate.x - n) * 4);
- + memsetl ((void *)vt->vcd->buffer.buffer + (vt->vcd->curstate.y * vtdata.numcolumns + vt->vcd->curstate.x),
- + vt->vcd->combined_state | 32, n * 4);
- +}
- +
- +static inline void insert_line (const struct vt * const vt, int n)
- +{
- + scrolldown (vt, vt->vcd->curstate.y, vt->vcd->bottom, n);
- +/* vt->vcd->need_wrap = 0; why? We haven't moved the cursor. */
- +}
- +
- +static void delete_char (const struct vt * const vt)
- +{
- + register unsigned char *c, *cc;
- + register unsigned int row;
- + register int mcol, bpr, n = 1;
- +
- + /*
- + * Should really convert this to memmoves...
- + */
- + if (vtdata.fgconsole == vt) {
- + c = (unsigned char *)vt->vcd->screen.pos;
- + cc = (unsigned char *)c + vtdata.screen.bytespercharh * n;
- + mcol = vtdata.screen.bytespercharh * (vtdata.numcolumns - vt->vcd->curstate.x - n);
- + bpr = vtdata.numcolumns * vtdata.screen.bytespercharh;
- +
- + for (row = vtdata.screen.bytespercharv; row > 0; row --) {
- + register int col;
- +
- + for (col = 0; col < mcol; col++)
- + c[col] = cc[col];
- + for (; col < mcol + n * vtdata.screen.bytespercharh - 1; col ++)
- + c[col] = 0x11 * vt->vcd->curstate.backcol;
- + c += bpr;
- + cc += bpr;
- + }
- + }
- +
- + if (vt->vcd->curstate.x != vtdata.numcolumns - 1)
- + memmove ((void *)vt->vcd->buffer.buffer + (vt->vcd->curstate.y * vtdata.numcolumns + vt->vcd->curstate.x),
- + (void *)vt->vcd->buffer.buffer + (vt->vcd->curstate.y * vtdata.numcolumns + vt->vcd->curstate.x + n),
- + (vtdata.numcolumns - vt->vcd->curstate.x - n) * 4);
- + memsetl ((void *)vt->vcd->buffer.buffer + ((vt->vcd->curstate.y + 1) * vtdata.numcolumns - n),
- + vt->vcd->combined_state | 32, n * 4);
- +}
- +
- +static void delete_line (const struct vt * const vt, int n)
- +{
- + scrollup (vt, vt->vcd->curstate.y, vt->vcd->bottom, n);
- +/* vt->vcd->need_wrap = 0; why? we haven't moved the cursor. */
- +}
- +
- +static void csi_at (const struct vt * const vt, int nr)
- +{
- + if (nr > vtdata.numcolumns - vt->vcd->curstate.x)
- + nr = vtdata.numcolumns - vt->vcd->curstate.x;
- + else
- + if (!nr)
- + nr = 1;
- + while (nr--)
- + insert_char (vt);
- +}
- +
- +static void csi_L (const struct vt * const vt, int nr)
- +{
- + if (nr > vtdata.numrows - vt->vcd->curstate.y)
- + nr = vtdata.numrows - vt->vcd->curstate.y;
- + else
- + if (!nr)
- + nr = 1;
- + insert_line (vt, nr);
- +}
- +
- +static void csi_P (const struct vt * const vt, int nr)
- +{
- + if (nr > vtdata.numcolumns)
- + nr = vtdata.numcolumns;
- + else
- + if (!nr)
- + nr = 1;
- + while (nr--)
- + delete_char (vt);
- +}
- +
- +static void csi_M (const struct vt * const vt, int nr)
- +{
- + if (nr > vtdata.numrows)
- + nr = vtdata.numrows;
- + else
- + if (!nr)
- + nr = 1;
- + delete_line (vt, nr);
- +}
- +
- +enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
- + EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
- + ESpalette };
- +
- +static void reset_terminal (const struct vt * const vt, int initialising)
- +{
- + struct con_struct *vcd;
- +
- + vcd = vt->vcd;
- +
- + vcd->top = 0;
- + vcd->bottom = vtdata.numrows;
- + vcd->state = ESnormal;
- + vcd->ques = 0;
- + vcd->translate = set_translate (LAT1_MAP);
- + vcd->curstate.G0_charset = LAT1_MAP;
- + vcd->curstate.G1_charset = GRAF_MAP;
- + vcd->curstate.flags = 0;
- + vcd->need_wrap = 0;
- + vcd->report_mouse = 0;
- + vcd->utf = 0;
- + vcd->utf_count = 0;
- + vcd->disp_ctrl = 0;
- + vcd->toggle_meta = 0;
- + vcd->decscnm = 0;
- + vcd->decom = 0;
- + vcd->decawm = 1;
- + vcd->deccm = 1;
- + vcd->decim = 0;
- + vcd->curstate.forecol = color_table[7];
- + vcd->curstate.backcol = color_table[0];
- + vcd->def_forecol = color_table[7];
- + vcd->def_backcol = color_table[0];
- + vcd->tab_stop[0] = 0x01010100;
- + vcd->tab_stop[1] =
- + vcd->tab_stop[2] =
- + vcd->tab_stop[3] =
- + vcd->tab_stop[4] = 0x01010101;
- +
- + set_kbd (decarm);
- + clr_kbd (decckm);
- + clr_kbd(kbdapplic);
- + clr_kbd(lnm);
- +
- + vt->kbd->lockstate = 0;
- + vt->kbd->ledmode = LED_SHOW_FLAGS;
- + vt->kbd->ledflagstate = vt->kbd->default_ledflagstate;
- + if (!initialising)
- + set_leds ();
- +
- + if (vcd->screen.palette_entries) {
- + kfree (vcd->screen.palette_entries);
- + vcd->screen.palette_entries = 0;
- + }
- +
- + gotoxy (vt, 0, 0);
- + vcd->savedstate = vcd->curstate;
- + update_attr (vt);
- +
- + if (!initialising)
- + csi_J (vt, 2);
- +}
- +
- +void console_print(const char *b)
- +{
- + static int printing = 0;
- + struct vt *vt = vtdata.fgconsole;
- + struct con_struct * const vcd = vt->vcd;
- + unsigned long character, bufpos;
- + unsigned char c;
- +
- +#ifdef DEBUG
- + vcd_validate (vcd, "console_print entry");
- +#endif
- +
- + if (!printable || printing || vt->vtd->vc_mode == KD_GRAPHICS)
- + return; /* console not yet initialized */
- + printing = 1;
- +
- + if (!vt_allocated(vtdata.fgconsole)) {
- + /* impossible */
- + printk ("console_print: tty %d not allocated ??\n", vtdata.fgconsole->num);
- + printing = 0;
- + return;
- + }
- +
- +#ifdef CONFIG_SERIAL_ECHO
- + serial_echo_print (b);
- +#endif
- +
- + vcd_removecursors (vt);
- + bufpos = vcd->buffer.pos;
- +
- + while ((c = *b++) != 0) {
- + if (c == 10 || c == 13 || vcd->need_wrap) {
- + vcd->screen.pos -= vcd->curstate.x * vtdata.screen.bytespercharh;
- + bufpos -= vcd->curstate.x;
- +
- + vcd->need_wrap = vcd->curstate.x = 0;
- +
- + if (vcd->curstate.y + 1 == vcd->bottom)
- + scrollup (vt, vt->vcd->top, vt->vcd->bottom, 1);
- + else
- + if (vcd->curstate.y < vtdata.numrows - 1) {
- + vcd->curstate.y ++;
- + vcd->screen.pos += vtdata.screen.sizerow;
- + bufpos += vtdata.buffer.sizerow;
- + }
- + if (c == 10 || c == 13)
- + continue;
- + }
- +
- + vcd->buffer.buffer[bufpos] = character = vcd->combined_state | (c & 255);
- + ll_write_char (vcd->screen.pos, character);
- +
- + if (vcd->curstate.x == vtdata.numcolumns - 1) {
- + vcd->need_wrap = 1;
- + continue;
- + }
- + vcd->curstate.x++;
- + vcd->screen.pos += vtdata.screen.bytespercharh;
- + bufpos ++;
- + }
- + vcd->buffer.pos = bufpos;
- + set_cursor (vt);
- + vcd_restorecursors (vt);
- +
- + vt_pokeblankedconsole ();
- + printing = 0;
- +#ifdef DEBUG
- + vcd_validate (vt->vcd, "console_print exit");
- +#endif
- +}
- +#if 0
- +static void con_setsize (unsigned long rows, unsigned long cols)
- +{
- + vtdata.numcolumns = cols;
- + vtdata.numrows = rows;
- + vtdata.screen.sizerow = cols * vtdata.screen.bytespercharh * vtdata.screen.bytespercharv;
- + vtdata.screen.totsize = vtdata.screen.sizerow * rows;
- + vtdata.buffer.sizerow = cols;
- + vtdata.buffer.totsize = vtdata.buffer.sizerow * rows;
- +}
- +#endif
- +
- +void update_scrmem (const struct vt * const vt, int start, int length)
- +{
- + unsigned long p, pp, sx, sy, ex, ey;
- + unsigned long *buffer;
- +
- + sy = start / vtdata.numcolumns;
- + sx = start % vtdata.numcolumns;
- + length += start;
- + ey = length / vtdata.numcolumns;
- + ex = length % vtdata.numcolumns;
- +
- + if (ey > vtdata.numrows)
- + ey = vtdata.numrows;
- +
- + p = vt->vcd->screen.origin + sy * vtdata.screen.sizerow;
- + buffer = vt->vcd->buffer.buffer + start;
- +
- + if (ey > sy) {
- + for (; sy < ey; sy++) {
- + pp = p + sx * vtdata.screen.bytespercharh;
- + for (; sx < vtdata.numcolumns; sx++) {
- + ll_write_char (pp, *buffer);
- + pp += vtdata.screen.bytespercharh;
- + buffer ++;
- + }
- + p += vtdata.screen.sizerow;
- + sx = 0;
- + }
- + }
- +
- + if (ey == sy && ex) {
- + for (; sx < ex; sx++) {
- + ll_write_char (p, *buffer);
- + p += vtdata.screen.bytespercharh;
- + buffer ++;
- + }
- + }
- +}
- +
- +void set_scrmem (const struct vt * const vt, long offset)
- +{
- + unsigned long p, pp, my, by;
- + unsigned long *buffer;
- + int i;
- +
- + p = vt->vcd->screen.origin;
- + buffer = vt->vcd->buffer.buffer;
- +
- + by = vtdata.screen.sizerow;
- + for (my = vtdata.numrows; my > 0; my--) {
- + int mx, bx = vtdata.screen.bytespercharh;
- + pp = p;
- + mx = vtdata.numcolumns;
- + while (mx > 8) {
- + mx -= 8;
- + ll_write_char (pp, *buffer++); pp += bx;
- + ll_write_char (pp, *buffer++); pp += bx;
- + ll_write_char (pp, *buffer++); pp += bx;
- + ll_write_char (pp, *buffer++); pp += bx;
- + ll_write_char (pp, *buffer++); pp += bx;
- + ll_write_char (pp, *buffer++); pp += bx;
- + ll_write_char (pp, *buffer++); pp += bx;
- + ll_write_char (pp, *buffer++); pp += bx;
- + }
- + while (mx > 0) {
- + mx -= 1;
- + ll_write_char (pp, *buffer++); pp += bx;
- + }
- + p += by;
- + }
- + pp = vt->vcd->screen.origin + ((vtdata.screen.totsize + 0x7FFF) & PAGE_MASK);
- + while (p < pp) {
- + *(unsigned long *)p = 0;
- + p += 4;
- + }
- + if (!vt->vcd->screen.palette_entries || vt->vtd->vc_mode != KD_GRAPHICS)
- + for (i = 0; i < 17; i++)
- + vidc_write(i << 2, default_palette_entries[i]);
- + else
- + for (i = 0; i < 17; i++)
- + vidc_write(i << 2, vt->vcd->screen.palette_entries[i] & 0x1fff);
- +}
- +
- +/*
- + * PIO_FONT support
- + */
- +int con_set_font (char *arg)
- +{
- + return -EINVAL;
- +}
- +
- +int con_get_font (char *arg)
- +{
- + return -EINVAL;
- +}
- +
- +void con_reset_palette (const struct vt * const vt)
- +{
- +}
- +
- +void con_set_palette (const struct vt * const vt)
- +{
- + int i;
- + if (vt->vtd->vc_mode == KD_GRAPHICS && vt->vcd->screen.palette_entries)
- + for (i = 0; i < 17; i++)
- + vidc_write (i << 2, vt->vcd->screen.palette_entries[i] & 0x1fff);
- + else
- + for (i = 0; i < 17; i++)
- + vidc_write (i << 2, default_palette_entries[i]);
- +}
- +
- +/* == arm specific console code ============================================================== */
- +
- +int do_screendump(int arg)
- +{
- + char *buf = (char *)arg;
- + int l;
- + if (!suser())
- + return -EPERM;
- + l = verify_area (VERIFY_WRITE, buf, 2);
- + if (l)
- + return l;
- + return -ENOSYS;
- +}
- +
- +static void ll_erase (const struct vt *vt, unsigned char sx, unsigned char sy,
- + unsigned char ex, unsigned char ey)
- +{
- + int displayed;
- +
- + displayed = vt == vtdata.fgconsole;
- + ex += 1;
- +
- + if (sx != 0) {
- + /* erase to end of line */
- + int c, i;
- + if (sy == ey)
- + c = ex - sx;
- + else
- + c = vtdata.numcolumns - sx;
- +
- + if (sx + c > vtdata.numcolumns + 1)
- + c = vtdata.numcolumns - sx;
- +
- + memsetl (vt->vcd->buffer.buffer + (sx + sy * vtdata.buffer.sizerow),
- + vt->vcd->combined_state | 32, 4 * c);
- +
- + if (displayed)
- + for (i = vtdata.screen.bytespercharv - 1; i >= 0; i --)
- + memset ((void *)(vt->vcd->screen.origin + sx * vtdata.screen.bytespercharh +
- + sy * vtdata.screen.sizerow + i * vtdata.numcolumns *
- + vtdata.screen.bytespercharh), vt->vcd->cached_backcolwrd,
- + c * vtdata.screen.bytespercharh);
- + sy ++;
- + }
- + if (sy < ey || (sy == ey && ex == vtdata.numcolumns - 1)) {
- + /* erase all lines from sy to ey (not inclusive) */
- + int c;
- +
- + c = ey - sy;
- + if (c == 0)
- + c ++;
- +
- + memsetl (vt->vcd->buffer.buffer + sy * vtdata.buffer.sizerow,
- + vt->vcd->combined_state | 32, 4 * c * vtdata.buffer.sizerow);
- +
- + if (displayed)
- + memset ((void *)(vt->vcd->screen.origin + sy * vtdata.screen.sizerow),
- + vt->vcd->cached_backcolwrd, c * vtdata.screen.sizerow);
- + sy += c;
- + }
- + if (sy == ey) {
- + int i;
- +
- + memsetl (vt->vcd->buffer.buffer + sy * vtdata.buffer.sizerow,
- + vt->vcd->combined_state | 32, 4 * ex);
- +
- + if (displayed)
- + for (i = vtdata.screen.bytespercharv - 1; i >= 0; i--)
- + memset ((void *)(vt->vcd->screen.origin + sy * vtdata.screen.sizerow + i *
- + vtdata.numcolumns * vtdata.screen.bytespercharh),
- + vt->vcd->cached_backcolwrd, ex * vtdata.screen.bytespercharh);
- + }
- +}
- +
- +/*===============================================================================================*/
- +
- +static int vcd_write_utf (struct con_struct *vcd, int c)
- +{
- + /* Combine UTF-8 into Unicode */
- + /* Incomplete characters silently ignored */
- + if (c < 0x80) {
- + vcd->utf_count = 0;
- + return c;
- + }
- +
- + if (vcd->utf_count > 0 && (c & 0xc0) == 0x80) {
- + vcd->utf_char = (vcd->utf_char << 6) | (c & 0x3f);
- + if (--vcd->utf_count == 0)
- + return vcd->utf_char;
- + else
- + return -1;
- + } else {
- + unsigned int count, chr;
- + if ((c & 0xe0) == 0xc0) {
- + count = 1;
- + chr = (c & 0x1f);
- + } else if ((c & 0xf0) == 0xe0) {
- + count = 2;
- + chr = (c & 0x0f);
- + } else if ((c & 0xf8) == 0xf0) {
- + count = 3;
- + chr = (c & 0x07);
- + } else if ((c & 0xfc) == 0xf8) {
- + count = 4;
- + chr = (c & 0x03);
- + } else if ((c & 0xfe) == 0xfc) {
- + count = 5;
- + chr = (c & 0x01);
- + } else {
- + count = 0;
- + chr = 0;
- + }
- + vcd->utf_count = count;
- + vcd->utf_char = chr;
- + return -1;
- + }
- +}
- +
- +static void vcd_write_char (const struct vt * const vt, unsigned int c)
- +{
- + struct con_struct * const vcd = vt->vcd;
- + unsigned long character, bufpos;
- +
- + if (c & ~console_charmask)
- + return;
- +
- + bufpos = vcd->buffer.pos;
- +
- + if (vcd->need_wrap) {
- + vcd->screen.pos -= vcd->curstate.x * vtdata.screen.bytespercharh;
- + bufpos -= vcd->curstate.x;
- +
- + vcd->need_wrap = vcd->curstate.x = 0;
- +
- + if (vcd->curstate.y + 1 == vcd->bottom)
- + scrollup (vt, vt->vcd->top, vt->vcd->bottom, 1);
- + else
- + if (vcd->curstate.y + 1 < vtdata.numrows) {
- + vcd->curstate.y ++;
- + vcd->screen.pos += vtdata.screen.sizerow;
- + bufpos += vtdata.buffer.sizerow;
- + }
- + }
- +
- + if (vcd->decim)
- + insert_char (vt);
- +
- + /*
- + * write character to buffer and screen
- + */
- + vcd->buffer.buffer[bufpos] = character = vcd->combined_state | (c & 255);
- +
- + if (vtdata.fgconsole == vt)
- + ll_write_char (vcd->screen.pos, character);
- +
- + if (vcd->curstate.x + 1 == vtdata.numcolumns)
- + vcd->need_wrap = vcd->decawm;
- + else {
- + vcd->curstate.x ++;
- + vcd->screen.pos += vtdata.screen.bytespercharh;
- + bufpos += 1;
- + }
- + vcd->buffer.pos = bufpos;
- +}
- +
- +static int vcd_write_ctrl (const struct vt *vt, unsigned int c)
- +{
- + struct con_struct *vcd = vt->vcd;
- + /*
- + * Control characters can be used in the _middle_
- + * of an escape sequence.
- + */
- + switch(c) {
- + case 7:
- + vt_mksound (0x637, 72, HZ/8);
- + return 0;
- + case 8:
- + if (vcd->need_wrap)
- + vcd->need_wrap = 0;
- + else
- + if (vcd->curstate.x) {
- + vcd->curstate.x --;
- + vcd->screen.pos -= vtdata.screen.bytespercharh;
- + vcd->buffer.pos -= 1;
- + vcd->need_wrap = 0;
- + }
- + return 0;
- + case 9:
- + vcd->screen.pos -= vcd->curstate.x * vtdata.screen.bytespercharh;
- + vcd->buffer.pos -= vcd->curstate.x;
- + while (vcd->curstate.x < vtdata.numcolumns - 1) {
- + vcd->curstate.x++;
- + if (vcd->tab_stop[vcd->curstate.x >> 5] & (1 << (vcd->curstate.x & 31)))
- + break;
- + }
- + vcd->screen.pos += vcd->curstate.x * vtdata.screen.bytespercharh;
- + vcd->buffer.pos += vcd->curstate.x;
- + return 0;
- + case 10:
- + case 11:
- + case 12:
- + if (vcd->curstate.y + 1 == vcd->bottom)
- + scrollup (vt, vt->vcd->top, vt->vcd->bottom, 1);
- + else
- + if (vcd->curstate.y < vtdata.numrows - 1) {
- + vcd->curstate.y ++;
- + vcd->screen.pos += vtdata.screen.sizerow;
- + vcd->buffer.pos += vtdata.buffer.sizerow;
- + }
- + vcd->need_wrap = 0;
- + if (!is_kbd(lnm))
- + return 0;
- + case 13:
- + vcd->screen.pos -= vcd->curstate.x * vtdata.screen.bytespercharh;
- + vcd->buffer.pos -= vcd->curstate.x;
- + vcd->curstate.x = vcd->need_wrap = 0;
- + return 0;
- + case 14:
- + vcd->curstate.flags |= FLG_CHRSET;
- + vcd->disp_ctrl = 1;
- + vcd->translate = set_translate(vcd->curstate.G1_charset);
- + return 1;
- + case 15:
- + vcd->curstate.flags &= ~FLG_CHRSET;
- + vcd->disp_ctrl = 0;
- + vcd->translate = set_translate(vcd->curstate.G0_charset);
- + return 1;
- + case 24:
- + case 26:
- + vcd->state = ESnormal;
- + return 0;
- + case 27:
- + vcd->state = ESesc;
- + return 0;
- + case 127:
- + /* ignored */
- + return 0;
- + case 128+27:
- + vcd->state = ESsquare;
- + return 0;
- + }
- +
- + switch(vcd->state) {
- + case ESesc:
- + vcd->state = ESnormal;
- + switch (c) {
- + case '[':
- + vcd->state = ESsquare;
- + return 0;
- + case '%':
- + vcd->state = ESpercent;
- + return 0;
- + case 'E':
- + vcd->screen.pos -= vcd->curstate.x * vtdata.screen.bytespercharh;
- + vcd->buffer.pos -= vcd->curstate.x;
- + vcd->curstate.x = vcd->need_wrap = 0;
- + case 'D':
- + if (vcd->curstate.y + 1 == vcd->bottom)
- + scrollup (vt, vt->vcd->top, vt->vcd->bottom, 1);
- + else
- + if (vcd->curstate.y < vtdata.numrows - 1) {
- + vcd->curstate.y ++;
- + vcd->screen.pos += vtdata.screen.sizerow;
- + vcd->buffer.pos += vtdata.buffer.sizerow;
- + }
- + /* vcd->need_wrap = 0; why should we reset this when the x position is the same? */
- + return 0;
- + case 'M':
- + if (vcd->curstate.y == vcd->top)
- + scrolldown (vt, vt->vcd->top, vt->vcd->bottom, 1);
- + else
- + if (vcd->curstate.y > 0) {
- + vcd->curstate.y --;
- + vcd->screen.pos -= vtdata.screen.sizerow;
- + vcd->buffer.pos -= vtdata.buffer.sizerow;
- + }
- + /* vcd->need_wrap = 0; why should we reset this when the x position is the same? */
- + return 0;
- + case 'H':
- + vcd->tab_stop[vcd->curstate.x >> 5] |= (1 << (vcd->curstate.x & 31));
- + return 0;
- + case 'Z':
- + respond_ID (*vt->tty);
- + return 0;
- + case '7':
- + vcd->savedstate = vcd->curstate;
- + return 0;
- + case '8':
- + vcd->curstate = vcd->savedstate;
- + update_attr (vt);
- + return 1;
- + case '(':
- + vcd->state = ESsetG0;
- + return 0;
- + case ')':
- + vcd->state = ESsetG1;
- + return 0;
- + case '#':
- + vcd->state = EShash;
- + return 0;
- + case 'c':
- + reset_terminal (vt, 0);
- + return 0;
- + case '>': /* Numeric keypad */
- + clr_kbd (kbdapplic);
- + return 0;
- + case '=': /* Appl. keypad */
- + set_kbd (kbdapplic);
- + return 0;
- + }
- + return 0;
- + case ESnonstd:
- + vcd->state = ESnormal;
- + switch (c) {
- + case 'P': /* palette escape sequence */
- + for (vcd->npar = 0; vcd->npar < NPAR; vcd->npar++)
- + vcd->par[vcd->npar] = 0;
- + vcd->npar = 0 ;
- + vcd->state = ESpalette;
- + return 0;
- + case 'R': /* reset palette */
- + con_reset_palette (vt);
- + default:
- + return 0;
- + }
- + case ESpalette:
- + if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) {
- + vcd->par[vcd->npar++] = (c > '9' ? (c & 0xDF) - 'A' + 10 : c - '0');
- + if (vcd->npar == 7) {
- +#if TODO
- + int i = par[0]*3, j = 1;
- + palette[i] = 16*par[j++];
- + palette[i++] += par[j++];
- + palette[i] = 16*par[j++];
- + palette[i++] += par[j++];
- + palette[i] = 16*par[j++];
- + palette[i++] += par[j];
- +#endif
- + con_set_palette (vt);
- + vcd->state = ESnormal;
- + }
- + } else
- + vcd->state = ESnormal;
- + return 0;
- + case ESsquare:
- + for (vcd->npar = 0; vcd->npar < NPAR; vcd->npar++)
- + vcd->par[vcd->npar] = 0;
- + vcd->npar = 0;
- + vcd->state = ESgetpars;
- + if (c == '[') { /* Function key */
- + vcd->state = ESfunckey;
- + return 0;
- + }
- +
- + vcd->ques = (c == '?');
- + if (vcd->ques)
- + return 0;
- + case ESgetpars:
- + if (c == ';' && vcd->npar < NPAR - 1) {
- + vcd->npar ++;
- + return 0;
- + } else
- + if (c >= '0' && c <= '9') {
- + vcd->par[vcd->npar] = vcd->par[vcd->npar] * 10 + c - '0';
- + return 0;
- + } else
- + vcd->state = ESgotpars;
- + case ESgotpars:
- + vcd->state=ESnormal;
- + switch (c) {
- + case 'h':
- + set_mode(vt, 1);
- + return 0;
- + case 'l':
- + set_mode(vt, 0);
- + return 0;
- + case 'n':
- + if (!vcd->ques) {
- + if (vcd->par[0] == 5)
- + status_report (*vt->tty);
- + else
- + if (vcd->par[0] == 6)
- + cursor_report (vt);
- + }
- + return 0;
- + }
- + if (vcd->ques) {
- + vcd->ques = 0;
- + return 0;
- + }
- + switch(c) {
- + case 'G':
- + case '`':
- + if (vcd->par[0])
- + vcd->par[0]--;
- + gotoxy (vt, vcd->par[0], vcd->curstate.y);
- + return 0;
- + case 'A':
- + if (!vcd->par[0])
- + vcd->par[0]++;
- + gotoxy (vt, vcd->curstate.x, vcd->curstate.y - vcd->par[0]);
- + return 0;
- + case 'B':
- + case 'e':
- + if (!vcd->par[0])
- + vcd->par[0]++;
- + gotoxy (vt, vcd->curstate.x, vcd->curstate.y + vcd->par[0]);
- + return 0;
- + case 'C':
- + case 'a':
- + if (!vcd->par[0])
- + vcd->par[0]++;
- + gotoxy (vt, vcd->curstate.x + vcd->par[0], vcd->curstate.y);
- + return 0;
- + case 'D':
- + if (!vcd->par[0])
- + vcd->par[0]++;
- + gotoxy (vt, vcd->curstate.x - vcd->par[0], vcd->curstate.y);
- + return 0;
- + case 'E':
- + if (!vcd->par[0])
- + vcd->par[0]++;
- + gotoxy (vt, 0, vcd->curstate.y + vcd->par[0]);
- + return 0;
- + case 'F':
- + if (!vcd->par[0])
- + vcd->par[0]++;
- + gotoxy (vt, 0, vcd->curstate.y - vcd->par[0]);
- + return 0;
- + case 'd':
- + if (vcd->par[0])
- + vcd->par[0]--;
- + gotoxay (vt, vcd->curstate.x, vcd->par[0]);
- + return 0;
- + case 'H':
- + case 'f':
- + if (vcd->par[0])
- + vcd->par[0]--;
- + if (vcd->par[1])
- + vcd->par[1]--;
- + gotoxay (vt, vcd->par[1], vcd->par[0]);
- + return 0;
- + case 'J':
- + csi_J (vt, vcd->par[0]);
- + return 0;
- + case 'K':
- + csi_K (vt, vcd->par[0]);
- + return 0;
- + case 'L':
- + csi_L (vt, vcd->par[0]);
- + return 0;
- + case 'M':
- + csi_M (vt, vcd->par[0]);
- + return 0;
- + case 'P':
- + csi_P (vt, vcd->par[0]);
- + return 0;
- + case 'c':
- + if (!vcd->par[0])
- + respond_ID(*vt->tty);
- + return 0;
- + case 'g':
- + if (!vcd->par[0])
- + vcd->tab_stop[vcd->curstate.x >> 5] &= ~(1 << (vcd->curstate.x & 31));
- + else
- + if (vcd->par[0] == 3) {
- + vcd->tab_stop[0] =
- + vcd->tab_stop[1] =
- + vcd->tab_stop[2] =
- + vcd->tab_stop[3] =
- + vcd->tab_stop[4] = 0;
- + }
- + return 0;
- + case 'm':
- + return csi_m (vt);
- + case 'q': /* DECLL - but only 3 leds */
- + /* map 0,1,2,3 to 0,1,2,4 */
- + if (vcd->par[0] < 4)
- + setledstate(vt->kbd, (vcd->par[0] < 3 ? vcd->par[0] : 4));
- + return 0;
- + case 'r':
- + if (!vcd->par[0])
- + vcd->par[0]++;
- + if (!vcd->par[1])
- + vcd->par[1] = vtdata.numrows;
- + if (vcd->par[0] < vcd->par[1] && vcd->par[1] <= vtdata.numrows) {
- + vcd->top = vcd->par[0] - 1;
- + vcd->bottom = vcd->par[1];
- + gotoxay (vt, 0, 0);
- + }
- + return 0;
- + case 's':
- + vcd->savedstate = vcd->curstate;
- + return 0;
- + case 'u':
- + vcd->curstate = vcd->savedstate;
- + update_attr (vt);
- + return 1;
- + case 'X':
- + csi_X (vt, vcd->par[0]);
- + return 0;
- + case '@':
- + csi_at (vt, vcd->par[0]);
- + return 0;
- + case ']': /* setterm functions */
- + setterm_command (vt);
- + return 0;
- + }
- + return 0;
- + case ESpercent:
- + vcd->state = ESnormal;
- + switch (c) {
- + case '@': /* defined in ISO 2022 */
- + vcd->utf = 0;
- + return 0;
- + case 'G': /* prelim official escape code */
- + case '8': /* retained for compatibility */
- + vcd->utf = 1;
- + return 0;
- + }
- + return 0;
- + case ESfunckey:
- + vcd->state = ESnormal;
- + return 0;
- + case EShash:
- + vcd->state = ESnormal;
- + if (c == '8') {
- + /* DEC screen alignment test. kludge :-) */
- + }
- + return 0;
- + case ESsetG0:
- + vcd->state = ESnormal;
- + if (c == '0')
- + vcd->curstate.G0_charset = GRAF_MAP;
- + else
- + if (c == 'B')
- + vcd->curstate.G0_charset = LAT1_MAP;
- + else
- + if (c == 'U')
- + vcd->curstate.G0_charset = IBMPC_MAP;
- + else
- + if (c == 'K')
- + vcd->curstate.G0_charset = USER_MAP;
- + if ((vcd->curstate.flags & FLG_CHRSET) == 0) {
- + vcd->translate = set_translate(vcd->curstate.G0_charset);
- + return 1;
- + }
- + return 0;
- + case ESsetG1:
- + vcd->state = ESnormal;
- + if (c == '0')
- + vcd->curstate.G1_charset = GRAF_MAP;
- + else
- + if (c == 'B')
- + vcd->curstate.G1_charset = LAT1_MAP;
- + else
- + if (c == 'U')
- + vcd->curstate.G1_charset = IBMPC_MAP;
- + else
- + if (c == 'K')
- + vcd->curstate.G1_charset = USER_MAP;
- + if (vcd->curstate.flags & FLG_CHRSET) {
- + vcd->translate = set_translate(vcd->curstate.G1_charset);
- + return 1;
- + }
- + return 0;
- + default:
- + vcd->state = ESnormal;
- + }
- + return 0;
- +}
- +
- +/*===============================================================================================*/
- +
- +int vcd_init (struct vt *vt, int kmallocok, unsigned long *kmem)
- +{
- + memset (vt->vcd, 0, sizeof (*vt->vcd));
- +
- + vt->vcd->screen.origin = vtdata.screen.memstart;
- + if (kmallocok) {
- + vt->vcd->buffer.buffer = kmalloc (vtdata.buffer.totsize * sizeof (unsigned long), GFP_KERNEL);
- + if (!vt->vcd->buffer.buffer)
- + return -ENOMEM;
- + vt->vcd->buffer.kmalloced = 1;
- + } else {
- + vt->vcd->buffer.buffer = (unsigned long *) *kmem;
- + *kmem += vtdata.buffer.totsize * sizeof (unsigned long);
- + }
- +
- + vt->vtd->paste_wait = NULL;
- + vt->vcd->screen.cursoron = (kmem ? 1 : 0);
- + vt->vcd->screen.palette_entries = NULL;
- + reset_terminal (vt, (kmem ? 1 : 0));
- +
- + return 0;
- +}
- +
- +static struct irqaction vsyncirq = { vsync_irq, 0, 0, "vsync", NULL, NULL };
- +
- +unsigned long vcd_pre_init (unsigned long kmem, struct vt *vt)
- +{
- + int colours, i;
- +
- + /* Use ORIG_VIDEO_MODE */
- + vtdata.screen.bytespercharh = bytes_per_char_h;
- + vtdata.screen.bytespercharv = bytes_per_char_v;
- + video_num_columns = vtdata.numcolumns;
- + vtdata.screen.sizerow = (vtdata.numcolumns *
- + vtdata.screen.bytespercharh * vtdata.screen.bytespercharv);
- + vtdata.screen.totsize = vtdata.screen.sizerow * vtdata.numrows;
- + vtdata.screen.memmask = (vtdata.screen.totsize - 1) | (PAGE_SIZE-1);
- + vtdata.screen.memend = 0x02000000;
- + vtdata.screen.memstart = vtdata.screen.memend - vtdata.screen.memmask - 1;
- + vtdata.buffer.sizerow = vtdata.numcolumns;
- + vtdata.buffer.totsize = vtdata.numcolumns * vtdata.numrows;
- +
- + map_screen_mem (vtdata.screen.memstart, 1);
- +
- + switch (vtdata.screen.bytespercharh) {
- + case 1:
- + default_palette_entries = palette_1;
- + color_table = color_1;
- + can_do_color = 0;
- + vtdata.screen.bitsperpix = 1;
- + colours = 1;
- + break;
- + default:
- + case 4:
- + default_palette_entries = palette_4;
- + color_table = color_4;
- + can_do_color = 1;
- + vtdata.screen.bitsperpix = 4;
- + colours = 16;
- + break;
- + case 8:
- + default_palette_entries = palette_8;
- + color_table = color_8;
- + can_do_color = 1;
- + vtdata.screen.bitsperpix = 8;
- + colours = 256;
- + break;
- + }
- +
- + for (i = 0; i < 17; i++)
- + vidc_write (i << 2, default_palette_entries[i]);
- +
- + vcd_init (vt, 0, &kmem);
- +
- + vt->vcd->screen.cursoron = 1;
- +
- + set_origin (vt);
- +
- + gotoxy (vt, ORIG_X, ORIG_Y);
- + csi_J (vt, 0);
- + printable = 1;
- +
- +#ifdef CONFIG_SERIAL_ECHO
- + serial_echo_init (SERIAL_ECHO_PORT);
- +#endif
- +
- + printk ("Console: %s %s %dx%dx%d, %d virtual console%s (max %d)\n",
- + can_do_color ? "colour" : "mono",
- + "A-series",
- + vtdata.numcolumns,
- + vtdata.numrows,
- + colours,
- + MIN_NR_CONSOLES,
- + (MIN_NR_CONSOLES == 1) ? "":"s",
- + MAX_NR_CONSOLES);
- +
- + register_console (console_print);
- +
- + if (setup_arm_irq(IRQ_VSYNCPULSE, &vsyncirq))
- + panic ("Unable to get VSYNC irq for console\n");
- +
- + return kmem;
- +}
- +
- +void vcd_disallocate (struct vt *vt)
- +{
- + if (vt->vcd && vt->vcd->buffer.kmalloced) {
- + kfree (vt->vcd->buffer.buffer);
- + vt->vcd->buffer.buffer = NULL;
- + vt->vcd->buffer.kmalloced = 0;
- + }
- +}
- +
- +int vcd_resize(unsigned long lines, unsigned long cols)
- +{/* TODO */
- + return -ENOMEM;
- +}
- +
- +void vcd_blankscreen(int nopowersave)
- +{
- + int i;
- +
- + /* DISABLE VIDEO */
- + for (i = 0; i < 17; i++)
- + vidc_write(i<<2, 0);
- +}
- +
- +void vcd_unblankscreen (void)
- +{
- + int i;
- + if (!vtdata.fgconsole->vcd->screen.palette_entries ||
- + vtdata.fgconsole->vtd->vc_mode != KD_GRAPHICS)
- + for(i=0; i<17; i++)
- + vidc_write(i<<2, default_palette_entries[i]);
- + else
- + for(i=0; i<17; i++)
- + vidc_write(i<<2, vtdata.fgconsole->vcd->screen.palette_entries[i] & 0x1fff);
- +}
- +
- +static unsigned long old_origin;
- +
- +void vcd_savestate (const struct vt *vt, int blanked)
- +{
- + clear_selection ();
- + vcd_removecursors (vt);
- +
- + old_origin = vt->vcd->screen.origin;
- +
- + if (blanked)
- + vtdata.blanked = NULL;
- +#ifdef DEBUG
- + vcd_validate (vt->vcd, "vcd_savestate");
- +#endif
- +}
- +
- +void vcd_restorestate (const struct vt *vt)
- +{
- + int text_mode;
- +#ifdef DEBUG
- + unsigned long o_orig, o_pos;
- +#endif
- + /*
- + * Reset the origin on this VT to be the same as the previous.
- + * This way we don't get any jumps when we change VT's.
- + */
- + text_mode = vt->vtd->vc_mode == KD_TEXT ? 1 : 0;
- +#ifdef DEBUG
- + o_orig = vt->vcd->screen.origin;
- + o_pos = vt->vcd->screen.pos;
- +#endif
- + vt->vcd->screen.pos -= vt->vcd->screen.origin;
- + if (text_mode) {
- + vt->vcd->screen.origin = old_origin;
- + set_scrmem (vt, 0);
- + } else
- + vt->vcd->screen.origin = 0x02000000;
- + vt->vcd->screen.pos += vt->vcd->screen.origin;
- +#ifdef DEBUG
- + if (vcd_validate (vt->vcd, "vcd_restorestate")) {
- + printk ("old_origin = %08lX; old_pos = %08lX; new_origin = %08lX\n", o_orig, o_pos, old_origin);
- + }
- +#endif
- + set_origin (vt);
- + set_cursor (vt);
- + if (text_mode)
- + vcd_restorecursors (vt);
- + set_leds ();
- +}
- +
- +void vcd_setup_graphics (const struct vt *vt)
- +{
- + clear_selection ();
- + vcd_removecursors (vt);
- +
- + __real_origin = 0;
- + __set_origin (0);
- +}
- +
- +int vcd_write (const struct vt *vt, int from_user, const unsigned char *buf, int count)
- +{
- + int strt_count = count;
- + unsigned short *cached_trans;
- + unsigned int cached_ctrls;
- + register struct con_struct *vcd;
- +
- + if (from_user && get_fs () == KERNEL_DS)
- + from_user = 0;
- +
- + vcd = vt->vcd;
- +
- +#ifdef DEBUG
- + vcd_validate (vcd, "vcd_write entry");
- +#endif
- +
- + vcd_removecursors (vt);
- + if (vt == vtdata.select.vt)
- + clear_selection();
- +
- + disable_bh (KEYBOARD_BH);
- +recache:
- + cached_ctrls = vcd->disp_ctrl ? CTRL_ALWAYS : CTRL_ACTION;
- + cached_trans = vcd->translate + (vcd->toggle_meta ? 0x80 : 0);
- +
- + while (!(*vt->tty)->stopped && count) {
- + int tc, c;
- + __asm__("teq %3, #0
- + ldreqb %0, [%1], #1
- + ldrnebt %0, [%1], #1" : "=r" (c), "=&r" (buf) : "1" (buf), "r" (from_user));
- + count --;
- +
- + if (vcd->utf) {
- + if ((tc = vcd_write_utf (vcd, c)) < 0)
- + continue;
- + c = tc;
- + } else
- + tc = cached_trans[c];
- +
- + if (vcd->state == ESnormal && tc && (c != 127 || vcd->disp_ctrl)) {
- + if (c >= 32 || (!vcd->utf && !(cached_ctrls & (1 << c)))) { /* ok */
- + tc = conv_uni_to_pc (tc);
- + if (tc + 4 == 0)
- + tc = conv_uni_to_pc (0xfffd);
- + else
- + if (tc + 3 == 0)
- + tc = c;
- +
- + vcd_write_char (vt, tc);
- + continue;
- + }
- + }
- +
- + if (vcd_write_ctrl (vt, c))
- + goto recache;
- + else
- + continue;
- + }
- + if (vt->vtd->vc_mode != KD_GRAPHICS)
- + set_cursor (vt);
- + enable_bh(KEYBOARD_BH);
- + vcd_restorecursors (vt);
- +#ifdef DEBUG
- + vcd_validate (vcd, "vcd_write exit");
- +#endif
- + return strt_count - count;
- +}
- +
- +int vcd_ioctl (const struct vt *vt, int cmd, unsigned long arg)
- +{
- + int i;
- + switch (cmd) {
- + case VT_GETPALETTE: {
- + unsigned long pix, num, *val;
- +
- + i = verify_area (VERIFY_READ, (void *)arg, 12);
- + if (i)
- + return i;
- + pix = get_user ((unsigned long *)arg);
- + num = get_user ((unsigned long *)(arg + 4));
- + val = (unsigned long *) get_user ((unsigned long *)(arg + 8));
- +
- + if (pix > 18 || pix + num > 19 || num < 1)
- + return -EINVAL;
- +
- + i = verify_area (VERIFY_WRITE, val, num * sizeof (unsigned long));
- + if (i)
- + return i;
- +
- + if (vt->vcd->screen.palette_entries)
- + for (i = pix; i < pix + num; i++)
- + *val++ = vt->vcd->screen.palette_entries[i] & 0x1fff;
- + else
- + for (i = pix; i < pix + num; i++)
- + *val++ = default_palette_entries[i] & 0x1fff;
- + return 0;
- + }
- + case VT_SETPALETTE: {
- + unsigned long pix, num, *val;
- +
- + i = verify_area (VERIFY_READ, (void *)arg, 12);
- + if (i)
- + return i;
- +
- + pix = get_user ((unsigned long *)arg);
- + num = get_user ((unsigned long *)(arg + 4));
- + val = (unsigned long *) get_user ((unsigned long *)(arg + 8));
- +
- + if (pix > 18 || pix + num > 19 || num < 1)
- + return -EINVAL;
- +
- + i = verify_area (VERIFY_READ, val, num * sizeof (unsigned long));
- + if (i)
- + return i;
- +
- + if (!vt->vcd->screen.palette_entries) {
- + vt->vcd->screen.palette_entries = kmalloc (sizeof (unsigned long) * 19, GFP_KERNEL);
- + if (!vt->vcd->screen.palette_entries)
- + return -ENOMEM;
- + for (i = 0; i < 19; i++)
- + vt->vcd->screen.palette_entries[i] = default_palette_entries[i];
- + }
- +
- + for (i = pix; i < pix + num; i++) {
- + vt->vcd->screen.palette_entries[i] = *val++;
- + vidc_write (i << 2, vt->vcd->screen.palette_entries[i] & 0x1fff);
- + }
- + return 0;
- + }
- +
- + case PIO_FONT:
- + if (vt->vtd->vc_mode != KD_TEXT)
- + return -EINVAL;
- + return con_set_font((char *)arg);
- + /* con_set_font() defined in console.c */
- +
- + case GIO_FONT:
- + if (vt->vtd->vc_mode != KD_TEXT)
- + return -EINVAL;
- + return con_get_font((char *)arg);
- + /* con_get_font() defined in console.c */
- +
- + case PIO_SCRNMAP:
- + return con_set_trans_old ((char *)arg);
- +
- + case GIO_SCRNMAP:
- + return con_get_trans_old((char *)arg);
- +
- + case PIO_UNISCRNMAP:
- + return con_set_trans_new((short *)arg);
- +
- + case GIO_UNISCRNMAP:
- + return con_get_trans_new((short *)arg);
- +
- + case PIO_UNIMAPCLR: {
- + struct unimapinit ui;
- +
- + i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapinit));
- + if (i)
- + return i;
- + memcpy_fromfs(&ui, (void *)arg, sizeof(struct unimapinit));
- + con_clear_unimap(&ui);
- + return 0;
- + }
- +
- + case PIO_UNIMAP: {
- + i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapdesc));
- + if (i == 0) {
- + struct unimapdesc *ud;
- + u_short ct;
- + struct unipair *list;
- +
- + ud = (struct unimapdesc *) arg;
- + ct = get_fs_word(&ud->entry_ct);
- + list = (struct unipair *) get_fs_long(&ud->entries);
- +
- + i = verify_area(VERIFY_READ, (void *) list,
- + ct*sizeof(struct unipair));
- + if (!i)
- + return con_set_unimap(ct, list);
- + }
- + return i;
- + }
- +
- + case GIO_UNIMAP: {
- + i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct unimapdesc));
- + if (i == 0) {
- + struct unimapdesc *ud;
- + u_short ct;
- + struct unipair *list;
- +
- + ud = (struct unimapdesc *) arg;
- + ct = get_fs_word(&ud->entry_ct);
- + list = (struct unipair *) get_fs_long(&ud->entries);
- + if (ct)
- + i = verify_area(VERIFY_WRITE, (void *) list,
- + ct*sizeof(struct unipair));
- + if (!i)
- + return con_get_unimap(ct, &(ud->entry_ct), list);
- + }
- + return i;
- + }
- +
- + default:
- + return -ENOIOCTLCMD;
- + }
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/cp437.uni linux/arch/arm/drivers/char/cp437.uni
- --- linux.orig/arch/arm/drivers/char/cp437.uni Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/cp437.uni Thu Jul 18 22:32:15 1996
- @@ -0,0 +1,291 @@
- +#
- +# Unicode table for IBM Codepage 437. Note that there are many more
- +# substitutions that could be conceived (for example, thick-line
- +# graphs probably should be replaced with double-line ones, accented
- +# Latin characters should replaced with their nonaccented versions,
- +# and some upper case Greek characters could be replaced by Latin), however,
- +# I have limited myself to the Unicodes used by the kernel ISO 8859-1,
- +# DEC VT, and IBM CP 437 tables.
- +#
- +# --------------------------------
- +#
- +# Basic IBM dingbats, some of which will never have a purpose clear
- +# to mankind
- +#
- +0x00 U+0000
- +0x01 U+263a
- +0x02 U+263b
- +0x03 U+2665
- +0x04 U+2666 U+25c6
- +0x05 U+2663
- +0x06 U+2660
- +0x07 U+2022
- +0x08 U+25d8
- +0x09 U+25cb
- +0x0a U+25d9
- +0x0b U+2642
- +0x0c U+2640
- +0x0d U+266a
- +0x0e U+266b
- +0x0f U+263c
- +0x10 U+25b6 U+25ba
- +0x11 U+25c0 U+25c4
- +0x12 U+2195
- +0x13 U+203c
- +0x14 U+00b6
- +0x15 U+00a7
- +0x16 U+25ac
- +0x17 U+21a8
- +0x18 U+2191
- +0x19 U+2193
- +0x1a U+2192
- +0x1b U+2190
- +0x1c U+221f
- +0x1d U+2194
- +0x1e U+25b2
- +0x1f U+25bc
- +#
- +# The ASCII range is identity-mapped, but some of the characters also
- +# have to act as substitutes, especially the upper-case characters.
- +#
- +0x20 U+0020
- +0x21 U+0021
- +0x22 U+0022 U+00a8
- +0x23 U+0023
- +0x24 U+0024
- +0x25 U+0025
- +0x26 U+0026
- +0x27 U+0027
- +0x28 U+0028
- +0x29 U+0029
- +0x2a U+002a
- +0x2b U+002b
- +0x2c U+002c U+00b8
- +0x2d U+002d U+00ad
- +0x2e U+002e
- +0x2f U+002f
- +0x30 U+0030
- +0x31 U+0031
- +0x32 U+0032
- +0x33 U+0033
- +0x34 U+0034
- +0x35 U+0035
- +0x36 U+0036
- +0x37 U+0037
- +0x38 U+0038
- +0x39 U+0039
- +0x3a U+003a
- +0x3b U+003b
- +0x3c U+003c
- +0x3d U+003d
- +0x3e U+003e
- +0x3f U+003f
- +0x40 U+0040
- +0x41 U+0041 U+00c0 U+00c1 U+00c2 U+00c3
- +0x42 U+0042
- +0x43 U+0043 U+00a9
- +0x44 U+0044
- +0x45 U+0045 U+00c8 U+00ca U+00cb
- +0x46 U+0046
- +0x47 U+0047
- +0x48 U+0048
- +0x49 U+0049 U+00cc U+00cd U+00ce U+00cf
- +0x4a U+004a
- +0x4b U+004b U+212a
- +0x4c U+004c
- +0x4d U+004d
- +0x4e U+004e
- +0x4f U+004f U+00d2 U+00d3 U+00d4 U+00d5
- +0x50 U+0050
- +0x51 U+0051
- +0x52 U+0052 U+00ae
- +0x53 U+0053
- +0x54 U+0054
- +0x55 U+0055 U+00d9 U+00da U+00db
- +0x56 U+0056
- +0x57 U+0057
- +0x58 U+0058
- +0x59 U+0059 U+00dd
- +0x5a U+005a
- +0x5b U+005b
- +0x5c U+005c
- +0x5d U+005d
- +0x5e U+005e
- +0x5f U+005f U+f804
- +0x60 U+0060
- +0x61 U+0061 U+00e3
- +0x62 U+0062
- +0x63 U+0063
- +0x64 U+0064
- +0x65 U+0065
- +0x66 U+0066
- +0x67 U+0067
- +0x68 U+0068
- +0x69 U+0069
- +0x6a U+006a
- +0x6b U+006b
- +0x6c U+006c
- +0x6d U+006d
- +0x6e U+006e
- +0x6f U+006f U+00f5
- +0x70 U+0070
- +0x71 U+0071
- +0x72 U+0072
- +0x73 U+0073
- +0x74 U+0074
- +0x75 U+0075
- +0x76 U+0076
- +0x77 U+0077
- +0x78 U+0078 U+00d7
- +0x79 U+0079 U+00fd
- +0x7a U+007a
- +0x7b U+007b
- +0x7c U+007c U+00a5
- +0x7d U+007d
- +0x7e U+007e
- +#
- +# Okay, what on Earth is this one supposed to be used for?
- +#
- +0x7f U+2302
- +#
- +# Non-English characters, mostly lower case letters...
- +#
- +0x80 U+00c7
- +0x81 U+00fc
- +0x82 U+00e9
- +0x83 U+00e2
- +0x84 U+00e4
- +0x85 U+00e0
- +0x86 U+00e5
- +0x87 U+00e7
- +0x88 U+00ea
- +0x89 U+00eb
- +0x8a U+00e8
- +0x8b U+00ef
- +0x8c U+00ee
- +0x8d U+00ec
- +0x8e U+00c4
- +0x8f U+00c5 U+212b
- +0x90 U+00c9
- +0x91 U+00e6
- +0x92 U+00c6
- +0x93 U+00f4
- +0x94 U+00f6
- +0x95 U+00f2
- +0x96 U+00fb
- +0x97 U+00f9
- +0x98 U+00ff
- +0x99 U+00d6
- +0x9a U+00dc
- +0x9b U+00a2
- +0x9c U+00a3
- +0x9d U+00a5
- +0x9e U+20a7
- +0x9f U+0192
- +0xa0 U+00e1
- +0xa1 U+00ed
- +0xa2 U+00f3
- +0xa3 U+00fa
- +0xa4 U+00f1
- +0xa5 U+00d1
- +0xa6 U+00aa
- +0xa7 U+00ba
- +0xa8 U+00bf
- +0xa9 U+2310
- +0xaa U+00ac
- +0xab U+00bd
- +0xac U+00bc
- +0xad U+00a1
- +0xae U+00ab
- +0xaf U+00bb
- +#
- +# Block graphics
- +#
- +0xb0 U+2591
- +0xb1 U+2592
- +0xb2 U+2593
- +0xb3 U+2502
- +0xb4 U+2524
- +0xb5 U+2561
- +0xb6 U+2562
- +0xb7 U+2556
- +0xb8 U+2555
- +0xb9 U+2563
- +0xba U+2551
- +0xbb U+2557
- +0xbc U+255d
- +0xbd U+255c
- +0xbe U+255b
- +0xbf U+2510
- +0xc0 U+2514
- +0xc1 U+2534
- +0xc2 U+252c
- +0xc3 U+251c
- +0xc4 U+2500
- +0xc5 U+253c
- +0xc6 U+255e
- +0xc7 U+255f
- +0xc8 U+255a
- +0xc9 U+2554
- +0xca U+2569
- +0xcb U+2566
- +0xcc U+2560
- +0xcd U+2550
- +0xce U+256c
- +0xcf U+2567
- +0xd0 U+2568
- +0xd1 U+2564
- +0xd2 U+2565
- +0xd3 U+2559
- +0xd4 U+2558
- +0xd5 U+2552
- +0xd6 U+2553
- +0xd7 U+256b
- +0xd8 U+256a
- +0xd9 U+2518
- +0xda U+250c
- +0xdb U+2588
- +0xdc U+2584
- +0xdd U+258c
- +0xde U+2590
- +0xdf U+2580
- +#
- +# Greek letters and mathematical symbols
- +#
- +0xe0 U+03b1
- +0xe1 U+03b2 U+00df
- +0xe2 U+0393
- +0xe3 U+03c0
- +0xe4 U+03a3
- +0xe5 U+03c3
- +0xe6 U+00b5 U+03bc
- +0xe7 U+03c4
- +0xe8 U+03a6 U+00d8
- +0xe9 U+0398
- +0xea U+03a9 U+2126
- +0xeb U+03b4
- +0xec U+221e
- +0xed U+03c6 U+00f8
- +0xee U+03b5
- +0xef U+2229
- +0xf0 U+2261
- +0xf1 U+00b1
- +0xf2 U+2265
- +0xf3 U+2264
- +0xf4 U+2320
- +0xf5 U+2321
- +0xf6 U+00f7
- +0xf7 U+2248
- +0xf8 U+00b0
- +0xf9 U+2219
- +0xfa U+00b7
- +0xfb U+221a
- +0xfc U+207f
- +0xfd U+00b2
- +#
- +# Square bullet, non-spacing blank
- +# Mapping U+fffd to the square bullet means it is the substitution
- +# character
- +#
- +0xfe U+25a0 U+fffd
- +0xff U+00a0
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/defkeymap.c linux/arch/arm/drivers/char/defkeymap.c
- --- linux.orig/arch/arm/drivers/char/defkeymap.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/defkeymap.c Sun Mar 3 12:31:45 1996
- @@ -0,0 +1,337 @@
- +/*
- + * linux/arch/arm/drivers/char/defkeymap.c
- + *
- + * Copyright (C) 1995, 1996 Russell King
- + */
- +
- +#include <linux/types.h>
- +#include <linux/keyboard.h>
- +#include <linux/kd.h>
- +
- +/* Normal (maps 1:1 with no processing) */
- +#define KTn 0xF0
- +/* Function keys */
- +#define KTf 0xF1
- +/* Special (Performs special house-keeping funcs) */
- +#define KTs 0xF2
- +#define KIGNORE K(KTs, 0) /* Ignore */
- +#define KENTER K(KTs, 1) /* Enter */
- +#define KREGS K(KTs, 2) /* Regs */
- +#define KMEM K(KTs, 3) /* Mem */
- +#define KSTAT K(KTs, 4) /* State */
- +#define KINTR K(KTs, 5) /* Intr */
- +#define Ksl 6 /* Last console */
- +#define KCAPSLK K(KTs, 7) /* Caps lock */
- +#define KNUMLK K(KTs, 8) /* Num-lock */
- +#define KSCRLLK K(KTs, 9) /* Scroll-lock */
- +#define KSCRLFOR K(KTs,10) /* Scroll forward */
- +#define KSCRLBAK K(KTs,11) /* Scroll back */
- +#define KREBOOT K(KTs,12) /* Reboot */
- +#define KCAPSON K(KTs,13) /* Caps on */
- +#define KCOMPOSE K(KTs,14) /* Compose */
- +#define KSAK K(KTs,15) /* SAK */
- +#define CONS_DEC K(KTs,16) /* Dec console */
- +#define CONS_INC K(KTs,17) /* Incr console */
- +#define KFLOPPY K(KTs,18) /* Floppy */
- +/* Key pad (0-9 = digits, 10=+, 11=-, 12=*, 13=/, 14=enter, 16=., 17=# */
- +#define KTp 0xF3
- +#define KPAD_0 K(KTp, 0 )
- +#define KPAD_1 K(KTp, 1 )
- +#define KPAD_2 K(KTp, 2 )
- +#define KPAD_3 K(KTp, 3 )
- +#define KPAD_4 K(KTp, 4 )
- +#define KPAD_5 K(KTp, 5 )
- +#define KPAD_6 K(KTp, 6 )
- +#define KPAD_7 K(KTp, 7 )
- +#define KPAD_8 K(KTp, 8 )
- +#define KPAD_9 K(KTp, 9 )
- +#define KPAD_PL K(KTp,10 )
- +#define KPAD_MI K(KTp,11 )
- +#define KPAD_ML K(KTp,12 )
- +#define KPAD_DV K(KTp,13 )
- +#define KPAD_EN K(KTp,14 )
- +#define KPAD_DT K(KTp,16 )
- +#define KPAD_HS K(KTp,18 )
- +/* Console switching */
- +#define KCn 0xF5
- +/* Cursor */
- +#define KTc 0xF6
- +#define Kcd 0 /* Cursor down */
- +#define Kcl 1 /* Cursor left */
- +#define Kcr 2 /* Cursor right */
- +#define Kcu 3 /* Cursor up */
- +/* Shift/alt modifiers etc */
- +#define KMd 0xF7
- +#define KSHIFT K(KMd, 0 )
- +#define KALTGR K(KMd, 1 )
- +#define KCTRL K(KMd, 2 )
- +#define KALT K(KMd, 3 )
- +/* Meta */
- +#define KMt 0xF8
- +#define KAs 0xF9
- +#define KPADA_0 K(KAs, 0 )
- +#define KPADA_1 K(KAs, 1 )
- +#define KPADA_2 K(KAs, 2 )
- +#define KPADA_3 K(KAs, 3 )
- +#define KPADA_4 K(KAs, 4 )
- +#define KPADA_5 K(KAs, 5 )
- +#define KPADA_6 K(KAs, 6 )
- +#define KPADA_7 K(KAs, 7 )
- +#define KPADA_8 K(KAs, 8 )
- +#define KPADA_9 K(KAs, 9 )
- +#define KPADB_0 K(KAs,10 )
- +#define KPADB_1 K(KAs,11 )
- +#define KPADB_2 K(KAs,12 )
- +#define KPADB_3 K(KAs,13 )
- +#define KPADB_4 K(KAs,14 )
- +#define KPADB_5 K(KAs,15 )
- +#define KPADB_6 K(KAs,16 )
- +#define KPADB_7 K(KAs,17 )
- +#define KPADB_8 K(KAs,18 )
- +#define KPADB_9 K(KAs,19 )
- +/* Locking keys */
- +#define KLk 0xFA
- +/* Letters */
- +#define KTl 0xFB
- +
- +u_short plain_map[]=
- +{
- + K(KTn, 27),K(KTf, 0),K(KTf, 1),K(KTf, 2 ),K(KTf, 3),K(KTf, 4),K(KTf, 5 ),K(KTf, 6),
- + K(KTf, 7),K(KTf, 8),K(KTf, 9),K(KTf, 10 ),K(KTf, 11),KIGNORE ,KSCRLLK ,KINTR ,
- + K(KTn,'`'),K(KTn,'1'),K(KTn,'2'),K(KTn,'3' ),K(KTn,'4'),K(KTn,'5'),K(KTn,'6' ),K(KTn,'7'),
- + K(KTn,'8'),K(KTn,'9'),K(KTn,'0'),K(KTn,'-' ),K(KTn,'='),K(KTn,'£'),K(KTn,127 ),K(KTf,21 ),
- + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,K(KTn, 9 ),K(KTl,'q'),
- + K(KTl,'w'),K(KTl,'e'),K(KTl,'r'),K(KTl,'t' ),K(KTl,'y'),K(KTl,'u'),K(KTl,'i' ),K(KTl,'o'),
- + K(KTl,'p'),K(KTn,'['),K(KTn,']'),K(KTn,'\\'),K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPAD_7 ,
- + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTl,'a'),K(KTl,'s'),K(KTl,'d' ),K(KTl,'f'),
- + K(KTl,'g'),K(KTl,'h'),K(KTl,'j'),K(KTl,'k' ),K(KTl,'l'),K(KTn,';'),K(KTn,'\''),KENTER ,
- + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTl,'z' ),K(KTl,'x'),
- + K(KTl,'c'),K(KTl,'v'),K(KTl,'b'),K(KTl,'n' ),K(KTl,'m'),K(KTn,','),K(KTn,'.' ),K(KTn,'/'),
- + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn,' '),
- + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN
- +};
- +
- +u_short shift_map[]=
- +{
- + K(KTn, 27),K(KTf, 10),K(KTf, 11),K(KTf, 12 ),K(KTf, 13),K(KTf, 14),K(KTf, 15 ),K(KTf, 16),
- + K(KTf, 17),K(KTf, 18),K(KTf, 19),K(KTf, 20 ),K(KTf, 21),KIGNORE ,KMEM ,KINTR ,
- + K(KTn,'~'),K(KTn,'!'),K(KTn,'@'),K(KTn,'#' ),K(KTn,'$'),K(KTn,'%'),K(KTn,'^' ),K(KTn,'&'),
- + K(KTn,'*'),K(KTn,'('),K(KTn,')'),K(KTn,'_' ),K(KTn,'+'),K(KTn,'¤'),K(KTn,127 ),K(KTf,21 ),
- + K(KTf,20 ),KSCRLBAK ,KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,K(KTn, 9 ),K(KTl,'Q'),
- + K(KTl,'W'),K(KTl,'E'),K(KTl,'R'),K(KTl,'T' ),K(KTl,'Y'),K(KTl,'U'),K(KTl,'I' ),K(KTl,'O'),
- + K(KTl,'P'),K(KTn,'{'),K(KTn,'}'),K(KTn,'|' ),K(KTf,22 ),K(KTf,23 ),KSCRLFOR ,KPAD_7 ,
- + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTl,'A'),K(KTl,'S'),K(KTl,'D' ),K(KTl,'F'),
- + K(KTl,'G'),K(KTl,'H'),K(KTl,'J'),K(KTl,'K' ),K(KTl,'L'),K(KTn,':'),K(KTn,'"' ),KENTER ,
- + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTl,'Z' ),K(KTl,'X'),
- + K(KTl,'C'),K(KTl,'V'),K(KTl,'B'),K(KTl,'N' ),K(KTl,'M'),K(KTn,'<'),K(KTn,'>' ),K(KTn,'?'),
- + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn,' '),
- + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN
- +};
- +
- +u_short altgr_map[]=
- +{
- + KIGNORE ,K(KCn,12 ),K(KCn,13 ),K(KCn,14 ),K(KCn,15 ),K(KCn,16 ),K(KCn,17 ),K(KCn, 18),
- + K(KCn, 19),K(KCn,20 ),K(KCn,21 ),K(KCn,22 ),K(KCn,23 ),KIGNORE ,KREGS ,KINTR ,
- + KIGNORE ,KIGNORE ,K(KTn,'@'),KIGNORE ,K(KTn,'$'),KIGNORE ,KIGNORE ,K(KTn,'{'),
- + K(KTn,'['),K(KTn,']'),K(KTn,'}'),K(KTn,'\\'),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ),
- + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTl,'q'),
- + K(KTl,'w'),K(KTl,'e'),K(KTl,'r'),K(KTl,'t' ),K(KTl,'y'),K(KTl,'u'),K(KTl,'i' ),K(KTl,'o'),
- + K(KTl,'p'),KIGNORE ,K(KTn,'~'),KIGNORE ,K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPADB_7 ,
- + KPADB_8 ,KPADB_9 ,KPAD_MI ,KCTRL ,K(KAs,20 ),K(KTl,'s'),K(KAs,23 ),K(KAs,25 ),
- + K(KTl,'g'),K(KTl,'h'),K(KTl,'j'),K(KTl,'k' ),K(KTl,'l'),KIGNORE ,KIGNORE ,KENTER ,
- + KPADB_4 ,KPADB_5 ,KPADB_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTl,'z' ),K(KTl,'x'),
- + K(KAs,22 ),K(KTl,'v'),K(KTl,21 ),K(KTl,'n' ),K(KTl,'m'),KIGNORE ,KIGNORE ,KIGNORE ,
- + KSHIFT ,K(KTc,Kcu),KPADB_1 ,KPADB_2 ,KPADB_3 ,KCAPSLK ,KALT ,KIGNORE ,
- + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPADB_0 ,KPAD_DT ,KPAD_EN
- +};
- +
- +u_short ctrl_map[]=
- +{
- + KIGNORE ,K(KTf, 0),K(KTf, 1),K(KTf, 2 ),K(KTf, 3),K(KTf, 4),K(KTf, 5 ),K(KTf, 6),
- + K(KTf, 7),K(KTf, 8),K(KTf, 9),K(KTf, 10 ),K(KTf, 11),KIGNORE ,KSTAT ,KINTR ,
- + KIGNORE ,K(KTn, 1 ),K(KTn, 2 ),K(KTn, 3 ),K(KTn, 4 ),K(KTn, 5 ),K(KTn, 6 ),K(KTn, 7 ),
- + K(KTn, 8 ),K(KTn, 9 ),K(KTn, 0 ),K(KTn,31 ),KIGNORE ,KIGNORE ,K(KTn, 8 ),K(KTf,21 ),
- + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTn,17 ),
- + K(KTn,23 ),K(KTn, 5 ),K(KTn,18 ),K(KTn,20 ),K(KTn,25 ),K(KTn,21 ),K(KTn, 9 ),K(KTn,15 ),
- + K(KTn,16 ),K(KTn,27 ),K(KTn,29 ),K(KTn,28 ),K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPAD_7 ,
- + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTn, 1 ),K(KTn,19 ),K(KTn, 4 ),K(KTn, 6 ),
- + K(KTn, 7 ),K(KTn, 8 ),K(KTn,10 ),K(KTn,11 ),K(KTn,12 ),KIGNORE ,K(KTn, 7 ),KENTER ,
- + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTn,26 ),K(KTn,24 ),
- + K(KTn, 3 ),K(KTn,22 ),K(KTn, 2 ),K(KTn,14 ),K(KTn,13 ),KIGNORE ,KCOMPOSE ,K(KTn,127),
- + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn, 0 ),
- + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN
- +};
- +
- +u_short shift_ctrl_map[]=
- +{
- + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
- + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KFLOPPY ,KINTR ,
- + KIGNORE ,KIGNORE ,K(KTn, 0 ),KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
- + KIGNORE ,KIGNORE ,KIGNORE ,K(KTn,31 ),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ),
- + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTn,17 ),
- + K(KTn,23 ),K(KTn, 5 ),K(KTn,18 ),K(KTn,20 ),K(KTn,25 ),K(KTn,21 ),K(KTn, 9 ),K(KTn,15 ),
- + K(KTn,16 ),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPAD_7 ,
- + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTn, 1 ),K(KTn,19 ),K(KTn, 4 ),K(KTn, 6 ),
- + K(KTn, 7 ),K(KTn, 8 ),K(KTn,10 ),K(KTn,11 ),K(KTn,12 ),KIGNORE ,K(KTn, 7 ),KENTER ,
- + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTn,26 ),K(KTn,24 ),
- + K(KTn, 3 ),K(KTn,22 ),K(KTn, 2 ),K(KTn,14 ),K(KTn,13 ),KIGNORE ,KIGNORE ,KIGNORE ,
- + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn, 0 ),
- + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN
- +};
- +
- +u_short alt_map[]=
- +{
- + K(KMt,27 ),K(KCn, 0 ),K(KCn, 1 ),K(KCn, 2 ),K(KCn, 3 ),K(KCn, 4 ),K(KCn, 5 ),K(KCn, 6 ),
- + K(KCn, 7 ),K(KCn, 8 ),K(KCn, 9 ),K(KCn,10 ),K(KCn,11 ),KIGNORE ,KSCRLLK ,KINTR ,
- + K(KMt,'`'),K(KMt,'1'),K(KMt,'2'),K(KMt,'3' ),K(KMt,'4'),K(KMt,'5'),K(KMt,'6' ),K(KMt,'7'),
- + K(KMt,'8'),K(KMt,'9'),K(KMt,'0'),K(KMt,'-' ),K(KMt,'='),K(KMt,'£'),K(KMt,127 ),K(KTf,21 ),
- + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,K(KMt, 9 ),K(KMt,'q'),
- + K(KMt,'w'),K(KMt,'e'),K(KMt,'r'),K(KMt,'t' ),K(KMt,'y'),K(KMt,'u'),K(KMt,'i' ),K(KMt,'o'),
- + K(KMt,'p'),K(KMt,'['),K(KMt,']'),K(KMt,'\\'),K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPADA_7 ,
- + KPADA_8 ,KPADA_9 ,KPAD_MI ,KCTRL ,K(KMt,'a'),K(KMt,'s'),K(KMt,'d' ),K(KMt,'f'),
- + K(KMt,'g'),K(KMt,'h'),K(KMt,'j'),K(KMt,'k' ),K(KMt,'l'),K(KMt,';'),K(KMt,'\''),K(KMt,13 ),
- + KPADA_4 ,KPADA_5 ,KPADA_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KMt,'z' ),K(KMt,'x'),
- + K(KMt,'c'),K(KMt,'v'),K(KMt,'b'),K(KMt,'n' ),K(KMt,'m'),K(KMt,','),K(KMt,'.' ),KIGNORE ,
- + KSHIFT ,K(KTc,Kcu),KPADA_1 ,KPADA_2 ,KPADA_3 ,KCAPSLK ,KALT ,K(KMt,' '),
- + KALTGR ,KCTRL ,CONS_DEC ,K(KTc,Kcd ),CONS_INC ,KPADA_0 ,KPAD_DT ,KPAD_EN
- +};
- +
- +u_short ctrl_alt_map[]=
- +{
- + KIGNORE ,K(KCn, 0 ),K(KCn, 1 ),K(KCn, 2 ),K(KCn, 3 ),K(KCn, 4 ),K(KCn, 5 ),K(KCn, 6 ),
- + K(KCn, 7 ),K(KCn, 8 ),K(KCn, 9 ),K(KCn,10 ),K(KCn,11 ),KIGNORE ,KIGNORE ,KINTR ,
- + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
- + KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ),
- + K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KMt,17 ),
- + K(KMt,23 ),K(KMt, 5 ),K(KMt,18 ),K(KMt,20 ),K(KMt,25 ),K(KMt,21 ),K(KMt, 9 ),K(KMt,15 ),
- + K(KMt,16 ),KIGNORE ,KIGNORE ,KIGNORE ,KREBOOT ,K(KTf,23 ),K(KTf,25 ),KPAD_7 ,
- + KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KMt, 1 ),K(KMt,19 ),K(KMt, 4 ),K(KMt, 6 ),
- + K(KMt, 7 ),K(KMt, 8 ),K(KMt,10 ),K(KMt,11 ),K(KMt,12 ),KIGNORE ,KIGNORE ,KENTER ,
- + KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KMt,26 ),K(KMt,24 ),
- + K(KMt, 3 ),K(KMt,22 ),K(KMt, 2 ),K(KMt,14 ),K(KMt,13 ),KIGNORE ,KIGNORE ,KIGNORE ,
- + KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,KIGNORE ,
- + KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KREBOOT ,KPAD_EN
- +};
- +
- +ushort *key_maps[MAX_NR_KEYMAPS] = {
- + plain_map, shift_map, altgr_map, 0,
- + ctrl_map, shift_ctrl_map, 0, 0,
- + alt_map, 0, 0, 0,
- + ctrl_alt_map, 0
- +};
- +
- +unsigned int keymap_count = 7;
- +
- +/*
- + * Philosophy: most people do not define more strings, but they who do
- + * often want quite a lot of string space. So, we statically allocate
- + * the default and allocate dynamically in chunks of 512 bytes.
- + */
- +
- +char func_buf[] = {
- + '\033', '[', '[', 'A', 0,
- + '\033', '[', '[', 'B', 0,
- + '\033', '[', '[', 'C', 0,
- + '\033', '[', '[', 'D', 0,
- + '\033', '[', '[', 'E', 0,
- + '\033', '[', '1', '7', '~', 0,
- + '\033', '[', '1', '8', '~', 0,
- + '\033', '[', '1', '9', '~', 0,
- + '\033', '[', '2', '0', '~', 0,
- + '\033', '[', '2', '1', '~', 0,
- + '\033', '[', '2', '3', '~', 0,
- + '\033', '[', '2', '4', '~', 0,
- + '\033', '[', '2', '5', '~', 0,
- + '\033', '[', '2', '6', '~', 0,
- + '\033', '[', '2', '8', '~', 0,
- + '\033', '[', '2', '9', '~', 0,
- + '\033', '[', '3', '1', '~', 0,
- + '\033', '[', '3', '2', '~', 0,
- + '\033', '[', '3', '3', '~', 0,
- + '\033', '[', '3', '4', '~', 0,
- + '\033', '[', '1', '~', 0,
- + '\033', '[', '2', '~', 0,
- + '\033', '[', '3', '~', 0,
- + '\033', '[', '4', '~', 0,
- + '\033', '[', '5', '~', 0,
- + '\033', '[', '6', '~', 0,
- + '\033', '[', 'M', 0,
- + '\033', '[', 'P', 0,
- +};
- +
- +char *funcbufptr = func_buf;
- +int funcbufsize = sizeof(func_buf);
- +int funcbufleft = 0; /* space left */
- +
- +char *func_table[MAX_NR_FUNC] = {
- + func_buf + 0,
- + func_buf + 5,
- + func_buf + 10,
- + func_buf + 15,
- + func_buf + 20,
- + func_buf + 25,
- + func_buf + 31,
- + func_buf + 37,
- + func_buf + 43,
- + func_buf + 49,
- + func_buf + 55,
- + func_buf + 61,
- + func_buf + 67,
- + func_buf + 73,
- + func_buf + 79,
- + func_buf + 85,
- + func_buf + 91,
- + func_buf + 97,
- + func_buf + 103,
- + func_buf + 109,
- + func_buf + 115,
- + func_buf + 120,
- + func_buf + 125,
- + func_buf + 130,
- + func_buf + 135,
- + func_buf + 140,
- + func_buf + 145,
- + 0,
- + 0,
- + func_buf + 149,
- + 0,
- +};
- +
- +struct kbdiacr accent_table[MAX_DIACR] = {
- + {'`', 'A', '\300'}, {'`', 'a', '\340'},
- + {'\'', 'A', '\301'}, {'\'', 'a', '\341'},
- + {'^', 'A', '\302'}, {'^', 'a', '\342'},
- + {'~', 'A', '\303'}, {'~', 'a', '\343'},
- + {'"', 'A', '\304'}, {'"', 'a', '\344'},
- + {'O', 'A', '\305'}, {'o', 'a', '\345'},
- + {'0', 'A', '\305'}, {'0', 'a', '\345'},
- + {'A', 'A', '\305'}, {'a', 'a', '\345'},
- + {'A', 'E', '\306'}, {'a', 'e', '\346'},
- + {',', 'C', '\307'}, {',', 'c', '\347'},
- + {'`', 'E', '\310'}, {'`', 'e', '\350'},
- + {'\'', 'E', '\311'}, {'\'', 'e', '\351'},
- + {'^', 'E', '\312'}, {'^', 'e', '\352'},
- + {'"', 'E', '\313'}, {'"', 'e', '\353'},
- + {'`', 'I', '\314'}, {'`', 'i', '\354'},
- + {'\'', 'I', '\315'}, {'\'', 'i', '\355'},
- + {'^', 'I', '\316'}, {'^', 'i', '\356'},
- + {'"', 'I', '\317'}, {'"', 'i', '\357'},
- + {'-', 'D', '\320'}, {'-', 'd', '\360'},
- + {'~', 'N', '\321'}, {'~', 'n', '\361'},
- + {'`', 'O', '\322'}, {'`', 'o', '\362'},
- + {'\'', 'O', '\323'}, {'\'', 'o', '\363'},
- + {'^', 'O', '\324'}, {'^', 'o', '\364'},
- + {'~', 'O', '\325'}, {'~', 'o', '\365'},
- + {'"', 'O', '\326'}, {'"', 'o', '\366'},
- + {'/', 'O', '\330'}, {'/', 'o', '\370'},
- + {'`', 'U', '\331'}, {'`', 'u', '\371'},
- + {'\'', 'U', '\332'}, {'\'', 'u', '\372'},
- + {'^', 'U', '\333'}, {'^', 'u', '\373'},
- + {'"', 'U', '\334'}, {'"', 'u', '\374'},
- + {'\'', 'Y', '\335'}, {'\'', 'y', '\375'},
- + {'T', 'H', '\336'}, {'t', 'h', '\376'},
- + {'s', 's', '\337'}, {'"', 'y', '\377'},
- + {'s', 'z', '\337'}, {'i', 'j', '\377'},
- +};
- +
- +unsigned int accent_table_size = 68;
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/diacr.h linux/arch/arm/drivers/char/diacr.h
- --- linux.orig/arch/arm/drivers/char/diacr.h Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/diacr.h Thu Aug 8 23:34:50 1996
- @@ -0,0 +1,8 @@
- +#ifndef _DIACR_H
- +#define _DIACR_H
- +#include <linux/kd.h>
- +
- +extern struct kbdiacr accent_table[];
- +extern unsigned int accent_table_size;
- +
- +#endif /* _DIACR_H */
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/kbd_kern.h linux/arch/arm/drivers/char/kbd_kern.h
- --- linux.orig/arch/arm/drivers/char/kbd_kern.h Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/kbd_kern.h Sat Sep 7 10:31:12 1996
- @@ -0,0 +1,131 @@
- +#ifndef _KBD_KERN_H
- +#define _KBD_KERN_H
- +
- +#include <linux/interrupt.h>
- +#include <linux/keyboard.h>
- +
- +extern int shift_state;
- +extern char *func_table[MAX_NR_FUNC];
- +extern char func_buf[];
- +extern char *funcbufptr;
- +extern int funcbufsize, funcbufleft;
- +
- +#ifndef _VT_KERN_H
- +#include "vt_kern.h"
- +#endif
- +
- +extern void compute_shiftstate (void);
- +extern int kbd_struct_init (struct vt *vt, int init);
- +extern int kbd_ioctl (struct vt *vt, int cmd, unsigned long arg);
- +extern int kbd_init (void);
- +
- +/*
- + * kbd->xxx contains the VC-local things (flag settings etc..)
- + *
- + * Note: externally visible are LED_SCR, LED_NUM, LED_CAP defined in kd.h
- + * The code in KDGETLED / KDSETLED depends on the internal and
- + * external order being the same.
- + *
- + * Note: lockstate is used as index in the array key_map.
- + */
- +struct kbd_struct {
- +
- + unsigned char lockstate;
- +/* 8 modifiers - the names do not have any meaning at all;
- + they can be associated to arbitrarily chosen keys */
- +#define VC_SHIFTLOCK KG_SHIFT /* shift lock mode */
- +#define VC_ALTGRLOCK KG_ALTGR /* altgr lock mode */
- +#define VC_CTRLLOCK KG_CTRL /* control lock mode */
- +#define VC_ALTLOCK KG_ALT /* alt lock mode */
- +#define VC_SHIFTLLOCK KG_SHIFTL /* shiftl lock mode */
- +#define VC_SHIFTRLOCK KG_SHIFTR /* shiftr lock mode */
- +#define VC_CTRLLLOCK KG_CTRLL /* ctrll lock mode */
- +#define VC_CTRLRLOCK KG_CTRLR /* ctrlr lock mode */
- + unsigned char slockstate; /* for `sticky' Shift, Ctrl, etc. */
- +
- + unsigned char ledmode:2; /* one 2-bit value */
- +#define LED_SHOW_FLAGS 0 /* traditional state */
- +#define LED_SHOW_IOCTL 1 /* only change leds upon ioctl */
- +#define LED_SHOW_MEM 2 /* `heartbeat': peek into memory */
- +
- + unsigned char ledflagstate:3; /* flags, not lights */
- + unsigned char default_ledflagstate:3;
- +#define VC_SCROLLOCK 0 /* scroll-lock mode */
- +#define VC_NUMLOCK 1 /* numeric lock mode */
- +#define VC_CAPSLOCK 2 /* capslock mode */
- +
- + unsigned char kbdmode:2; /* one 2-bit value */
- +#define VC_XLATE 0 /* translate keycodes using keymap */
- +#define VC_MEDIUMRAW 1 /* medium raw (keycode) mode */
- +#define VC_RAW 2 /* raw (scancode) mode */
- +#define VC_UNICODE 3 /* Unicode mode */
- +
- + unsigned char modeflags:5;
- +#define VC_APPLIC 0 /* application key mode */
- +#define VC_CKMODE 1 /* cursor key mode */
- +#define VC_REPEAT 2 /* keyboard repeat */
- +#define VC_CRLF 3 /* 0 - enter sends CR, 1 - enter sends CRLF */
- +#define VC_META 4 /* 0 - meta, 1 - meta=prefix with ESC */
- +};
- +
- +extern unsigned char getledstate(void);
- +extern void setledstate(struct kbd_struct *kbd, unsigned int led);
- +
- +extern inline void set_leds(void)
- +{
- + mark_bh(KEYBOARD_BH);
- +}
- +
- +extern inline int vc_kbd_mode(struct kbd_struct * kbd, int flag)
- +{
- + return ((kbd->modeflags >> flag) & 1);
- +}
- +
- +extern inline int vc_kbd_led(struct kbd_struct * kbd, int flag)
- +{
- + return ((kbd->ledflagstate >> flag) & 1);
- +}
- +
- +extern inline void set_vc_kbd_mode(struct kbd_struct * kbd, int flag)
- +{
- + kbd->modeflags |= 1 << flag;
- +}
- +
- +extern inline void set_vc_kbd_led(struct kbd_struct * kbd, int flag)
- +{
- + kbd->ledflagstate |= 1 << flag;
- +}
- +
- +extern inline void clr_vc_kbd_mode(struct kbd_struct * kbd, int flag)
- +{
- + kbd->modeflags &= ~(1 << flag);
- +}
- +
- +extern inline void clr_vc_kbd_led(struct kbd_struct * kbd, int flag)
- +{
- + kbd->ledflagstate &= ~(1 << flag);
- +}
- +
- +extern inline void chg_vc_kbd_lock(struct kbd_struct * kbd, int flag)
- +{
- + kbd->lockstate ^= 1 << flag;
- +}
- +
- +extern inline void chg_vc_kbd_slock(struct kbd_struct * kbd, int flag)
- +{
- + kbd->slockstate ^= 1 << flag;
- +}
- +
- +extern inline void chg_vc_kbd_mode(struct kbd_struct * kbd, int flag)
- +{
- + kbd->modeflags ^= 1 << flag;
- +}
- +
- +extern inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag)
- +{
- + kbd->ledflagstate ^= 1 << flag;
- +}
- +
- +#define U(x) ((x) ^ 0xf000)
- +
- +#endif
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/kbdmouse.c linux/arch/arm/drivers/char/kbdmouse.c
- --- linux.orig/arch/arm/drivers/char/kbdmouse.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/kbdmouse.c Tue Jul 2 09:47:58 1996
- @@ -0,0 +1,178 @@
- +/*
- + * linux/arch/arm/drivers/char/kbdmouse.c
- + *
- + * Copyright (C) 1995, 1996 Russell King
- + *
- + * Medium-level interface for quadrature mouse connected to the keyboard.
- + */
- +
- +#include <linux/module.h>
- +
- +#include <linux/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/signal.h>
- +#include <linux/errno.h>
- +#include <linux/mm.h>
- +#include <linux/miscdevice.h>
- +#include <linux/random.h>
- +
- +#include <asm/system.h>
- +#include <asm/segment.h>
- +
- +char mouse_buttons;
- +int mouse_dxpos;
- +int mouse_dypos;
- +int mouse_present;
- +char mouse_ready;
- +char mouse_active;
- +struct wait_queue *mouse_wait;
- +struct fasync_struct *fasyncptr;
- +
- +static int fasync_mouse(struct inode *inode, struct file *filp, int on)
- +{
- + int retval;
- +
- + retval = fasync_helper(inode, filp, on, &fasyncptr);
- + if (retval < 0)
- + return retval;
- + return 0;
- +}
- +
- +static void close_mouse(struct inode *inode, struct file *file)
- +{
- + fasync_mouse (inode, file, 0);
- + if (--mouse_active)
- + return;
- + mouse_ready = 0;
- +
- + MOD_DEC_USE_COUNT;
- +}
- +
- +static int open_mouse(struct inode *inode,struct file *file)
- +{
- + unsigned long flags;
- +
- + if (!mouse_present)
- + return -EINVAL;
- + if (mouse_active++)
- + return 0;
- +
- + save_flags_cli (flags);
- + mouse_active = 1;
- + mouse_ready = 0;
- + mouse_dxpos = 0;
- + mouse_dypos = 0;
- + mouse_buttons = 0;
- + restore_flags (flags);
- +
- + MOD_INC_USE_COUNT;
- +
- + return 0;
- +}
- +
- +static int write_mouse(struct inode *inode,struct file *file,const char *buffer,int count)
- +{
- + return -EINVAL;
- +}
- +
- +static int read_mouse(struct inode *inode,struct file *file,char *buffer,int count)
- +{
- + unsigned long flags;
- + int dxpos, dypos, i, buttons;
- +
- + if (count < 3)
- + return -EINVAL;
- + if ((i = verify_area(VERIFY_WRITE, buffer, count)))
- + return i;
- + if (!mouse_ready)
- + return -EAGAIN;
- +
- + save_flags_cli (flags);
- +
- + dxpos = mouse_dxpos;
- + dypos = mouse_dypos;
- + buttons = mouse_buttons ^ 7;
- +
- + if (dxpos < -127)
- + dxpos =- 127;
- + if (dxpos > 127)
- + dxpos = 127;
- + if (dypos <- 127)
- + dypos =- 127;
- + if (dypos > 127)
- + dypos = 127;
- +
- + mouse_dxpos -= dxpos;
- + mouse_dypos -= dypos;
- + mouse_ready = 0;
- +
- + restore_flags (flags);
- +
- + put_fs_byte ((char) buttons | 0x80, buffer);
- + put_fs_byte ((char) dxpos, buffer + 1);
- + put_fs_byte ((char) dypos, buffer + 2);
- + for(i = 3; i < count; i++)
- + put_fs_byte (0x00, buffer + i);
- +
- + return i;
- +}
- +
- +static int select_mouse(struct inode *inode,struct file *file,int sel_type,select_table *wait)
- +{
- + if (sel_type == SEL_IN) {
- + if (mouse_ready)
- + return 1;
- + select_wait (&mouse_wait,wait);
- + }
- + return 0;
- +}
- +
- +struct file_operations kbd_mouse_fops=
- +{
- + NULL, /* mouse_seek */
- + read_mouse,
- + write_mouse,
- + NULL, /* mouse_readdir */
- + select_mouse,
- + NULL, /* mouse_ioctl */
- + NULL, /* mouse_mmap */
- + open_mouse,
- + close_mouse,
- + NULL,
- + fasync_mouse,
- +};
- +
- +static struct miscdevice kbd_mouse = {
- + 6, "kbdmouse", &kbd_mouse_fops, NULL, NULL
- +};
- +
- +int arch_mouse_init(void)
- +{
- + unsigned long flags;
- +
- + save_flags_cli (flags);
- +
- + mouse_buttons=0;
- + mouse_dxpos =0;
- + mouse_dypos =0;
- + mouse_present=1;
- + mouse_ready =0;
- + mouse_active =0;
- + mouse_wait =NULL;
- +
- + restore_flags (flags);
- + misc_register (&kbd_mouse);
- + return 0;
- +}
- +
- +#ifdef MODULE
- +int init_module(void)
- +{
- + return arch_mouse_init();
- +}
- +
- +void cleanup_module(void)
- +{
- + misc_deregister (&kbd_mouse);
- +}
- +#endif
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/keyboard.c linux/arch/arm/drivers/char/keyboard.c
- --- linux.orig/arch/arm/drivers/char/keyboard.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/keyboard.c Sat Aug 24 18:34:06 1996
- @@ -0,0 +1,1769 @@
- +/*
- + * linux/arch/arm/drivers/block/keyboard.c
- + *
- + * Keyboard driver for Linux using Latin-1.
- + *
- + * Written for linux by Johan Myreen as a translation from
- + * the assembly version by Linus (with diacriticals added)
- + *
- + * Some additional features added by Christoph Niemann (ChN), March 1993
- + *
- + * Loadable keymaps by Risto Kankkunen, May 1993
- + *
- + * Diacriticals redone & other small changes, aeb@cwi.nl, June 93
- + * Added decr/incr_console, dynamic keymaps, unicode support,
- + * dynamic function/string keys, led setting, Sept 1994
- + * `Sticky' modifier keys, 951006
- + *
- + * Majorly altered for use with arm Russell King (rmk92@ecs.soton.ac.uk).
- + */
- +
- +#define IRQ_KEYBOARDRX 15
- +#define IRQ_KEYBOARDTX 14
- +
- +#define VERSION 104
- +
- +#include <linux/config.h>
- +#include <linux/sched.h>
- +#include <linux/interrupt.h>
- +#include <linux/tty.h>
- +#include <linux/tty_flip.h>
- +#include <linux/mm.h>
- +#include <linux/malloc.h>
- +#include <linux/ptrace.h>
- +#include <linux/signal.h>
- +#include <linux/timer.h>
- +#include <linux/random.h>
- +#include <linux/ctype.h>
- +
- +#include <asm/bitops.h>
- +#include <asm/irq.h>
- +#include <asm/hardware.h>
- +
- +#include "kbd_kern.h"
- +#include "diacr.h"
- +#include "vt_kern.h"
- +
- +#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
- +
- +#define KBD_REPORT_ERR
- +#define KBD_REPORT_UNKN
- +
- +#ifndef KBD_DEFMODE
- +#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
- +#endif
- +
- +#ifndef KBD_DEFLEDS
- +/*
- + * Starts with NumLock off.
- + */
- +#define KBD_DEFLEDS 0
- +#endif
- +
- +#ifndef KBD_DEFLOCK
- +#define KBD_DEFLOCK 0
- +#endif
- +
- +#define REPEAT_TIMEOUT HZ*300/1000
- +#define REPEAT_RATE HZ*30/1000
- +
- +/*
- + * these are the valid i/o ports we're allowed to change. they map all the
- + * video ports
- + */
- +#define GPFIRST 0x3b4
- +#define GPLAST 0x3df
- +#define GPNUM (GPLAST - GPFIRST + 1)
- +
- +extern ushort *key_maps[];
- +extern unsigned int keymap_count;
- +extern void ctrl_alt_del (void);
- +
- +/*
- + * global state includes the following, and various static variables
- + * in this module: shift_state, diacr, npadch, dead_key_next.
- + * (last console is now a global variable).
- + */
- +
- +/* shift state counters.. */
- +static unsigned char k_down[NR_SHIFT] = {0, };
- +/*
- + * Key down counters
- + */
- +#define BITS_PER_SHORT (8*sizeof (unsigned short))
- +static unsigned short key_down[256/BITS_PER_SHORT] = {0, };
- +
- +extern struct vt *last_console;
- +static struct vt *want_console;
- +static int dead_key_next;
- +/*
- + * In order to retrieve the shift_state (for the mouse server), either
- + * the variable must be global, or a new procedure must be created to
- + * return the value. I chose the former way.
- + */
- +/*static*/ int shift_state;
- +static int npadch = -1; /* -1 or number assembled on pad */
- +static unsigned char diacr;
- +static char rep; /* Flag telling character repeat */
- +static int kbd_repeatkey = -1;
- +static int kbd_repeattimeout = REPEAT_TIMEOUT;
- +static int kbd_repeatrate = REPEAT_RATE;
- +static int kbd_id = -1;
- +static struct wait_queue *kbd_waitq;
- +
- +static struct kbd_struct * kbd = NULL;
- +static struct tty_struct * tty = NULL;
- +
- +extern void compute_shiftstate (void);
- +
- +typedef void (*k_hand)(unsigned char value, char up_flag);
- +typedef void (k_handfn)(unsigned char value, char up_flag);
- +
- +static k_handfn
- + do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
- + do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_ignore;
- +
- +static k_hand key_handler[16] = {
- + do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
- + do_meta, do_ascii, do_lock, do_lowercase, do_slock,
- + do_ignore, do_ignore, do_ignore
- +};
- +
- +typedef void (*void_fnp)(void);
- +typedef void (void_fn)(void);
- +
- +static void_fn do_null, enter, show_ptregs, send_intr, lastcons, caps_toggle,
- + num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose,
- + SAK, decr_console, incr_console, spawn_console, show_stack, bare_num;
- +
- +static void_fnp spec_fn_table[] = {
- + do_null, enter, show_ptregs, show_mem,
- + show_state, send_intr, lastcons, caps_toggle,
- + num, hold, scroll_forw, scroll_back,
- + boot_it, caps_on, compose, SAK,
- + decr_console, incr_console, show_stack, bare_num
- +};
- +
- +/* maximum values each key_handler can handle */
- +const int max_vals[] = {
- + 255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1,
- + NR_DEAD - 1, 255, 3, NR_SHIFT - 1,
- + 255, NR_ASCII - 1, NR_LOCK - 1, 255,
- + NR_LOCK - 1
- +};
- +
- +const int NR_TYPES = SIZE(max_vals);
- +
- +static void put_queue(int);
- +static unsigned char handle_diacr(unsigned char);
- +
- +/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
- +static struct pt_regs * pt_regs;
- +
- +/*
- + * Many other routines do put_queue, but I think either
- + * they produce ASCII, or they produce some user-assigned
- + * string, and in both cases we might assume that it is
- + * in utf-8 already.
- + */
- +void to_utf8(ushort c) {
- + if (c < 0x80)
- + put_queue(c); /* 0******* */
- + else if (c < 0x800) {
- + put_queue(0xc0 | (c >> 6)); /* 110***** 10****** */
- + put_queue(0x80 | (c & 0x3f));
- + } else {
- + put_queue(0xe0 | (c >> 12)); /* 1110**** 10****** 10****** */
- + put_queue(0x80 | ((c >> 6) & 0x3f));
- + put_queue(0x80 | (c & 0x3f));
- + }
- + /* uft-8 is defined for words of up to 36 bits,
- + but we need only 16 bits here */
- +}
- +
- +/*
- + * ARM mouse specifics stuff.
- + *
- + * Since the mouse movements and buttons are part of the
- + * keyboard protocol, we have to handle this part in this
- + * driver. The mouse specific parts are in arcmouse.c.
- + */
- +#ifdef CONFIG_KBDMOUSE
- +extern struct wait_queue *mouse_wait;
- +extern char mouse_buttons;
- +extern int mouse_dxpos;
- +extern int mouse_dypos;
- +extern char mouse_ready;
- +#endif
- +/*
- + * Protocol codes to send the keyboard.
- + */
- +/* reset keyboard */
- +#define HRST 0xff
- +/* reset response */
- +#define RAK1 0xfe
- +/* reset response */
- +#define RAK2 0xfd
- +/* Ack for first keyboard pair */
- +#define BACK 0x3f
- +/* Last data byte ack (key scanning + mouse movement scanning) */
- +#define SMAK 0x33
- +/* Last data byte ack (mouse movement scanning) */
- +#define MACK 0x32
- +/* Last data byte ack (key scanning) */
- +#define SACK 0x31
- +/* Last data byte ack (no scanning, mouse data) */
- +#define NACK 0x30
- +/* Request mouse data */
- +#define RQMP 0x22
- +/* nothing */
- +#define PRST 0x21
- +/* Request ID */
- +#define RQID 0x20
- +
- +#define UP_FLAG 0x0100
- +
- +static unsigned char kbd_sendvala[4];
- +static unsigned char kbd_sendptri;
- +static unsigned char kbd_sendptro;
- +static unsigned char ledstate;
- +static unsigned char getleds(void);
- +
- +/*
- + * 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,
- +};
- +
- +static unsigned short ghost_down[128/BITS_PER_SHORT];
- +
- +/*
- + * As yet, we don't support setting and getting the
- + * key codes. I'll have to find out where this is used.
- + */
- +int setkeycode(unsigned int scancode, unsigned int keycode)
- +{
- +#if 0
- + if (scancode < SC_LIM || scancode > 255 || keycode > 127)
- + return -EINVAL;
- + if (scancode < 128)
- + high_keys[scancode - SC_LIM] = keycode;
- + else
- + e0_keys[scancode - 128] = keycode;
- + return 0;
- +#else
- + return -EINVAL;
- +#endif
- +}
- +
- +int getkeycode(unsigned int scancode)
- +{
- +#if 0
- + return
- + (scancode < SC_LIM || scancode > 255) ? -EINVAL :
- + (scancode < 128) ? high_keys[scancode - SC_LIM] :
- + e0_keys[scancode - 128];
- +#else
- + return -EINVAL;
- +#endif
- +}
- +
- +
- +/* ----------------------------------------------------------------------------------------- */
- +/*
- + * This is the keyboard specific part.
- + */
- +static void key_callback(unsigned long nr)
- +{
- + rep = 1;
- + mark_bh (KEYBOARD_BH);
- +}
- +
- +static struct timer_list key_timer =
- +{
- + NULL, NULL, 0, 0, key_callback
- +};
- +
- +static void kbd_key(unsigned int raw_keycode, int repeat_timeout)
- +{
- + unsigned int up_flag;
- + unsigned int keycode;
- + unsigned char real_keycode;
- +
- + up_flag = raw_keycode & UP_FLAG;
- + keycode = raw_keycode & 255;
- +
- + /*
- + * Separate out the mouse stuff first
- + */
- + if (keycode >= 0x70 && keycode <= 0x72) {
- +#ifdef CONFIG_KBDMOUSE
- + switch (keycode) {
- + case 0x70: /* Left mouse button */
- + if (up_flag)
- + mouse_buttons &= ~4;
- + else
- + mouse_buttons |= 4;
- + break;
- +
- + case 0x71: /* Middle mouse button */
- + if (up_flag)
- + mouse_buttons &= ~2;
- + else
- + mouse_buttons |= 2;
- + break;
- +
- + case 0x72:/* Right mouse button */
- + if (up_flag)
- + mouse_buttons &= ~1;
- + else
- + mouse_buttons |= 1;
- + break;
- + default:
- +#ifdef KBD_REPORT_UNKN
- + printk ("kbd: unknown scancode 0x%04x\n", raw_keycode);
- +#endif
- + return;
- + }
- + add_mouse_randomness (mouse_buttons << 16);
- + mouse_ready = 1;
- + wake_up_interruptible (&mouse_wait);
- +#endif
- + mark_bh (KEYBOARD_BH);
- + return;
- + }
- +
- + if (keycode >= 0x70) {
- +#ifdef KBD_REPORT_UNKN
- + printk ("kbd: unknown scancode 0x%04x\n", raw_keycode);
- +#endif
- + return;
- + }
- +
- + real_keycode = scancode_2_colrow[keycode];
- +
- + /*
- + * 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 fouth: (@ = 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...
- + */
- +
- + 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);
- +
- + }
- +
- + if (kbd_repeatkey == (raw_keycode & ~UP_FLAG) || !up_flag)
- + kbd_repeatkey = -1;
- +
- + add_keyboard_randomness (raw_keycode);
- +
- + /*
- + * We have to disable interrupts here to prevent the timer from
- + * being put on the list twice or a key being released during
- + * the processing of autorepeat
- + */
- + del_timer (&key_timer);
- +
- + tty = *vtdata.fgconsole->tty;
- + kbd = vtdata.fgconsole->kbd;
- +
- + if (up_flag) {
- + rep = 0;
- + clear_bit (keycode, key_down);
- + /* don't report an error if key is already up - happens when a ghost key is released */
- + } else
- + rep = set_bit (keycode, key_down);
- +
- + if (kbd->kbdmode == VC_RAW || kbd->kbdmode == VC_MEDIUMRAW) {
- + put_queue (keycode | (up_flag ? 0x80 : 0));
- + return;
- + }
- +
- + /*
- + * We have to do our own auto-repeat processing...
- + */
- + if (!up_flag && repeat_timeout) {
- + kbd_repeatkey = raw_keycode;
- + if (vc_kbd_mode(kbd, VC_REPEAT)) {
- + key_timer.expires = jiffies + repeat_timeout;
- + add_timer(&key_timer);
- + }
- + }
- +
- + /*
- + * Repeat a key only if the input buffers are empty or the
- + * characters get echoed locally. This makes key repeat
- + * usable with slow applications and under heavy loads.
- + */
- + if (!rep || (vc_kbd_mode (kbd, VC_REPEAT) && tty &&
- + (L_ECHO(tty) || (tty->driver.chars_in_buffer (tty) == 0)))) {
- + u_short keysym;
- + u_char type;
- +
- + /* the XOR below used to be an OR */
- + int shift_final = shift_state ^ kbd->lockstate ^ kbd->slockstate;
- + u_short *key_map = key_maps[shift_final];
- +
- + if (key_map != NULL) {
- + keysym = key_map[keycode];
- + type = KTYP(keysym);
- +
- + if (type >= 0xf0) {
- + type -= 0xf0;
- +
- + if (type == KT_LETTER) {
- + type = KT_LATIN;
- + if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
- + key_map = key_maps[shift_final ^ (1<<KG_SHIFT)];
- + if (key_map)
- + keysym = key_map[keycode];
- + }
- + }
- + (*key_handler[type])(keysym & 0xff, up_flag ? 0x80 : 0);
- + if (type != KT_SLOCK)
- + kbd->slockstate = 0;
- + } else {
- + if (!up_flag)
- + to_utf8(keysym);
- + }
- + } else {
- + /*
- + * maybe beep?
- + * we have at least to update shift_state
- + * how? two almost equivalent choices follow
- + */
- +#if 1
- + compute_shiftstate ();
- +#else
- + keysym = U(plain_map[keycode]);
- + type = KTYP(keysym);
- + if (type == KT_SHIFT)
- + (*key_handler[type]) (keysym & 0xff, up_flag ? 0x80 : 0);
- +#endif
- + }
- + }
- +}
- +
- +static void kbd_sendval(unsigned char val)
- +{
- + kbd_sendvala[kbd_sendptri] = val;
- + kbd_sendptri = (kbd_sendptri + 1) & 3;
- + disable_irq (15);
- + enable_irq (14);
- +}
- +
- +static void kbd_tx(int irq, void *dev_id, struct pt_regs *regs)
- +{
- + if(kbd_sendptri != kbd_sendptro) {
- + IOC_KARTTX = kbd_sendvala[kbd_sendptro];
- + kbd_sendptro = (kbd_sendptro + 1) & 3;
- + }
- + if(kbd_sendptri == kbd_sendptro) {
- + disable_irq (14);
- + enable_irq (15);
- + }
- +}
- +
- +static void kbd_reset(void)
- +{
- + int i;
- +
- + for (i = 0; i < NR_SHIFT; i++)
- + k_down[i] = 0;
- +
- + for (i = 0; i < 256/BITS_PER_SHORT; i++)
- + key_down[i] = 0;
- +
- + for (i = 0; i < 128/BITS_PER_SHORT; i++)
- + ghost_down[i] = 0;
- +
- + shift_state = 0;
- +}
- +
- +/*
- + * Keyboard states:
- + * 0 initial reset condition, sent HRST, wait for HRST
- + * 1 Send HRST, wait for HRST acknowledge
- + * 2 Sent RAK1, wait for RAK1
- + * 3 Sent RAK2, wait for RAK2
- + * 4 Sent SMAK, wait for anything
- + * 5 Wait for second keyboard nibble for key pressed
- + * 6 Wait for second keyboard nibble for key released
- + * 7 Wait for second part of mouse data
- + */
- +#define KBD_INITRST 0
- +#define KBD_RAK1 1
- +#define KBD_RAK2 2
- +#define KBD_ID 3
- +#define KBD_IDLE 4
- +#define KBD_KEYDOWN 5
- +#define KBD_KEYUP 6
- +#define KBD_MOUSE 7
- +
- +static void kbd_rx(int irq, void *dev_id, struct pt_regs *regs)
- +{
- + int keyval;
- + static signed char kbd_mousedx=0;
- + signed char kbd_mousedy;
- + static unsigned char kbd_state=KBD_INITRST;
- + static unsigned char kbd_keyhigh=0;
- +
- + pt_regs = regs;
- +
- + keyval = IOC_KARTRX;
- +
- + switch(kbd_state) {
- + case KBD_INITRST: /* hard reset - sent HRST */
- + if (keyval == HRST) {
- + kbd_reset ();
- + kbd_sendval (RAK1);
- + kbd_state = KBD_RAK1;
- + } else
- + goto kbd_wontreset;
- + break;
- +
- + case KBD_RAK1:/* Sent RAK1 */
- + switch (keyval) {
- + case HRST:
- + kbd_sendval (RAK1);
- + kbd_state = KBD_INITRST;
- + break;
- + case RAK1:
- + kbd_sendval (RAK2);
- + kbd_state = KBD_RAK2;
- + break;
- + default:
- + goto kbd_wontreset;
- + }
- + break;
- +
- + case KBD_RAK2:/* Sent RAK2 */
- + switch (keyval) {
- + case HRST:
- + kbd_sendval (HRST);
- + kbd_state = KBD_INITRST;
- + break;
- + case RAK2:
- + if (kbd_id == -1) {
- + kbd_sendval (NACK);
- + kbd_sendval (RQID);
- + kbd_state = KBD_ID;
- + } else {
- + kbd_sendval (SMAK);
- + kbd_state = KBD_IDLE;
- + ledstate = 0xff;
- + mark_bh (KEYBOARD_BH);
- + }
- + break;
- + default:
- + goto kbd_wontreset;
- + }
- + break;
- +
- + case KBD_ID:
- + if (keyval == HRST) {
- + kbd_sendval (HRST);
- + kbd_state = KBD_INITRST;
- + kbd_id = -2;
- + wake_up(&kbd_waitq);
- + break;
- + } else
- + if ((keyval & 0xc0) == 0x80) {
- + kbd_id = keyval & 0x3f;
- + kbd_sendval (SMAK);
- + kbd_state = KBD_IDLE;
- + ledstate = 0xff;
- + mark_bh (KEYBOARD_BH);
- + wake_up(&kbd_waitq);
- + break;
- + }
- + break;
- +
- + case KBD_IDLE:/* Send SMAK, ready for any reply */
- + if (keyval == HRST) {
- + kbd_sendval (HRST);
- + kbd_state = KBD_INITRST;
- + } else
- + if (keyval & 0x80) {
- + if (!(keyval & 0x40)) {
- + if (kbd_id == -1)
- + kbd_id = keyval & 0x3f;
- + else {
- + kbd_state = KBD_INITRST;
- + kbd_sendval (HRST);
- + }
- + break;
- + }
- + switch (keyval & 0xf0) {
- + case 0xc0:
- + kbd_keyhigh = keyval;
- + kbd_state = KBD_KEYDOWN;
- + kbd_sendval (BACK);
- + break;
- +
- + case 0xd0:
- + kbd_keyhigh = keyval;
- + kbd_state = KBD_KEYUP;
- + kbd_sendval (BACK);
- + break;
- +
- + default:
- + kbd_state = KBD_INITRST;
- + kbd_sendval (HRST);
- + }
- + } else {
- + kbd_mousedx = keyval & 0x40 ? keyval|0x80 : keyval;
- + kbd_state = KBD_MOUSE;
- + kbd_sendval (BACK);
- + }
- + break;
- +
- + case KBD_KEYDOWN:
- + if ((keyval & 0xf0) != 0xc0)
- + goto kbd_error;
- + else {
- + kbd_state = KBD_IDLE;
- + kbd_sendval (SMAK);
- + if (((kbd_keyhigh ^ keyval) & 0xf0) == 0) {
- + kbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0), kbd_repeattimeout);
- + mark_bh (KEYBOARD_BH);
- + }
- + }
- + break;
- +
- + case KBD_KEYUP:
- + if ((keyval & 0xf0) != 0xd0)
- + goto kbd_error;
- + else {
- + kbd_state = KBD_IDLE;
- + kbd_sendval (SMAK);
- + if (((kbd_keyhigh ^ keyval) & 0xf0) == 0) {
- + kbd_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0) | UP_FLAG, 0);
- + mark_bh (KEYBOARD_BH);
- + }
- + }
- + break;
- +
- + case KBD_MOUSE:
- + if (keyval & 0x80)
- + goto kbd_error;
- + else {
- + kbd_state = KBD_IDLE;
- + kbd_sendval (SMAK);
- + kbd_mousedy = (char)(keyval & 0x40 ? keyval | 0x80 : keyval);
- +#ifdef CONFIG_KBDMOUSE
- + mouse_dypos += (int)kbd_mousedy;
- + mouse_dxpos += (int)kbd_mousedx;
- + add_mouse_randomness ((mouse_buttons << 16) + (kbd_mousedy << 8) + kbd_mousedx);
- + mouse_ready = 1;
- + wake_up_interruptible (&mouse_wait);
- +#endif
- + mark_bh (KEYBOARD_BH);
- + }
- + break;
- + }
- + return;
- +kbd_wontreset:
- +#ifdef KBD_REPORT_ERR
- + printk ("kbd: keyboard won't reset (kbdstate %d, keyval %02X)\n", kbd_state,
- + keyval);
- +#endif
- + kbd_sendval (HRST);
- + kbd_state = KBD_INITRST;
- + return;
- +kbd_error:
- +#ifdef KBD_REPORT_ERR
- + printk ("kbd: keyboard out of sync - resetting\n");
- +#endif
- + kbd_sendval (HRST);
- + kbd_state = KBD_INITRST;
- + return;
- +}
- +
- +/* ----------------------------------------------------------------------------------------- */
- +/*
- + * Here starts a copy of the PC keyboard code from linux/drivers/char/keyboard.c
- + */
- +
- +static void put_queue (int ch)
- +{
- + wake_up (&keypress_wait);
- + if (tty) {
- + tty_insert_flip_char (tty, ch, 0);
- + tty_schedule_flip (tty);
- + }
- +}
- +
- +static void puts_queue(char *cp)
- +{
- + wake_up (&keypress_wait);
- + if (!tty)
- + return;
- +
- + while (*cp)
- + tty_insert_flip_char (tty, *cp++, 0);
- + tty_schedule_flip (tty);
- +}
- +
- +static void applkey(int key, char mode)
- +{
- + static char buf[] = { 0x1b, 'O', 0x00, 0x00};
- +
- + buf[1] = (mode ? 'O' : '[');
- + buf[2] = key;
- + puts_queue(buf);
- +}
- +
- +static void enter(void)
- +{
- + put_queue (13);
- + if (vc_kbd_mode(kbd, VC_CRLF))
- + put_queue (10);
- +}
- +
- +static void caps_toggle(void)
- +{
- + if(rep)
- + return;
- + chg_vc_kbd_led(kbd, VC_CAPSLOCK);
- +}
- +
- +static void caps_on (void)
- +{
- + if (rep)
- + return;
- + set_vc_kbd_led (kbd, VC_CAPSLOCK);
- +}
- +
- +static void show_ptregs (void)
- +{
- + if (pt_regs)
- + show_regs (pt_regs);
- +}
- +
- +static void show_stack (void)
- +{
- + unsigned long *p;
- + unsigned long *q = (unsigned long *)((int)current->kernel_stack_page + 4096);
- + int i;
- + __asm__("mov %0, sp\n\t": "=r" (p));
- +
- + for(i=0; p < q; p++, i++) {
- + if(i && !(i & 7))
- + printk("\n");
- + printk("%08lX ", *p);
- + }
- +}
- +
- +static void hold (void)
- +{
- + if(rep || !tty)
- + return;
- +
- + /*
- + * Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty);
- + * these routines are also activated by ^S/^Q.
- + * (And SCROLLOCK can also be set by the KDSKBLED.)
- + */
- + if (tty->stopped)
- + start_tty (tty);
- + else
- + stop_tty (tty);
- +}
- +
- +static void num (void)
- +{
- + if(vc_kbd_mode(kbd,VC_APPLIC))
- + applkey('P', 1);
- + else
- + bare_num ();
- +}
- +
- +/*
- + * Bind this to Shift-NumLock if you work in application keypad mode
- + * but want to be able to change the NumLock flag.
- + * Bind this to NumLock if you prefer that the NumLock key always
- + * changes the NumLock flag.
- + */
- +static void bare_num (void)
- +{
- + if (!rep)
- + chg_vc_kbd_led (kbd, VC_NUMLOCK);
- +}
- +
- +static void lastcons (void)
- +{
- + want_console = last_console;
- +}
- +
- +static void decr_console (void)
- +{
- + int i;
- + int fgnum = vtdata.fgconsole->num - 1;
- +
- + for (i = fgnum - 1; i != fgnum; i--) {
- + if (i == -1)
- + i = MAX_NR_CONSOLES - 1;
- + if (vt_allocated (vt_con_data + i))
- + break;
- + }
- + want_console = vt_con_data + i;
- +}
- +
- +static void incr_console (void)
- +{
- + int i;
- + int fgnum = vtdata.fgconsole->num - 1;
- +
- + for (i = fgnum + 1; i != fgnum; i++) {
- + if (i == MAX_NR_CONSOLES)
- + i = 0;
- + if (vt_allocated (vt_con_data + i))
- + break;
- + }
- + want_console = vt_con_data + i;
- +}
- +
- +static void send_intr (void)
- +{
- + if (!tty)
- + return;
- + tty_insert_flip_char (tty, 0, TTY_BREAK);
- + tty_schedule_flip (tty);
- +}
- +
- +static void scroll_forw (void)
- +{
- +}
- +
- +static void scroll_back (void)
- +{
- +}
- +
- +static void boot_it (void)
- +{
- + ctrl_alt_del();
- +}
- +
- +static void compose (void)
- +{
- + dead_key_next = 1;
- +}
- +
- +static int spawnpid, spawnsig;
- +
- +static void spawn_console (void)
- +{
- + if (spawnpid)
- + if (kill_proc (spawnpid, spawnsig, 1))
- + spawnpid = 0;
- +}
- +
- +static void SAK (void)
- +{
- + do_SAK(tty);
- +#if 0
- + /*
- + * Need to fix SAK handling to fix up RAW/MEDIUM_RAW and
- + * vt_cons modes before we can enable RAW/MEDIUM_RAW SAK
- + * handling.
- + *
- + * We should do this some day --- the whole point of a secure
- + * attention key is that it should be guaranteed to always
- + * work.
- + */
- + vt_reset (vtdata.fgconsole);
- + do_unblank_screen (); /* not in interrupt routine? */
- +#endif
- +}
- +
- +static void do_ignore (unsigned char value, char up_flag)
- +{
- +}
- +
- +static void do_null (void)
- +{
- + compute_shiftstate ();
- +}
- +
- +static void do_spec (unsigned char value,char up_flag)
- +{
- + if (up_flag)
- + return;
- + if (value >= SIZE(spec_fn_table))
- + return;
- + if (!spec_fn_table[value])
- + return;
- + spec_fn_table[value] ();
- +}
- +
- +static void do_lowercase (unsigned char value, char up_flag)
- +{
- + printk("keyboard.c: do_lowercase was called - impossible\n");
- +}
- +
- +static void do_self(unsigned char value, char up_flag)
- +{
- + if (up_flag)
- + return; /* no action, if this is a key release */
- +
- + if (diacr)
- + value = handle_diacr (value);
- +
- + if (dead_key_next) {
- + dead_key_next = 0;
- + diacr = value;
- + return;
- + }
- + put_queue (value);
- +}
- +
- +#define A_GRAVE '`'
- +#define A_ACUTE '\''
- +#define A_CFLEX '^'
- +#define A_TILDE '~'
- +#define A_DIAER '"'
- +static unsigned char ret_diacr[] = {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER };
- +
- +static void do_dead(unsigned char value, char up_flag)
- +{
- + if (up_flag)
- + return;
- +
- + value = ret_diacr[value];
- + if (diacr == value) { /* pressed twice */
- + diacr = 0;
- + put_queue (value);
- + return;
- + }
- + diacr = value;
- +}
- +
- +static unsigned char handle_diacr(unsigned char ch)
- +{
- + int d = diacr;
- + int i;
- +
- + diacr = 0;
- + if (ch == ' ')
- + return d;
- +
- + for (i = 0; i < accent_table_size; i++) {
- + if (accent_table[i].diacr == d && accent_table[i].base == ch)
- + return accent_table[i].result;
- + }
- +
- + put_queue (d);
- + return ch;
- +}
- +
- +static void do_cons(unsigned char value, char up_flag)
- +{
- + if (up_flag)
- + return;
- + want_console = vt_con_data + value;
- +}
- +
- +static void do_fn(unsigned char value, char up_flag)
- +{
- + if (up_flag)
- + return;
- +
- + if (value < SIZE(func_table)) {
- + if (func_table[value])
- + puts_queue (func_table[value]);
- + } else
- + printk ("do_fn called with value=%d\n",value);
- +}
- +
- +static void do_pad(unsigned char value, char up_flag)
- +{
- + static char *pad_chars = "0123456789+-*/\015,.?#";
- + static char *app_map = "pqrstuvwxylmRQMnn?S";
- +
- + if (up_flag)
- + return;
- +
- + if (vc_kbd_mode (kbd, VC_APPLIC) && !k_down[KG_SHIFT]) {
- + applkey (app_map[value], 1);
- + return;
- + }
- +
- + if (!vc_kbd_led (kbd,VC_NUMLOCK))
- + switch (value) {
- + case 15:
- + case 16: do_fn (22, 0); return;
- + case 7: do_fn (20, 0); return;
- + case 8: do_cur( 3, 0); return;
- + case 9: do_fn (24, 0); return;
- + case 4: do_cur( 1, 0); return;
- + case 6: do_cur( 2, 0); return;
- + case 1: do_fn (23, 0); return;
- + case 2: do_cur( 0, 0); return;
- + case 3: do_fn (25, 0); return;
- + case 5: applkey('G',vc_kbd_mode(kbd, VC_APPLIC)); return;
- + }
- +
- + put_queue (pad_chars[value]);
- + if (value == 14 && vc_kbd_mode (kbd, VC_CRLF))
- + put_queue (10);
- +}
- +
- +static void do_cur(unsigned char value, char up_flag)
- +{
- + static char *cur_chars = "BDCA";
- +
- + if (up_flag)
- + return;
- +
- + applkey (cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE));
- +}
- +
- +static void do_shift(unsigned char value, char up_flag)
- +{
- + int old_state = shift_state;
- +
- + if (rep)
- + return;
- +
- + /* Mimic typewriter: a CapsShift key acts like Shift but undoes CapsLock */
- + if (value == KVAL(K_CAPSSHIFT)) {
- + value = KVAL(K_SHIFT);
- + if (!up_flag)
- + clr_vc_kbd_led (kbd, VC_CAPSLOCK);
- + }
- +
- + if(up_flag) {
- + /* handle the case that two shift or control
- + keys are depressed simultaneously */
- + if(k_down[value])
- + k_down[value]--;
- + } else
- + k_down[value]++;
- +
- + if(k_down[value])
- + shift_state |= (1 << value);
- + else
- + shift_state &= ~ (1 << value);
- +
- + /* kludge */
- + if(up_flag && shift_state != old_state && npadch != -1) {
- + if(kbd->kbdmode == VC_UNICODE)
- + to_utf8(npadch & 0xffff);
- + else
- + put_queue(npadch & 0xff);
- + npadch = -1;
- + }
- +}
- +
- +/*
- + * Called after returning from RAW mode or when changing consoles -
- + * recompute k_down[] and shift_state from key_down[]
- + * Maybe called when keymap is undefined so that shift key release is seen
- + */
- +void compute_shiftstate(void)
- +{
- + int i, j, k, sym, val;
- +
- + shift_state = 0;
- + for (i = 0; i < SIZE(k_down); i++)
- + k_down[i] = 0;
- +
- + for (i = 0; i < SIZE(key_down); i++)
- + if (key_down[i]) { /* skip this short word if zero */
- + k = i * BITS_PER_SHORT;
- + for (j = 0; j < BITS_PER_SHORT; j++, k++)
- + if (test_bit (k, key_down)) {
- + sym = U(plain_map[k]);
- + if (KTYP(sym) == KT_SHIFT) {
- + val = KVAL (sym);
- + if (val == KVAL(K_CAPSSHIFT))
- + val = KVAL(K_SHIFT);
- + k_down[val] ++;
- + shift_state |= (1 << val);
- + }
- + }
- + }
- +}
- +
- +static void do_meta(unsigned char value, char up_flag)
- +{
- + if(up_flag)
- + return;
- +
- + if(vc_kbd_mode(kbd, VC_META)) {
- + put_queue ('\033');
- + put_queue (value);
- + } else
- + put_queue (value | 0x80);
- +}
- +
- +static void do_ascii(unsigned char value, char up_flag)
- +{
- + int base;
- +
- + if(up_flag)
- + return;
- +
- + if(value < 10)
- + base = 10;
- + else {
- + value -= 10;
- + base = 16;
- + }
- +
- + if(npadch == -1)
- + npadch = value;
- + else
- + npadch = npadch * base + value;
- +}
- +
- +static void do_lock (unsigned char value, char up_flag)
- +{
- + if (up_flag || rep)
- + return;
- + chg_vc_kbd_lock (kbd, value);
- +}
- +
- +static void do_slock (unsigned char value, char up_flag)
- +{
- + if (up_flag || rep)
- + return;
- + chg_vc_kbd_slock (kbd, value);
- +}
- +
- +/* --------------------------------------------------------------------------------------- *
- + * Led driver *
- + * --------------------------------------------------------------------------------------- */
- +
- +/*
- + * The leds display either
- + * (i) The status of NumLock, CapsLock and ScrollLock.
- + * (ii) Whatever pattern of lights people wait to show using KDSETLED.
- + * (iii) Specified bits of specified words in kernel memory.
- + */
- +
- +static unsigned char ledioctl;
- +
- +unsigned char getledstate(void)
- +{
- + return ledstate;
- +}
- +
- +void setledstate(struct kbd_struct *kbd, unsigned int led)
- +{
- + if(!(led & ~7)) {
- + ledioctl = led;
- + kbd->ledmode = LED_SHOW_IOCTL;
- + } else
- + kbd->ledmode = LED_SHOW_FLAGS;
- + set_leds();
- +}
- +
- +static struct ledptr
- +{
- + unsigned int *addr;
- + unsigned int mask;
- + unsigned char valid:1;
- +} ledptrs[3];
- +
- +void register_leds(int console, unsigned int led, unsigned int *addr, unsigned int mask)
- +{
- + struct kbd_struct *kbd = vt_con_data[console].kbd;
- +
- + if(led < 3) {
- + ledptrs[led].addr = addr;
- + ledptrs[led].mask = mask;
- + ledptrs[led].valid= 1;
- + kbd->ledmode = LED_SHOW_MEM;
- + } else
- + kbd->ledmode = LED_SHOW_FLAGS;
- +}
- +
- +static inline unsigned char getleds(void)
- +{
- + struct kbd_struct *kbd = vtdata.fgconsole->kbd;
- + unsigned char leds;
- +
- + if(kbd->ledmode == LED_SHOW_IOCTL)
- + return ledioctl;
- +
- + leds = kbd->ledflagstate;
- + if (kbd->ledmode == LED_SHOW_MEM) {
- + if (ledptrs[0].valid) {
- + if (*ledptrs[0].addr & ledptrs[0].mask)
- + leds |= 1;
- + else
- + leds &= ~1;
- + }
- + if (ledptrs[1].valid) {
- + if (*ledptrs[1].addr & ledptrs[1].mask)
- + leds |= 2;
- + else
- + leds &= ~2;
- + }
- + if (ledptrs[2].valid) {
- + if(*ledptrs[2].addr & ledptrs[2].mask)
- + leds |= 4;
- + else
- + leds &= ~4;
- + }
- + }
- + return leds;
- +}
- +
- +/*
- + * This routine is the bottom half of the keyboard interrupt
- + * routine, and runs with all interrupts enabled. It does
- + * console changing, led setting, and copy_to_cooked, which can
- + * take a reasonably long time.
- + *
- + * Aside from timing (which isn't really that important for
- + * keyboard interrupts as they happen often), using the software
- + * interrupt routines for this thing allows us to easily mask
- + * this when we don't want any of the above to happen. Not yet
- + * used, but this allows for easy and efficient race-condition
- + * prevention later on.
- + */
- +static void kbd_bh(void)
- +{
- + unsigned char leds;
- +
- + tty = *vtdata.fgconsole->tty;
- + kbd = vtdata.fgconsole->kbd;
- +
- + if (rep) {
- + /*
- + * This prevents the kbd_key routine from being called
- + * twice, once by this BH, and once by the interrupt
- + * routine. Maybe we should put the key press in a
- + * buffer or variable, and then call the BH...
- + */
- + disable_irq (IRQ_KEYBOARDRX);
- + if (kbd_repeatkey != -1)
- + kbd_key (kbd_repeatkey, kbd_repeatrate);
- + enable_irq (IRQ_KEYBOARDRX);
- + rep = 0;
- + }
- +
- + if (want_console) {
- + if(want_console != vtdata.fgconsole) {
- + vt_changeconsole (want_console);
- + /* we only changed when the console had already
- + * been allocated - a new console is not created
- + * in an interrupt routine.
- + */
- + }
- + want_console = NULL;
- + }
- +
- + leds = getleds ();
- + if (leds != ledstate) {
- + unsigned long flags;
- + ledstate = leds;
- + leds = ((leds & (1<<VC_SCROLLOCK))?4:0) | ((leds & (1<<VC_NUMLOCK))?2:0) |
- + ((leds & (1<<VC_CAPSLOCK))?1:0);
- +
- + save_flags_cli (flags);
- + intr_count -= 1;
- +
- + kbd_sendval (leds);
- +
- + intr_count += 1;
- + restore_flags(flags);
- + }
- + vt_pokeblankedconsole();
- +}
- +
- +/*
- + * Initialise a kbd struct
- + */
- +int kbd_struct_init (struct vt *vt, int init)
- +{
- + vt->kbd->ledflagstate =
- + vt->kbd->default_ledflagstate = KBD_DEFLEDS;
- + vt->kbd->ledmode = LED_SHOW_FLAGS;
- + vt->kbd->lockstate = KBD_DEFLOCK;
- + vt->kbd->slockstate = 0;
- + vt->kbd->modeflags = KBD_DEFMODE;
- + vt->kbd->kbdmode = VC_XLATE;
- + return 0;
- +}
- +
- +int kbd_ioctl (struct vt *vt, int cmd, unsigned long arg)
- +{
- + asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on);
- + int i;
- +
- + switch (cmd) {
- + case KDGKBTYPE:
- + /*
- + * This is naive.
- + */
- + i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned char));
- + if (!i)
- + put_user (KB_101, (char *)arg);
- + return i;
- +
- + case KDADDIO:
- + case KDDELIO:
- + /*
- + * KDADDIO and KDDELIO may be able to add ports beyond what
- + * we reject here, but to be safe...
- + */
- + if (arg < GPFIRST || arg > GPLAST)
- + return -EINVAL;
- + return sys_ioperm (arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0;
- +
- + case KDENABIO:
- + case KDDISABIO:
- + return sys_ioperm (GPFIRST, GPLAST, (cmd == KDENABIO)) ? -ENXIO : 0;
- +
- + case KDMAPDISP:
- + case KDUNMAPDISP:
- + /*
- + * These work like a combination of mmap and KDENABIO.
- + * this could easily be finished.
- + */
- + return -EINVAL;
- +
- + case KDSKBMODE:
- + switch (arg) {
- + case K_RAW:
- + vt->kbd->kbdmode = VC_RAW;
- + break;
- + case K_MEDIUMRAW:
- + vt->kbd->kbdmode = VC_MEDIUMRAW;
- + break;
- + case K_XLATE:
- + vt->kbd->kbdmode = VC_XLATE;
- + compute_shiftstate ();
- + break;
- + case K_UNICODE:
- + vt->kbd->kbdmode = VC_UNICODE;
- + compute_shiftstate ();
- + break;
- + default:
- + return -EINVAL;
- + }
- + if (tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer (*vt->tty);
- + return 0;
- +
- + case KDGKBMODE:
- + i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
- + if (!i) {
- + int ucval;
- + ucval = ((vt->kbd->kbdmode == VC_RAW) ? K_RAW :
- + (vt->kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW :
- + (vt->kbd->kbdmode == VC_UNICODE) ? K_UNICODE : K_XLATE);
- + put_user (ucval, (int *)arg);
- + }
- + return i;
- +
- + case KDSKBMETA:
- + /*
- + * this could be folded into KDSKBMODE, but for compatibility
- + * reasons, it is not so easy to fold kDGKBMETA into KDGKBMODE.
- + */
- + switch (arg) {
- + case K_METABIT:
- + clr_vc_kbd_mode (vt->kbd, VC_META);
- + break;
- + case K_ESCPREFIX:
- + set_vc_kbd_mode (vt->kbd, VC_META);
- + break;
- + default:
- + return -EINVAL;
- + }
- + return 0;
- +
- + case KDGKBMETA:
- + i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
- + if (!i) {
- + int ucval;
- + ucval = (vc_kbd_mode (vt->kbd, VC_META) ? K_ESCPREFIX : K_METABIT);
- + put_user (ucval, (int *)arg);
- + }
- + return i;
- +
- + case KDGETKEYCODE: {
- + struct kbkeycode *const a = (struct kbkeycode *)arg;
- +
- + i = verify_area (VERIFY_WRITE, a, sizeof (struct kbkeycode));
- + if (!i) {
- + unsigned int sc;
- +
- + sc = get_user (&a->scancode);
- + i = getkeycode (sc);
- + if (i > 0)
- + put_user (i, &a->keycode);
- + i = 0;
- + }
- + return i;
- + }
- +
- + case KDSETKEYCODE: {
- + struct kbkeycode *const a = (struct kbkeycode *)arg;
- +
- + i = verify_area (VERIFY_READ, a, sizeof (struct kbkeycode));
- + if (!i) {
- + unsigned int sc, kc;
- + sc = get_user (&a->scancode);
- + kc = get_user (&a->keycode);
- + i = setkeycode (sc, kc);
- + }
- + return i;
- + }
- +
- + case KDGKBENT: {
- + struct kbentry *const a = (struct kbentry *)arg;
- +
- + i = verify_area (VERIFY_WRITE, a, sizeof (struct kbentry));
- + if (!i) {
- + ushort *keymap, val;
- + u_char s;
- + i = get_user (&a->kb_index);
- + if (i >= NR_KEYS)
- + return -EINVAL;
- + s = get_user (&a->kb_table);
- + if (s >= MAX_NR_KEYMAPS)
- + return -EINVAL;
- + keymap = key_maps[s];
- + if (keymap) {
- + val = U(keymap[i]);
- + if (vt->kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
- + val = K_HOLE;
- + } else
- + val = (i ? K_HOLE : K_NOSUCHMAP);
- + put_user (val, &a->kb_value);
- + i = 0;
- + }
- + return i;
- + }
- +
- + case KDSKBENT: {
- + struct kbentry *const a = (struct kbentry *)arg;
- +
- + i = verify_area (VERIFY_WRITE, a, sizeof (struct kbentry));
- + if (!i) {
- + ushort *key_map;
- + u_char s;
- + u_short v, ov;
- +
- + if ((i = get_user(&a->kb_index)) >= NR_KEYS)
- + return -EINVAL;
- + if ((s = get_user(&a->kb_table)) >= MAX_NR_KEYMAPS)
- + return -EINVAL;
- + v = get_user(&a->kb_value);
- + if (!i && v == K_NOSUCHMAP) {
- + /* disallocate map */
- + key_map = key_maps[s];
- + if (s && key_map) {
- + key_maps[s] = 0;
- + if (key_map[0] == U(K_ALLOCATED)) {
- + kfree_s(key_map, sizeof(plain_map));
- + keymap_count--;
- + }
- + }
- + return 0;
- + }
- +
- + if (KTYP(v) < NR_TYPES) {
- + if (KVAL(v) > max_vals[KTYP(v)])
- + return -EINVAL;
- + } else
- + if (kbd->kbdmode != VC_UNICODE)
- + return -EINVAL;
- +
- + /* assignment to entry 0 only tests validity of args */
- + if (!i)
- + return 0;
- +
- + if (!(key_map = key_maps[s])) {
- + int j;
- +
- + if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !suser())
- + return -EPERM;
- +
- + key_map = (ushort *) kmalloc(sizeof(plain_map),
- + GFP_KERNEL);
- + if (!key_map)
- + return -ENOMEM;
- + key_maps[s] = key_map;
- + key_map[0] = U(K_ALLOCATED);
- + for (j = 1; j < NR_KEYS; j++)
- + key_map[j] = U(K_HOLE);
- + keymap_count++;
- + }
- + ov = U(key_map[i]);
- + if (v == ov)
- + return 0; /* nothing to do */
- + /*
- + * Only the Superuser can set or unset the Secure
- + * Attention Key.
- + */
- + if (((ov == K_SAK) || (v == K_SAK)) && !suser())
- + return -EPERM;
- + key_map[i] = U(v);
- + if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
- + compute_shiftstate();
- + return 0;
- + }
- + return i;
- + }
- +
- + case KDGKBSENT: {
- + struct kbsentry *a = (struct kbsentry *)arg;
- + char *p;
- + u_char *q;
- + int sz;
- +
- + i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbsentry));
- + if (i)
- + return i;
- + if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC || i < 0)
- + return -EINVAL;
- + sz = sizeof(a->kb_string) - 1; /* sz should have been
- + a struct member */
- + q = a->kb_string;
- + p = func_table[i];
- + if(p)
- + for ( ; *p && sz; p++, sz--)
- + put_user(*p, q++);
- + put_user('\0', q);
- + return ((p && *p) ? -EOVERFLOW : 0);
- + }
- +
- + case KDSKBSENT: {
- + struct kbsentry * const a = (struct kbsentry *)arg;
- + int delta;
- + char *first_free, *fj, *fnw;
- + int j, k, sz;
- + u_char *p;
- + char *q;
- +
- + i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
- + if (i)
- + return i;
- + if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC)
- + return -EINVAL;
- + q = func_table[i];
- +
- + first_free = funcbufptr + (funcbufsize - funcbufleft);
- + for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) ;
- + if (j < MAX_NR_FUNC)
- + fj = func_table[j];
- + else
- + fj = first_free;
- +
- + delta = (q ? -strlen(q) : 1);
- + sz = sizeof(a->kb_string); /* sz should have been
- + a struct member */
- + for (p = a->kb_string; get_user(p) && sz; p++,sz--)
- + delta++;
- + if (!sz)
- + return -EOVERFLOW;
- + if (delta <= funcbufleft) { /* it fits in current buf */
- + if (j < MAX_NR_FUNC) {
- + memmove(fj + delta, fj, first_free - fj);
- + for (k = j; k < MAX_NR_FUNC; k++)
- + if (func_table[k])
- + func_table[k] += delta;
- + }
- + if (!q)
- + func_table[i] = fj;
- + funcbufleft -= delta;
- + } else { /* allocate a larger buffer */
- + sz = 256;
- + while (sz < funcbufsize - funcbufleft + delta)
- + sz <<= 1;
- + fnw = (char *) kmalloc(sz, GFP_KERNEL);
- + if(!fnw)
- + return -ENOMEM;
- +
- + if (!q)
- + func_table[i] = fj;
- + if (fj > funcbufptr)
- + memmove(fnw, funcbufptr, fj - funcbufptr);
- + for (k = 0; k < j; k++)
- + if (func_table[k])
- + func_table[k] = fnw + (func_table[k] - funcbufptr);
- +
- + if (first_free > fj) {
- + memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
- + for (k = j; k < MAX_NR_FUNC; k++)
- + if (func_table[k])
- + func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
- + }
- + if (funcbufptr != func_buf)
- + kfree_s(funcbufptr, funcbufsize);
- + funcbufptr = fnw;
- + funcbufleft = funcbufleft - delta + sz - funcbufsize;
- + funcbufsize = sz;
- + }
- + for (p = a->kb_string, q = func_table[i]; ; p++, q++)
- + if (!(*q = get_user(p)))
- + break;
- + return 0;
- + }
- +
- + case KDGKBDIACR: {
- + struct kbdiacrs *a = (struct kbdiacrs *)arg;
- +
- + i = verify_area(VERIFY_WRITE, (void *) a, sizeof(struct kbdiacrs));
- + if (i)
- + return i;
- + put_user(accent_table_size, &a->kb_cnt);
- + memcpy_tofs(a->kbdiacr, accent_table,
- + accent_table_size*sizeof(struct kbdiacr));
- + return 0;
- + }
- +
- + case KDSKBDIACR: {
- + struct kbdiacrs *a = (struct kbdiacrs *)arg;
- + unsigned int ct;
- +
- + i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs));
- + if (i)
- + return i;
- + ct = get_user(&a->kb_cnt);
- + if (ct >= MAX_DIACR)
- + return -EINVAL;
- + accent_table_size = ct;
- + memcpy_fromfs(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr));
- + return 0;
- + }
- +
- + /* the ioctls below read/set the flags usually shown in the leds */
- + /* don't use them - they will go away without warning */
- + case KDGKBLED:
- + i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
- + if (i)
- + return i;
- + put_user(vt->kbd->ledflagstate |
- + (vt->kbd->default_ledflagstate << 4), (char *) arg);
- + return 0;
- +
- + case KDSKBLED:
- + if (arg & ~0x77)
- + return -EINVAL;
- + vt->kbd->ledflagstate = (arg & 7);
- + vt->kbd->default_ledflagstate = ((arg >> 4) & 7);
- + set_leds ();
- + return 0;
- +
- + /* the ioctls below only set the lights, not the functions */
- + /* for those, see KDGKBLED and KDSKBLED above */
- + case KDGETLED:
- + i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
- + if (i)
- + return i;
- + put_user(getledstate(), (char *) arg);
- + return 0;
- +
- + case KDSETLED:
- + setledstate(kbd, arg);
- + return 0;
- +
- + /*
- + * A process can indicate its willingness to accept signals
- + * generated by pressing an appropriate key combination.
- + * Thus, one can have a daemon that e.g. spawns a new console
- + * upon a keypess and then changes to it.
- + * Probably init should be changed to do this (and have a
- + * field ks (`keyboard signal') in inittab describing the
- + * desired acion), so that the number of background daemons
- + * does not increase.
- + */
- + case KDSIGACCEPT: {
- + if (arg < 1 || arg > NSIG || arg == SIGKILL)
- + return -EINVAL;
- + spawnpid = current->pid;
- + spawnsig = arg;
- + return 0;
- + }
- + default:
- + return -ENOIOCTLCMD;
- + }
- +}
- +
- +int kbd_init (void)
- +{
- + unsigned long flags;
- +
- + save_flags_cli (flags);
- + if (request_irq (IRQ_KEYBOARDRX, kbd_rx, 0, "keyboard", NULL)!=0)
- + panic("Could not allocate keyboard receive IRQ!");
- + if (request_irq (IRQ_KEYBOARDTX, kbd_tx, 0, "keyboard", NULL)!=0)
- + panic("Could not allocate keyboard transmit IRQ!");
- + disable_irq (IRQ_KEYBOARDTX);
- + (void)IOC_KARTRX;
- + restore_flags (flags);
- +
- + kbd_sendval (HRST);
- + init_bh (KEYBOARD_BH, kbd_bh);
- +
- + current->timeout = jiffies + HZ; /* wait 1s for keyboard to initialise */
- + interruptible_sleep_on(&kbd_waitq);
- +
- + printk (KERN_INFO "Keyboard driver installed v%d.%02d. (", VERSION/100, VERSION%100);
- + if (kbd_id != -1)
- + printk ("id=%d ", kbd_id);
- + printk ("English)\n");
- + return 0;
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/mem.c linux/arch/arm/drivers/char/mem.c
- --- linux.orig/arch/arm/drivers/char/mem.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/mem.c Sun Jul 28 20:48:00 1996
- @@ -0,0 +1,361 @@
- +/*
- + * linux/arch/arm/drivers/char/mem.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + *
- + * Modifications for ARM Copyright (C) 1995, 1996 Russell King
- + */
- +
- +#include <linux/config.h>
- +#include <linux/types.h>
- +#include <linux/errno.h>
- +#include <linux/sched.h>
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +#include <linux/tty.h>
- +#include <linux/miscdevice.h>
- +#include <linux/tpqic02.h>
- +#include <linux/malloc.h>
- +#include <linux/mman.h>
- +#include <linux/mm.h>
- +#include <linux/random.h>
- +
- +#include <asm/segment.h>
- +#include <asm/io.h>
- +#include <asm/pgtable.h>
- +
- +#ifdef CONFIG_SOUND
- +void soundcard_init(void);
- +#endif
- +
- +static int read_ram(struct inode * inode, struct file * file, char * buf, int count)
- +{
- + return -EIO;
- +}
- +
- +static int write_ram(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + return -EIO;
- +}
- +
- +static int read_mem(struct inode * inode, struct file * file, char * buf, int count)
- +{
- + unsigned long p = file->f_pos;
- +
- + p += PAGE_OFFSET;
- + if (count < 0)
- + return -EINVAL;
- + if (MAP_NR(p) >= MAP_NR(high_memory))
- + return 0;
- + if (count > high_memory - p)
- + count = high_memory - p;
- + memcpy_tofs(buf,(void *) p,count);
- + file->f_pos += count;
- + return count;
- +}
- +
- +static int write_mem(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + unsigned long p = file->f_pos;
- +
- + p += PAGE_OFFSET;
- + if (count < 0)
- + return -EINVAL;
- + if (MAP_NR(p) >= MAP_NR(high_memory))
- + return 0;
- + if (count > high_memory - p)
- + count = high_memory - p;
- + memcpy_fromfs((void *) p,buf,count);
- + file->f_pos += count;
- + return count;
- +}
- +
- +static int mmap_mem(struct inode * inode, struct file * file, struct vm_area_struct * vma)
- +{
- + if (vma->vm_offset & ~PAGE_MASK)
- + return -ENXIO;
- +
- + if (remap_page_range(vma->vm_start, vma->vm_offset, vma->vm_end - vma->vm_start, vma->vm_page_prot))
- + return -EAGAIN;
- + vma->vm_inode = inode;
- + inode->i_count++;
- + return 0;
- +}
- +
- +static int read_kmem(struct inode *inode, struct file *file, char *buf, int count)
- +{
- + int read1, read2;
- +
- + read1 = read_mem(inode, file, buf, count);
- + if (read1 < 0)
- + return read1;
- + read2 = vread(buf + read1, (char *) ((unsigned long) file->f_pos), count - read1);
- + if (read2 < 0)
- + return read2;
- + file->f_pos += read2;
- + return read1 + read2;
- +}
- +
- +static int read_port(struct inode * inode, struct file * file,char * buf, int count)
- +{
- + unsigned int i = file->f_pos;
- + char * tmp = buf;
- +
- + while (count-- > 0 && i < 65536) {
- + put_user(inb(i),tmp);
- + i++;
- + tmp++;
- + }
- + file->f_pos = i;
- + return tmp-buf;
- +}
- +
- +static int write_port(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + unsigned int i = file->f_pos;
- + const char * tmp = buf;
- +
- + while (count-- > 0 && i < 65536) {
- + outb(get_user(tmp),i);
- + i++;
- + tmp++;
- + }
- + file->f_pos = i;
- + return tmp-buf;
- +}
- +
- +static int read_null(struct inode * node, struct file * file, char * buf, int count)
- +{
- + return 0;
- +}
- +
- +static int write_null(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + return count;
- +}
- +
- +static int read_zero(struct inode * node, struct file * file, char * buf, int count)
- +{
- + int left;
- +
- + for (left = count; left > 0; left--) {
- + put_user(0,buf);
- + buf++;
- + if (need_resched)
- + schedule();
- + }
- + return count;
- +}
- +
- +static int mmap_zero(struct inode * inode, struct file * file, struct vm_area_struct * vma)
- +{
- + if (vma->vm_flags & VM_SHARED)
- + return -EINVAL;
- + if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot))
- + return -EAGAIN;
- + return 0;
- +}
- +
- +static int read_full(struct inode * node, struct file * file, char * buf,int count)
- +{
- + return count;
- +}
- +
- +static int write_full(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + return -ENOSPC;
- +}
- +
- +/*
- + * Special lseek() function for /dev/null and /dev/zero. Most notably, you can fopen()
- + * both devices with "a" now. This was previously impossible. SRB.
- + */
- +
- +static int null_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
- +{
- + return file->f_pos=0;
- +}
- +/*
- + * The memory devices use the full 32/64 bits of the offset, and so we cannot
- + * check against negative addresses: they are ok. The return value is weird,
- + * though, in that case (0).
- + *
- + * also note that seeking relative to the "end of file" isn't supported:
- + * it has no meaning, so it returns -EINVAL.
- + */
- +static int memory_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
- +{
- + switch (orig) {
- + case 0:
- + file->f_pos = offset;
- + return file->f_pos;
- + case 1:
- + file->f_pos += offset;
- + return file->f_pos;
- + default:
- + return -EINVAL;
- + }
- + if (file->f_pos < 0)
- + return 0;
- + return file->f_pos;
- +}
- +
- +#define write_kmem write_mem
- +#define mmap_kmem mmap_mem
- +#define zero_lseek null_lseek
- +#define write_zero write_null
- +
- +static struct file_operations ram_fops = {
- + memory_lseek,
- + read_ram,
- + write_ram,
- + NULL, /* ram_readdir */
- + NULL, /* ram_select */
- + NULL, /* ram_ioctl */
- + NULL, /* ram_mmap */
- + NULL, /* no special open code */
- + NULL, /* no special release code */
- + NULL /* fsync */
- +};
- +
- +static struct file_operations mem_fops = {
- + memory_lseek,
- + read_mem,
- + write_mem,
- + NULL, /* mem_readdir */
- + NULL, /* mem_select */
- + NULL, /* mem_ioctl */
- + mmap_mem,
- + NULL, /* no special open code */
- + NULL, /* no special release code */
- + NULL /* fsync */
- +};
- +
- +static struct file_operations kmem_fops = {
- + memory_lseek,
- + read_kmem,
- + write_kmem,
- + NULL, /* kmem_readdir */
- + NULL, /* kmem_select */
- + NULL, /* kmem_ioctl */
- + mmap_kmem,
- + NULL, /* no special open code */
- + NULL, /* no special release code */
- + NULL /* fsync */
- +};
- +
- +static struct file_operations null_fops = {
- + null_lseek,
- + read_null,
- + write_null,
- + NULL, /* null_readdir */
- + NULL, /* null_select */
- + NULL, /* null_ioctl */
- + NULL, /* null_mmap */
- + NULL, /* no special open code */
- + NULL, /* no special release code */
- + NULL /* fsync */
- +};
- +
- +static struct file_operations port_fops = {
- + memory_lseek,
- + read_port,
- + write_port,
- + NULL, /* port_readdir */
- + NULL, /* port_select */
- + NULL, /* port_ioctl */
- + NULL, /* port_mmap */
- + NULL, /* no special open code */
- + NULL, /* no special release code */
- + NULL /* fsync */
- +};
- +
- +static struct file_operations zero_fops = {
- + zero_lseek,
- + read_zero,
- + write_zero,
- + NULL, /* zero_readdir */
- + NULL, /* zero_select */
- + NULL, /* zero_ioctl */
- + mmap_zero,
- + NULL, /* no special open code */
- + NULL /* no special release code */
- +};
- +
- +static struct file_operations full_fops = {
- + memory_lseek,
- + read_full,
- + write_full,
- + NULL, /* full_readdir */
- + NULL, /* full_select */
- + NULL, /* full_ioctl */
- + NULL, /* full_mmap */
- + NULL, /* no special open code */
- + NULL /* no special release code */
- +};
- +
- +static int memory_open(struct inode * inode, struct file * filp)
- +{
- + switch (MINOR(inode->i_rdev)) {
- + case 0:
- + filp->f_op = &ram_fops;
- + break;
- + case 1:
- + filp->f_op = &mem_fops;
- + break;
- + case 2:
- + filp->f_op = &kmem_fops;
- + break;
- + case 3:
- + filp->f_op = &null_fops;
- + break;
- + case 4:
- + filp->f_op = &port_fops;
- + break;
- + case 5:
- + filp->f_op = &zero_fops;
- + break;
- + case 7:
- + filp->f_op = &full_fops;
- + break;
- + case 8:
- + filp->f_op = &random_fops;
- + break;
- + case 9:
- + filp->f_op = &urandom_fops;
- + break;
- + default:
- + return -ENXIO;
- + }
- + if (filp->f_op && filp->f_op->open)
- + return filp->f_op->open(inode,filp);
- + return 0;
- +}
- +
- +static struct file_operations memory_fops = {
- + NULL, /* lseek */
- + NULL, /* read */
- + NULL, /* write */
- + NULL, /* readdir */
- + NULL, /* select */
- + NULL, /* ioctl */
- + NULL, /* mmap */
- + memory_open, /* just a selector for the real open */
- + NULL, /* release */
- + NULL /* fsync */
- +};
- +
- +int chr_dev_init(void)
- +{
- + if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
- + printk("unable to get major %d for memory devs\n", MEM_MAJOR);
- + rand_initialize();
- + tty_init();
- +#ifdef CONFIG_PRINTER
- + lp_init();
- +#endif
- +#if defined(CONFIG_KBDMOUSE) || defined(CONFIG_SOFT_WATCHDOG)
- + misc_init();
- +#endif
- + return 0;
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/misc.c linux/arch/arm/drivers/char/misc.c
- --- linux.orig/arch/arm/drivers/char/misc.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/misc.c Wed Jul 31 20:26:12 1996
- @@ -0,0 +1,209 @@
- +/*
- + * linux/arch/arm/drivers/char/mouse.c
- + *
- + * Generic misc open routine by Johan Myreen
- + *
- + * Based on code from Linus
- + *
- + * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's
- + * changes incorporated into 0.97pl4
- + * by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)
- + * See busmouse.c for particulars.
- + *
- + * Made things a lot mode modular - easy to compile in just one or two
- + * of the misc drivers, as they are now completely independent. Linus.
- + *
- + * Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
- + *
- + * Fixed a failing symbol register to free the device registration
- + * Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-96
- + *
- + * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96
- + *
- + * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96
- + *
- + * Handling of mouse minor numbers for kerneld:
- + * Idea by Jacques Gelinas <jack@solucorp.qc.ca>,
- + * adapted by Bjorn Ekwall <bj0rn@blox.se>
- + * corrected by Alan Cox <alan@lxorguk.ukuu.org.uk>
- + */
- +
- +#include <linux/config.h>
- +#include <linux/module.h>
- +
- +#include <linux/fs.h>
- +#include <linux/errno.h>
- +#include <linux/miscdevice.h>
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +#include <linux/malloc.h>
- +#include <linux/proc_fs.h>
- +#include <linux/stat.h>
- +
- +#include <linux/tty.h> /* needed by selection.h */
- +#include "vt_kern.h"
- +#include "selection.h" /* export its symbols */
- +#ifdef CONFIG_KERNELD
- +#include <linux/kerneld.h>
- +#endif
- +
- +/*
- + * Head entry for the doubly linked miscdevice list
- + */
- +static struct miscdevice misc_list = { 0, "head", NULL, &misc_list, &misc_list };
- +
- +/*
- + * Assigned numbers, used for dynamic minors
- + */
- +#define DYNAMIC_MINORS 64 /* like dynamic majors */
- +static unsigned char misc_minors[DYNAMIC_MINORS / 8];
- +
- +#ifndef MODULE
- +extern void watchdog_init(void);
- +extern int arch_mouse_init(void);
- +
- +#ifdef CONFIG_PROC_FS
- +static int proc_misc_read(char *buf, char **start, off_t offset, int len, int unused)
- +{
- + struct miscdevice *p;
- +
- + len=0;
- + for (p = misc_list.next; p != &misc_list; p = p->next)
- + len += sprintf(buf+len, "%3i %s\n",p->minor, p->name ?: "");
- + return len;
- +}
- +
- +#endif /* PROC_FS */
- +#endif /* !MODULE */
- +
- +static int misc_open(struct inode * inode, struct file * file)
- +{
- + int minor = MINOR(inode->i_rdev);
- + struct miscdevice *c = misc_list.next;
- + file->f_op = NULL;
- +
- + while ((c != &misc_list) && (c->minor != minor))
- + c = c->next;
- + if (c == &misc_list) {
- +#ifdef CONFIG_KERNELD
- + char modname[20];
- + sprintf(modname, "char-major-%d-%d", MISC_MAJOR, minor);
- + request_module(modname);
- + c = misc_list.next;
- + while ((c != &misc_list) && (c->minor != minor))
- + c = c->next;
- + if (c == &misc_list)
- +#endif
- + return -ENODEV;
- + }
- +
- + if ((file->f_op = c->fops))
- + return file->f_op->open(inode,file);
- + else
- + return -ENODEV;
- +}
- +
- +static struct file_operations misc_fops = {
- + NULL, /* seek */
- + NULL, /* read */
- + NULL, /* write */
- + NULL, /* readdir */
- + NULL, /* select */
- + NULL, /* ioctl */
- + NULL, /* mmap */
- + misc_open,
- + NULL /* release */
- +};
- +
- +int misc_register(struct miscdevice * misc)
- +{
- + if (misc->next || misc->prev)
- + return -EBUSY;
- + if (misc->minor == MISC_DYNAMIC_MINOR) {
- + int i = DYNAMIC_MINORS;
- + while (--i >= 0)
- + if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)
- + break;
- + if (i<0) return -EBUSY;
- + misc->minor = i;
- + }
- + if (misc->minor < DYNAMIC_MINORS)
- + misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
- + MOD_INC_USE_COUNT;
- + misc->next = &misc_list;
- + misc->prev = misc_list.prev;
- + misc->prev->next = misc;
- + misc->next->prev = misc;
- + return 0;
- +}
- +
- +int misc_deregister(struct miscdevice * misc)
- +{
- + int i = misc->minor;
- + if (!misc->next || !misc->prev)
- + return -EINVAL;
- + MOD_DEC_USE_COUNT;
- + misc->prev->next = misc->next;
- + misc->next->prev = misc->prev;
- + misc->next = NULL;
- + misc->prev = NULL;
- + if (i < DYNAMIC_MINORS && i>0) {
- + misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
- + }
- + return 0;
- +}
- +
- +#ifdef MODULE
- +
- +#define misc_init init_module
- +
- +void cleanup_module(void)
- +{
- + unregister_chrdev(MISC_MAJOR, "misc");
- +}
- +
- +#endif
- +
- +static struct symbol_table misc_syms = {
- +/* Should this be surrounded with "#ifdef CONFIG_MODULES" ? */
- +#include <linux/symtab_begin.h>
- + X(misc_register),
- + X(misc_deregister),
- +#ifndef MODULE
- + X(set_selection), /* used by the kmouse module, can only */
- + X(paste_selection), /* be exported if misc.c is in linked in */
- +#endif
- +#include <linux/symtab_end.h>
- +};
- +
- +int misc_init(void)
- +{
- +#ifndef MODULE
- +#ifdef CONFIG_PROC_FS
- + proc_register_dynamic(&proc_root, &(struct proc_dir_entry) {
- + 0, 4, "misc",
- + S_IFREG | S_IRUGO, 1, 0, 0,
- + 0, NULL /* ops -- default to array */,
- + &proc_misc_read /* get_info */,
- + });
- +#endif /* PROC_FS */
- +#ifdef CONFIG_KBDMOUSE
- + arch_mouse_init();
- +#endif
- +#ifdef CONFIG_SOFT_WATCHDOG
- + watchdog_init();
- +#endif
- +#endif /* !MODULE */
- + if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) {
- + printk("unable to get major %d for misc devices\n",
- + MISC_MAJOR);
- + return -EIO;
- + }
- +
- + if(register_symtab(&misc_syms)!=0)
- + {
- + unregister_chrdev(MISC_MAJOR, "misc");
- + return -EIO;
- + }
- + return 0;
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/n_tty.c linux/arch/arm/drivers/char/n_tty.c
- --- linux.orig/arch/arm/drivers/char/n_tty.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/n_tty.c Fri Sep 6 21:12:49 1996
- @@ -0,0 +1,1030 @@
- +/*
- + * n_tty.c --- implements the N_TTY line discipline.
- + *
- + * This code used to be in tty_io.c, but things are getting hairy
- + * enough that it made sense to split things off. (The N_TTY
- + * processing has changed so much that it's hardly recognizable,
- + * anyway...)
- + *
- + * Note that the open routine for N_TTY is guaranteed never to return
- + * an error. This is because Linux will fall back to setting a line
- + * to N_TTY if it can not switch to any other line discipline.
- + *
- + * Written by Theodore Ts'o, Copyright 1994.
- + *
- + * This file also contains code originally written by Linus Torvalds,
- + * Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994.
- + *
- + * This file may be redistributed under the terms of the GNU Public
- + * License.
- + *
- + * Altered for ARM to reduce memory usage.
- + */
- +
- +#include <linux/types.h>
- +#include <linux/major.h>
- +#include <linux/errno.h>
- +#include <linux/signal.h>
- +#include <linux/fcntl.h>
- +#include <linux/sched.h>
- +#include <linux/interrupt.h>
- +#include <linux/tty.h>
- +#include <linux/timer.h>
- +#include <linux/ctype.h>
- +#include <linux/kd.h>
- +#include <linux/mm.h>
- +#include <linux/string.h>
- +#include <linux/malloc.h>
- +
- +#include <asm/segment.h>
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +
- +#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
- +
- +#ifndef MIN
- +#define MIN(a,b) ((a) < (b) ? (a) : (b))
- +#endif
- +
- +/* number of characters left in xmit buffer before select has we have room */
- +#define WAKEUP_CHARS 256
- +
- +/*
- + * This defines the low- and high-watermarks for throttling and
- + * unthrottling the TTY driver. These watermarks are used for
- + * controlling the space in the read buffer.
- + */
- +#define TTY_THRESHOLD_THROTTLE (N_TTY_BUF_SIZE - 128)
- +#define TTY_THRESHOLD_UNTHROTTLE 128
- +
- +static inline void put_tty_queue(unsigned char c, struct tty_struct *tty)
- +{
- + if (tty->read_cnt < N_TTY_BUF_SIZE) {
- + tty->read_buf[tty->read_head] = c;
- + tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);
- + tty->read_cnt++;
- + }
- +}
- +
- +/*
- + * Flush the input buffer
- + */
- +void n_tty_flush_buffer(struct tty_struct * tty)
- +{
- + tty->read_head = tty->read_tail = tty->read_cnt = 0;
- + tty->canon_head = tty->canon_data = tty->erasing = 0;
- + memset(&tty->read_flags, 0, sizeof tty->read_flags);
- +
- + if (!tty->link)
- + return;
- +
- + if (tty->driver.unthrottle)
- + (tty->driver.unthrottle)(tty);
- + if (tty->link->packet) {
- + tty->ctrl_status |= TIOCPKT_FLUSHREAD;
- + wake_up_interruptible(&tty->link->read_wait);
- + }
- +}
- +
- +/*
- + * Return number of characters buffered to be delivered to user
- + */
- +int n_tty_chars_in_buffer(struct tty_struct *tty)
- +{
- + return tty->read_cnt;
- +}
- +
- +/*
- + * Perform OPOST processing. Returns -1 when the output device is
- + * full and the character must be retried.
- + */
- +static int opost(unsigned char c, struct tty_struct *tty)
- +{
- + int space, spaces;
- +
- + space = tty->driver.write_room(tty);
- + if (!space)
- + return -1;
- +
- + if (O_OPOST(tty)) {
- + switch (c) {
- + case '\n':
- + if (O_ONLRET(tty))
- + tty->column = 0;
- + if (O_ONLCR(tty)) {
- + if (space < 2)
- + return -1;
- + tty->driver.put_char(tty, '\r');
- + tty->column = 0;
- + }
- + tty->canon_column = tty->column;
- + break;
- + case '\r':
- + if (O_ONOCR(tty) && tty->column == 0)
- + return 0;
- + if (O_OCRNL(tty)) {
- + c = '\n';
- + if (O_ONLRET(tty))
- + tty->canon_column = tty->column = 0;
- + break;
- + }
- + tty->canon_column = tty->column = 0;
- + break;
- + case '\t':
- + spaces = 8 - (tty->column & 7);
- + if (O_TABDLY(tty) == XTABS) {
- + if (space < spaces)
- + return -1;
- + tty->column += spaces;
- + tty->driver.write(tty, 0, " ", spaces);
- + return 0;
- + }
- + tty->column += spaces;
- + break;
- + case '\b':
- + if (tty->column > 0)
- + tty->column--;
- + break;
- + default:
- + if (O_OLCUC(tty))
- + c = toupper(c);
- + if (!iscntrl(c))
- + tty->column++;
- + break;
- + }
- + }
- + tty->driver.put_char(tty, c);
- + return 0;
- +}
- +
- +static inline void put_char(unsigned char c, struct tty_struct *tty)
- +{
- + tty->driver.put_char(tty, c);
- +}
- +
- +/* Must be called only when L_ECHO(tty) is true. */
- +
- +static void echo_char(unsigned char c, struct tty_struct *tty)
- +{
- + if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') {
- + put_char('^', tty);
- + put_char(c ^ 0100, tty);
- + tty->column += 2;
- + } else
- + opost(c, tty);
- +}
- +
- +static inline void finish_erasing(struct tty_struct *tty)
- +{
- + if (tty->erasing) {
- + put_char('/', tty);
- + tty->column += 2;
- + tty->erasing = 0;
- + }
- +}
- +
- +static void eraser(unsigned char c, struct tty_struct *tty)
- +{
- + enum { ERASE, WERASE, KILL } kill_type;
- + int head, seen_alnums;
- +
- + if (tty->read_head == tty->canon_head) {
- + /* opost('\a', tty); */ /* what do you think? */
- + return;
- + }
- + if (c == ERASE_CHAR(tty))
- + kill_type = ERASE;
- + else if (c == WERASE_CHAR(tty))
- + kill_type = WERASE;
- + else {
- + if (!L_ECHO(tty)) {
- + tty->read_cnt -= ((tty->read_head - tty->canon_head) &
- + (N_TTY_BUF_SIZE - 1));
- + tty->read_head = tty->canon_head;
- + return;
- + }
- + if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
- + tty->read_cnt -= ((tty->read_head - tty->canon_head) &
- + (N_TTY_BUF_SIZE - 1));
- + tty->read_head = tty->canon_head;
- + finish_erasing(tty);
- + echo_char(KILL_CHAR(tty), tty);
- + /* Add a newline if ECHOK is on and ECHOKE is off. */
- + if (L_ECHOK(tty))
- + opost('\n', tty);
- + return;
- + }
- + kill_type = KILL;
- + }
- +
- + seen_alnums = 0;
- + while (tty->read_head != tty->canon_head) {
- + head = (tty->read_head - 1) & (N_TTY_BUF_SIZE-1);
- + c = tty->read_buf[head];
- + if (kill_type == WERASE) {
- + /* Equivalent to BSD's ALTWERASE. */
- + if (isalnum(c) || c == '_')
- + seen_alnums++;
- + else if (seen_alnums)
- + break;
- + }
- + tty->read_head = head;
- + tty->read_cnt--;
- + if (L_ECHO(tty)) {
- + if (L_ECHOPRT(tty)) {
- + if (!tty->erasing) {
- + put_char('\\', tty);
- + tty->column++;
- + tty->erasing = 1;
- + }
- + echo_char(c, tty);
- + } else if (kill_type == ERASE && !L_ECHOE(tty)) {
- + echo_char(ERASE_CHAR(tty), tty);
- + } else if (c == '\t') {
- + unsigned int col = tty->canon_column;
- + unsigned long tail = tty->canon_head;
- +
- + /* Find the column of the last char. */
- + while (tail != tty->read_head) {
- + c = tty->read_buf[tail];
- + if (c == '\t')
- + col = (col | 7) + 1;
- + else if (iscntrl(c)) {
- + if (L_ECHOCTL(tty))
- + col += 2;
- + } else
- + col++;
- + tail = (tail+1) & (N_TTY_BUF_SIZE-1);
- + }
- +
- + /* should never happen */
- + if (tty->column > 0x80000000)
- + tty->column = 0;
- +
- + /* Now backup to that column. */
- + while (tty->column > col) {
- + /* Can't use opost here. */
- + put_char('\b', tty);
- + if (tty->column > 0)
- + tty->column--;
- + }
- + } else {
- + if (iscntrl(c) && L_ECHOCTL(tty)) {
- + put_char('\b', tty);
- + put_char(' ', tty);
- + put_char('\b', tty);
- + if (tty->column > 0)
- + tty->column--;
- + }
- + if (!iscntrl(c) || L_ECHOCTL(tty)) {
- + put_char('\b', tty);
- + put_char(' ', tty);
- + put_char('\b', tty);
- + if (tty->column > 0)
- + tty->column--;
- + }
- + }
- + }
- + if (kill_type == ERASE)
- + break;
- + }
- + if (tty->read_head == tty->canon_head)
- + finish_erasing(tty);
- +}
- +
- +static inline void isig(int sig, struct tty_struct *tty, int flush)
- +{
- + if (tty->pgrp > 0)
- + kill_pg(tty->pgrp, sig, 1);
- + if (flush || !L_NOFLSH(tty)) {
- + n_tty_flush_buffer(tty);
- + if (tty->driver.flush_buffer)
- + tty->driver.flush_buffer(tty);
- + }
- +}
- +
- +static inline void n_tty_receive_break(struct tty_struct *tty)
- +{
- + if (I_IGNBRK(tty))
- + return;
- + if (I_BRKINT(tty)) {
- + isig(SIGINT, tty, 1);
- + return;
- + }
- + if (I_PARMRK(tty)) {
- + put_tty_queue('\377', tty);
- + put_tty_queue('\0', tty);
- + }
- + put_tty_queue('\0', tty);
- + wake_up_interruptible(&tty->read_wait);
- +}
- +
- +static inline void n_tty_receive_overrun(struct tty_struct *tty)
- +{
- + char buf[64];
- +
- + tty->num_overrun++;
- + if (tty->overrun_time < (jiffies - HZ)) {
- + printk("%s: %d input overrun(s)\n", _tty_name(tty, buf),
- + tty->num_overrun);
- + tty->overrun_time = jiffies;
- + tty->num_overrun = 0;
- + }
- +}
- +
- +static inline void n_tty_receive_parity_error(struct tty_struct *tty,
- + unsigned char c)
- +{
- + if (I_IGNPAR(tty)) {
- + return;
- + }
- + if (I_PARMRK(tty)) {
- + put_tty_queue('\377', tty);
- + put_tty_queue('\0', tty);
- + put_tty_queue(c, tty);
- + } else if (I_INPCK(tty))
- + put_tty_queue('\0', tty);
- + else
- + put_tty_queue(c, tty);
- + wake_up_interruptible(&tty->read_wait);
- +}
- +
- +static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
- +{
- + if (tty->raw) {
- + put_tty_queue(c, tty);
- + return;
- + }
- +
- + if (tty->stopped && I_IXON(tty) && I_IXANY(tty)) {
- + start_tty(tty);
- + return;
- + }
- +
- + if (I_ISTRIP(tty))
- + c &= 0x7f;
- + if (I_IUCLC(tty) && L_IEXTEN(tty))
- + c=tolower(c);
- +
- + if (tty->closing) {
- + if (I_IXON(tty)) {
- + if (c == START_CHAR(tty))
- + start_tty(tty);
- + else if (c == STOP_CHAR(tty))
- + stop_tty(tty);
- + }
- + return;
- + }
- +
- + /*
- + * If the previous character was LNEXT, or we know that this
- + * character is not one of the characters that we'll have to
- + * handle specially, do shortcut processing to speed things
- + * up.
- + */
- + if (!test_bit(c, &tty->process_char_map) || tty->lnext) {
- + finish_erasing(tty);
- + tty->lnext = 0;
- + if (L_ECHO(tty)) {
- + if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
- + put_char('\a', tty); /* beep if no space */
- + return;
- + }
- + /* Record the column of first canon char. */
- + if (tty->canon_head == tty->read_head)
- + tty->canon_column = tty->column;
- + echo_char(c, tty);
- + }
- + if (I_PARMRK(tty) && c == (unsigned char) '\377')
- + put_tty_queue(c, tty);
- + put_tty_queue(c, tty);
- + return;
- + }
- +
- + if (c == '\r') {
- + if (I_IGNCR(tty))
- + return;
- + if (I_ICRNL(tty))
- + c = '\n';
- + } else if (c == '\n' && I_INLCR(tty))
- + c = '\r';
- + if (I_IXON(tty)) {
- + if (c == START_CHAR(tty)) {
- + start_tty(tty);
- + return;
- + }
- + if (c == STOP_CHAR(tty)) {
- + stop_tty(tty);
- + return;
- + }
- + }
- + if (L_ISIG(tty)) {
- + int signal;
- + signal = SIGINT;
- + if (c == INTR_CHAR(tty))
- + goto send_signal;
- + signal = SIGQUIT;
- + if (c == QUIT_CHAR(tty))
- + goto send_signal;
- + signal = SIGTSTP;
- + if (c == SUSP_CHAR(tty)) {
- +send_signal:
- + isig(signal, tty, 0);
- + return;
- + }
- + }
- + if (L_ICANON(tty)) {
- + if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
- + (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
- + eraser(c, tty);
- + return;
- + }
- + if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
- + tty->lnext = 1;
- + if (L_ECHO(tty)) {
- + finish_erasing(tty);
- + if (L_ECHOCTL(tty)) {
- + put_char('^', tty);
- + put_char('\b', tty);
- + }
- + }
- + return;
- + }
- + if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&
- + L_IEXTEN(tty)) {
- + unsigned long tail = tty->canon_head;
- +
- + finish_erasing(tty);
- + echo_char(c, tty);
- + opost('\n', tty);
- + while (tail != tty->read_head) {
- + echo_char(tty->read_buf[tail], tty);
- + tail = (tail+1) & (N_TTY_BUF_SIZE-1);
- + }
- + return;
- + }
- + if (c == '\n') {
- + if (L_ECHO(tty) || L_ECHONL(tty)) {
- + if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
- + put_char('\a', tty);
- + return;
- + }
- + opost('\n', tty);
- + }
- + goto handle_newline;
- + }
- + if (c == EOF_CHAR(tty)) {
- + if (tty->canon_head != tty->read_head)
- + set_bit(TTY_PUSH, &tty->flags);
- + c = __DISABLED_CHAR;
- + goto handle_newline;
- + }
- + if ((c == EOL_CHAR(tty)) ||
- + (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {
- + /*
- + * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
- + */
- + if (L_ECHO(tty)) {
- + if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
- + put_char('\a', tty);
- + return;
- + }
- + /* Record the column of first canon char. */
- + if (tty->canon_head == tty->read_head)
- + tty->canon_column = tty->column;
- + echo_char(c, tty);
- + }
- + /*
- + * XXX does PARMRK doubling happen for
- + * EOL_CHAR and EOL2_CHAR?
- + */
- + if (I_PARMRK(tty) && c == (unsigned char) '\377')
- + put_tty_queue(c, tty);
- +
- + handle_newline:
- + set_bit(tty->read_head, &tty->read_flags);
- + put_tty_queue(c, tty);
- + tty->canon_head = tty->read_head;
- + tty->canon_data++;
- + if (tty->fasync)
- + kill_fasync(tty->fasync, SIGIO);
- + if (tty->read_wait)
- + wake_up_interruptible(&tty->read_wait);
- + return;
- + }
- + }
- +
- + finish_erasing(tty);
- + if (L_ECHO(tty)) {
- + if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
- + put_char('\a', tty); /* beep if no space */
- + return;
- + }
- + if (c == '\n')
- + opost('\n', tty);
- + else {
- + /* Record the column of first canon char. */
- + if (tty->canon_head == tty->read_head)
- + tty->canon_column = tty->column;
- + echo_char(c, tty);
- + }
- + }
- +
- + if (I_PARMRK(tty) && c == (unsigned char) '\377')
- + put_tty_queue(c, tty);
- +
- + put_tty_queue(c, tty);
- +}
- +
- +static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
- + char *fp, int count)
- +{
- + const unsigned char *p;
- + char *f, flags = 0;
- + int i;
- +
- + if (!tty->read_buf)
- + return;
- +
- + if (tty->real_raw) {
- + i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
- + N_TTY_BUF_SIZE - tty->read_head));
- + memcpy(tty->read_buf + tty->read_head, cp, i);
- + tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
- + tty->read_cnt += i;
- + cp += i;
- + count -= i;
- +
- + i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
- + N_TTY_BUF_SIZE - tty->read_head));
- + memcpy(tty->read_buf + tty->read_head, cp, i);
- + tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
- + tty->read_cnt += i;
- + } else {
- + for (i=count, p = cp, f = fp; i; i--, p++) {
- + if (f)
- + flags = *f++;
- + switch (flags) {
- + case TTY_NORMAL:
- + n_tty_receive_char(tty, *p);
- + break;
- + case TTY_BREAK:
- + n_tty_receive_break(tty);
- + break;
- + case TTY_PARITY:
- + case TTY_FRAME:
- + n_tty_receive_parity_error(tty, *p);
- + break;
- + case TTY_OVERRUN:
- + n_tty_receive_overrun(tty);
- + break;
- + default:
- + printk("%s: unknown flag %d\n", tty_name(tty),
- + flags);
- + break;
- + }
- + }
- + if (tty->driver.flush_chars)
- + tty->driver.flush_chars(tty);
- + }
- +
- + if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
- + if (tty->fasync)
- + kill_fasync(tty->fasync, SIGIO);
- + if (tty->read_wait)
- + wake_up_interruptible(&tty->read_wait);
- + }
- +
- + if ((tty->read_cnt >= TTY_THRESHOLD_THROTTLE) &&
- + tty->driver.throttle &&
- + !set_bit(TTY_THROTTLED, &tty->flags))
- + tty->driver.throttle(tty);
- +}
- +
- +static int n_tty_receive_room(struct tty_struct *tty)
- +{
- + int left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
- +
- + /*
- + * If we are doing input canonicalization, and there are no
- + * pending newlines, let characters through without limit, so
- + * that erase characters will be handled. Other excess
- + * characters will be beeped.
- + */
- + if (tty->icanon && !tty->canon_data)
- + return N_TTY_BUF_SIZE;
- +
- + if (left > 0)
- + return left;
- + return 0;
- +}
- +
- +int is_ignored(int sig)
- +{
- + return ((current->blocked & (1<<(sig-1))) ||
- + (current->sig->action[sig-1].sa_handler == SIG_IGN));
- +}
- +
- +static void n_tty_set_termios(struct tty_struct *tty, struct termios * old)
- +{
- + if (!tty)
- + return;
- +
- + tty->icanon = (L_ICANON(tty) != 0);
- + if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) ||
- + I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
- + I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
- + I_PARMRK(tty)) {
- + cli();
- + memset(tty->process_char_map, 0, 256/8);
- +
- + if (I_IGNCR(tty) || I_ICRNL(tty))
- + set_bit('\r', &tty->process_char_map);
- + if (I_INLCR(tty))
- + set_bit('\n', &tty->process_char_map);
- +
- + if (L_ICANON(tty)) {
- + set_bit(ERASE_CHAR(tty), &tty->process_char_map);
- + set_bit(KILL_CHAR(tty), &tty->process_char_map);
- + set_bit(EOF_CHAR(tty), &tty->process_char_map);
- + set_bit('\n', &tty->process_char_map);
- + set_bit(EOL_CHAR(tty), &tty->process_char_map);
- + if (L_IEXTEN(tty)) {
- + set_bit(WERASE_CHAR(tty),
- + &tty->process_char_map);
- + set_bit(LNEXT_CHAR(tty),
- + &tty->process_char_map);
- + set_bit(EOL2_CHAR(tty),
- + &tty->process_char_map);
- + if (L_ECHO(tty))
- + set_bit(REPRINT_CHAR(tty),
- + &tty->process_char_map);
- + }
- + }
- + if (I_IXON(tty)) {
- + set_bit(START_CHAR(tty), &tty->process_char_map);
- + set_bit(STOP_CHAR(tty), &tty->process_char_map);
- + }
- + if (L_ISIG(tty)) {
- + set_bit(INTR_CHAR(tty), &tty->process_char_map);
- + set_bit(QUIT_CHAR(tty), &tty->process_char_map);
- + set_bit(SUSP_CHAR(tty), &tty->process_char_map);
- + }
- + clear_bit(__DISABLED_CHAR, &tty->process_char_map);
- + sti();
- + tty->raw = 0;
- + tty->real_raw = 0;
- + } else {
- + tty->raw = 1;
- + if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) &&
- + (I_IGNPAR(tty) || !I_INPCK(tty)) &&
- + (tty->driver.flags & TTY_DRIVER_REAL_RAW))
- + tty->real_raw = 1;
- + else
- + tty->real_raw = 0;
- + }
- +}
- +
- +static void n_tty_close(struct tty_struct *tty)
- +{
- + n_tty_flush_buffer(tty);
- + if (tty->read_buf) {
- + kfree_s (tty->read_buf, N_TTY_BUF_SIZE);
- + tty->read_buf = 0;
- + }
- +}
- +
- +static int n_tty_open(struct tty_struct *tty)
- +{
- + if (!tty)
- + return -EINVAL;
- +
- + if (!tty->read_buf) {
- + tty->read_buf = (unsigned char *)
- + kmalloc (N_TTY_BUF_SIZE, intr_count ? GFP_ATOMIC : GFP_KERNEL);
- + if (!tty->read_buf)
- + return -ENOMEM;
- + }
- + memset(tty->read_buf, 0, N_TTY_BUF_SIZE);
- + tty->read_head = tty->read_tail = tty->read_cnt = 0;
- + tty->canon_head = tty->canon_data = tty->erasing = 0;
- + tty->column = 0;
- + memset(tty->read_flags, 0, sizeof(tty->read_flags));
- + n_tty_set_termios(tty, 0);
- + tty->minimum_to_wake = 1;
- + tty->closing = 0;
- + return 0;
- +}
- +
- +static inline int input_available_p(struct tty_struct *tty, int amt)
- +{
- + if (L_ICANON(tty)) {
- + if (tty->canon_data)
- + return 1;
- + } else if (tty->read_cnt >= (amt ? amt : 1))
- + return 1;
- +
- + return 0;
- +}
- +
- +/*
- + * Helper function to speed up read_chan. It is only called when
- + * ICANON is off; it copies characters straight from the tty queue to
- + * user space directly. It can be profitably called twice; once to
- + * drain the space from the tail pointer to the (physical) end of the
- + * buffer, and once to drain the space from the (physical) beginning of
- + * the buffer to head pointer.
- + */
- +static inline void copy_from_read_buf(struct tty_struct *tty,
- + unsigned char **b,
- + unsigned int *nr)
- +
- +{
- + int n;
- +
- + n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail));
- + if (!n)
- + return;
- + memcpy_tofs(*b, &tty->read_buf[tty->read_tail], n);
- + tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
- + tty->read_cnt -= n;
- + *b += n;
- + *nr -= n;
- +}
- +
- +static int read_chan(struct tty_struct *tty, struct file *file,
- + unsigned char *buf, unsigned int nr)
- +{
- + struct wait_queue wait = { current, NULL };
- + int c;
- + unsigned char *b = buf;
- + int minimum, time;
- + int retval = 0;
- + int size;
- +
- +do_it_again:
- +
- + if (!tty->read_buf) {
- + printk("n_tty_read_chan: called with read_buf == NULL?!?\n");
- + return -EIO;
- + }
- +
- + /* Job control check -- must be done at start and after
- + every sleep (POSIX.1 7.1.1.4). */
- + /* NOTE: not yet done after every sleep pending a thorough
- + check of the logic of this change. -- jlc */
- + /* don't stop on /dev/console */
- + if (file->f_inode->i_rdev != CONSOLE_DEV &&
- + current->tty == tty) {
- + if (tty->pgrp <= 0)
- + printk("read_chan: tty->pgrp <= 0!\n");
- + else if (current->pgrp != tty->pgrp) {
- + if (is_ignored(SIGTTIN) ||
- + is_orphaned_pgrp(current->pgrp))
- + return -EIO;
- + kill_pg(current->pgrp, SIGTTIN, 1);
- + return -ERESTARTSYS;
- + }
- + }
- +
- + if (L_ICANON(tty)) {
- + minimum = time = 0;
- + current->timeout = (unsigned long) -1;
- + } else {
- + time = (HZ / 10) * TIME_CHAR(tty);
- + minimum = MIN_CHAR(tty);
- + if (minimum) {
- + current->timeout = (unsigned long) -1;
- + if (time)
- + tty->minimum_to_wake = 1;
- + else if (!waitqueue_active(&tty->read_wait) ||
- + (tty->minimum_to_wake > minimum))
- + tty->minimum_to_wake = minimum;
- + } else {
- + if (time) {
- + current->timeout = time + jiffies;
- + time = 0;
- + } else
- + current->timeout = 0;
- + tty->minimum_to_wake = minimum = 1;
- + }
- + }
- +
- + add_wait_queue(&tty->read_wait, &wait);
- + while (1) {
- + /* First test for status change. */
- + if (tty->packet && tty->link->ctrl_status) {
- + if (b != buf)
- + break;
- + put_user(tty->link->ctrl_status, b++);
- + tty->link->ctrl_status = 0;
- + break;
- + }
- + /* This statement must be first before checking for input
- + so that any interrupt will set the state back to
- + TASK_RUNNING. */
- + current->state = TASK_INTERRUPTIBLE;
- +
- + if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
- + ((minimum - (b - buf)) >= 1))
- + tty->minimum_to_wake = (minimum - (b - buf));
- +
- + if (!input_available_p(tty, 0)) {
- + if (tty->flags & (1 << TTY_OTHER_CLOSED)) {
- + retval = -EIO;
- + break;
- + }
- + if (tty_hung_up_p(file))
- + break;
- + if (!current->timeout)
- + break;
- + if (file->f_flags & O_NONBLOCK) {
- + retval = -EAGAIN;
- + break;
- + }
- + if (current->signal & ~current->blocked) {
- + retval = -ERESTARTSYS;
- + break;
- + }
- + schedule();
- + continue;
- + }
- + current->state = TASK_RUNNING;
- +
- + /* Deal with packet mode. */
- + if (tty->packet && b == buf) {
- + put_user(TIOCPKT_DATA, b++);
- + nr--;
- + }
- +
- + if (L_ICANON(tty)) {
- + while (1) {
- + int eol;
- +
- + disable_bh(TQUEUE_BH);
- + if (!tty->read_cnt) {
- + enable_bh(TQUEUE_BH);
- + break;
- + }
- + eol = clear_bit(tty->read_tail,
- + &tty->read_flags);
- + c = tty->read_buf[tty->read_tail];
- + tty->read_tail = ((tty->read_tail+1) &
- + (N_TTY_BUF_SIZE-1));
- + tty->read_cnt--;
- + enable_bh(TQUEUE_BH);
- + if (!eol) {
- + put_user(c, b++);
- + if (--nr)
- + continue;
- + break;
- + }
- + if (--tty->canon_data < 0) {
- + tty->canon_data = 0;
- + }
- + if (c != __DISABLED_CHAR) {
- + put_user(c, b++);
- + nr--;
- + }
- + break;
- + }
- + } else {
- + disable_bh(TQUEUE_BH);
- + copy_from_read_buf(tty, &b, &nr);
- + copy_from_read_buf(tty, &b, &nr);
- + enable_bh(TQUEUE_BH);
- + }
- +
- + /* If there is enough space in the read buffer now, let the
- + low-level driver know. */
- + if (tty->driver.unthrottle &&
- + (tty->read_cnt <= TTY_THRESHOLD_UNTHROTTLE)
- + && clear_bit(TTY_THROTTLED, &tty->flags))
- + tty->driver.unthrottle(tty);
- +
- + if (b - buf >= minimum || !nr)
- + break;
- + if (time)
- + current->timeout = time + jiffies;
- + }
- + remove_wait_queue(&tty->read_wait, &wait);
- +
- + if (!waitqueue_active(&tty->read_wait))
- + tty->minimum_to_wake = minimum;
- +
- + current->state = TASK_RUNNING;
- + current->timeout = 0;
- + size = b - buf;
- + if (size && nr)
- + clear_bit(TTY_PUSH, &tty->flags);
- + if (!size && clear_bit(TTY_PUSH, &tty->flags))
- + goto do_it_again;
- + if (!size && !retval)
- + clear_bit(TTY_PUSH, &tty->flags);
- + return (size ? size : retval);
- +}
- +
- +static int write_chan(struct tty_struct * tty, struct file * file,
- + const unsigned char * buf, unsigned int nr)
- +{
- + struct wait_queue wait = { current, NULL };
- + int c;
- + const unsigned char *b = buf;
- + int retval = 0;
- +
- + /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
- + if (L_TOSTOP(tty) && file->f_inode->i_rdev != CONSOLE_DEV) {
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- + }
- +
- + add_wait_queue(&tty->write_wait, &wait);
- + while (1) {
- + current->state = TASK_INTERRUPTIBLE;
- + if (current->signal & ~current->blocked) {
- + retval = -ERESTARTSYS;
- + break;
- + }
- + if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
- + retval = -EIO;
- + break;
- + }
- + if (O_OPOST(tty)) {
- + while (nr > 0) {
- + c = get_user(b);
- + if (opost(c, tty) < 0)
- + break;
- + b++; nr--;
- + }
- + if (tty->driver.flush_chars)
- + tty->driver.flush_chars(tty);
- + } else {
- + c = tty->driver.write(tty, 1, b, nr);
- + b += c;
- + nr -= c;
- + }
- + if (!nr)
- + break;
- + if (file->f_flags & O_NONBLOCK) {
- + retval = -EAGAIN;
- + break;
- + }
- + schedule();
- + }
- + current->state = TASK_RUNNING;
- + remove_wait_queue(&tty->write_wait, &wait);
- + return (b - buf) ? b - buf : retval;
- +}
- +
- +static int normal_select(struct tty_struct * tty, struct inode * inode,
- + struct file * file, int sel_type, select_table *wait)
- +{
- + switch (sel_type) {
- + case SEL_IN:
- + if (input_available_p(tty, TIME_CHAR(tty) ? 0 :
- + MIN_CHAR(tty)))
- + return 1;
- + /* fall through */
- + case SEL_EX:
- + if (tty->packet && tty->link->ctrl_status)
- + return 1;
- + if (tty->flags & (1 << TTY_OTHER_CLOSED))
- + return 1;
- + if (tty_hung_up_p(file))
- + return 1;
- + if (!waitqueue_active(&tty->read_wait)) {
- + if (MIN_CHAR(tty) && !TIME_CHAR(tty))
- + tty->minimum_to_wake = MIN_CHAR(tty);
- + else
- + tty->minimum_to_wake = 1;
- + }
- + select_wait(&tty->read_wait, wait);
- + return 0;
- + case SEL_OUT:
- + if (tty->driver.chars_in_buffer(tty) < WAKEUP_CHARS)
- + return 1;
- + select_wait(&tty->write_wait, wait);
- + return 0;
- + }
- + return 0;
- +}
- +
- +struct tty_ldisc tty_ldisc_N_TTY = {
- + TTY_LDISC_MAGIC, /* magic */
- + 0, /* num */
- + 0, /* flags */
- + n_tty_open, /* open */
- + n_tty_close, /* close */
- + n_tty_flush_buffer, /* flush_buffer */
- + n_tty_chars_in_buffer, /* chars_in_buffer */
- + read_chan, /* read */
- + write_chan, /* write */
- + n_tty_ioctl, /* ioctl */
- + n_tty_set_termios, /* set_termios */
- + normal_select, /* select */
- + n_tty_receive_buf, /* receive_buf */
- + n_tty_receive_room, /* receive_room */
- + 0 /* write_wakeup */
- +};
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/pty.c linux/arch/arm/drivers/char/pty.c
- --- linux.orig/arch/arm/drivers/char/pty.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/pty.c Sun Jul 7 12:50:23 1996
- @@ -0,0 +1,324 @@
- +/*
- + * linux/arch/arm/drivers/char/pty.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + *
- + * Modifications for ARM processor Copyright (C) 1995, 1996 Russell King.
- + */
- +
- +/*
- + * pty.c
- + *
- + * This module exports the following pty function:
- + *
- + * int pty_open(struct tty_struct * tty, struct file * filp);
- + */
- +
- +#include <linux/errno.h>
- +#include <linux/sched.h>
- +#include <linux/interrupt.h>
- +#include <linux/tty.h>
- +#include <linux/tty_flip.h>
- +#include <linux/fcntl.h>
- +#include <linux/string.h>
- +#include <linux/major.h>
- +#include <linux/mm.h>
- +#include <linux/malloc.h>
- +
- +#include <asm/segment.h>
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +
- +struct pty_struct {
- + int magic;
- + struct wait_queue * open_wait;
- +};
- +
- +#define PTY_MAGIC 0x5001
- +
- +#define PTY_BUF_SIZE 4096/2
- +
- +/*
- + * tmp_buf is used as a temporary buffer by pty_write. We need to
- + * lock it in case the memcpy_fromfs blocks while swapping in a page,
- + * and some other program tries to do a pty write at the same time.
- + * Since the lock will only come under contention when the system is
- + * swapping and available memory is low, it makes sense to share one
- + * buffer across all the PTY's, since it significantly saves memory if
- + * large numbers of PTY's are open.
- + */
- +static unsigned char *tmp_buf;
- +static struct semaphore tmp_buf_sem = MUTEX;
- +
- +struct tty_driver pty_driver, pty_slave_driver;
- +struct tty_driver old_pty_driver, old_pty_slave_driver;
- +static int pty_refcount;
- +
- +static struct tty_struct *pty_table[NR_PTYS];
- +static struct termios *pty_termios[NR_PTYS];
- +static struct termios *pty_termios_locked[NR_PTYS];
- +static struct tty_struct *ttyp_table[NR_PTYS];
- +static struct termios *ttyp_termios[NR_PTYS];
- +static struct termios *ttyp_termios_locked[NR_PTYS];
- +static struct pty_struct pty_state[NR_PTYS];
- +
- +#define MIN(a,b) ((a) < (b) ? (a) : (b))
- +
- +static void pty_close(struct tty_struct * tty, struct file * filp)
- +{
- + if (!tty)
- + return;
- + if (tty->driver.subtype == PTY_TYPE_MASTER) {
- + if (tty->count > 1)
- + printk("master pty_close: count = %d!!\n", tty->count);
- + } else {
- + if (tty->count > 2)
- + return;
- + }
- + wake_up_interruptible(&tty->read_wait);
- + wake_up_interruptible(&tty->write_wait);
- + if (!tty->link)
- + return;
- + wake_up_interruptible(&tty->link->read_wait);
- + wake_up_interruptible(&tty->link->write_wait);
- + set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
- + if (tty->driver.subtype == PTY_TYPE_MASTER) {
- + tty_hangup(tty->link);
- + set_bit(TTY_OTHER_CLOSED, &tty->flags);
- + }
- +}
- +
- +/*
- + * The unthrottle routine is called by the line discipline to signal
- + * that it can receive more characters. For PTY's, the TTY_THROTTLED
- + * flag is always set, to force the line discipline to always call the
- + * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE
- + * characters in the queue. This is necessary since each time this
- + * happens, we need to wake up any sleeping processes that could be
- + * (1) trying to send data to the pty, or (2) waiting in wait_until_sent()
- + * for the pty buffer to be drained.
- + */
- +static void pty_unthrottle(struct tty_struct * tty)
- +{
- + struct tty_struct *o_tty = tty->link;
- +
- + if (!o_tty)
- + return;
- +
- + if ((o_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- + o_tty->ldisc.write_wakeup)
- + (o_tty->ldisc.write_wakeup)(o_tty);
- + wake_up_interruptible(&o_tty->write_wait);
- + set_bit(TTY_THROTTLED, &tty->flags);
- +}
- +
- +static int pty_write(struct tty_struct * tty, int from_user,
- + const unsigned char *buf, int count)
- +{
- + struct tty_struct *to = tty->link;
- + int c=0, n, r;
- + char *temp_buffer;
- +
- + if (!to || tty->stopped)
- + return 0;
- +
- + if (from_user) {
- + down(&tmp_buf_sem);
- + temp_buffer = tmp_buf +
- + ((tty->driver.subtype-1) * PTY_BUF_SIZE);
- + while (count > 0) {
- + n = MIN(count, PTY_BUF_SIZE);
- + memcpy_fromfs(temp_buffer, buf, n);
- + r = to->ldisc.receive_room(to);
- + if (r <= 0)
- + break;
- + n = MIN(n, r);
- + to->ldisc.receive_buf(to, temp_buffer, 0, n);
- + buf += n; c+= n;
- + count -= n;
- + }
- + up(&tmp_buf_sem);
- + } else {
- + c = MIN(count, to->ldisc.receive_room(to));
- + to->ldisc.receive_buf(to, buf, 0, c);
- + }
- +
- + return c;
- +}
- +
- +static int pty_write_room(struct tty_struct *tty)
- +{
- + struct tty_struct *to = tty->link;
- +
- + if (!to || tty->stopped)
- + return 0;
- +
- + return to->ldisc.receive_room(to);
- +}
- +
- +static int pty_chars_in_buffer(struct tty_struct *tty)
- +{
- + struct tty_struct *to = tty->link;
- +
- + if (!to || !to->ldisc.chars_in_buffer)
- + return 0;
- +
- + return to->ldisc.chars_in_buffer(to);
- +}
- +
- +static void pty_flush_buffer(struct tty_struct *tty)
- +{
- + struct tty_struct *to = tty->link;
- +
- + if (!to)
- + return;
- +
- + if (to->ldisc.flush_buffer)
- + to->ldisc.flush_buffer(to);
- +
- + if (to->packet) {
- + tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
- + wake_up_interruptible(&to->read_wait);
- + }
- +}
- +
- +int pty_open(struct tty_struct *tty, struct file * filp)
- +{
- +#if PTY_SLAVE_WAITS_ON_OPEN
- + struct wait_queue wait = { current, NULL };
- +#endif
- + int retval;
- + int line;
- + struct pty_struct *pty;
- +
- + if (!tty || !tty->link)
- + return -ENODEV;
- + line = MINOR(tty->device) - tty->driver.minor_start;
- + if ((line < 0) || (line >= NR_PTYS))
- + return -ENODEV;
- + pty = pty_state + line;
- + tty->driver_data = pty;
- +
- + if (!tmp_buf) {
- + unsigned long page = (unsigned long) kmalloc(PTY_BUF_SIZE*2,GFP_KERNEL);
- + if (!tmp_buf) {
- + if (!page)
- + return -ENOMEM;
- + tmp_buf = (unsigned char *) page;
- + } else
- + kfree((void *)page);
- + }
- +
- + clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
- + wake_up_interruptible(&pty->open_wait);
- + set_bit(TTY_THROTTLED, &tty->flags);
- + if (filp->f_flags & O_NDELAY)
- + return 0;
- + /*
- + * If we're opening the master pty, just return. If we're
- + * trying to open the slave pty, then we have to wait for the
- + * master pty to open.
- + */
- + if (tty->driver.subtype == PTY_TYPE_MASTER)
- + return 0;
- + retval = 0;
- +#if PTY_SLAVE_WAITS_ON_OPEN
- + add_wait_queue(&pty->open_wait, &wait);
- + while (1) {
- + if (current->signal & ~current->blocked) {
- + retval = -ERESTARTSYS;
- + break;
- + }
- + /*
- + * Block until the master is open...
- + */
- + current->state = TASK_INTERRUPTIBLE;
- + if (tty->link->count &&
- + !test_bit(TTY_OTHER_CLOSED, &tty->flags))
- + break;
- + schedule();
- + }
- + current->state = TASK_RUNNING;
- + remove_wait_queue(&pty->open_wait, &wait);
- +#else
- + if (!tty->link->count || test_bit(TTY_OTHER_CLOSED, &tty->flags))
- + retval = -EPERM;
- +#endif
- + return retval;
- +}
- +
- +static void pty_set_termios(struct tty_struct *tty, struct termios *old_termios)
- +{
- + tty->termios->c_cflag &= ~(CSIZE | PARENB);
- + tty->termios->c_cflag |= (CS8 | CREAD);
- +}
- +
- +int pty_init(void)
- +{
- + memset(&pty_state, 0, sizeof(pty_state));
- + memset(&pty_driver, 0, sizeof(struct tty_driver));
- + pty_driver.magic = TTY_DRIVER_MAGIC;
- + pty_driver.name = "pty";
- + pty_driver.major = PTY_MASTER_MAJOR;
- + pty_driver.minor_start = 0;
- + pty_driver.num = NR_PTYS;
- + pty_driver.type = TTY_DRIVER_TYPE_PTY;
- + pty_driver.subtype = PTY_TYPE_MASTER;
- + pty_driver.init_termios = tty_std_termios;
- + pty_driver.init_termios.c_iflag = 0;
- + pty_driver.init_termios.c_oflag = 0;
- + pty_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
- + pty_driver.init_termios.c_lflag = 0;
- + pty_driver.flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW;
- + pty_driver.refcount = &pty_refcount;
- + pty_driver.table = pty_table;
- + pty_driver.termios = pty_termios;
- + pty_driver.termios_locked = pty_termios_locked;
- + pty_driver.other = &pty_slave_driver;
- +
- + pty_driver.open = pty_open;
- + pty_driver.close = pty_close;
- + pty_driver.write = pty_write;
- + pty_driver.write_room = pty_write_room;
- + pty_driver.flush_buffer = pty_flush_buffer;
- + pty_driver.chars_in_buffer = pty_chars_in_buffer;
- + pty_driver.unthrottle = pty_unthrottle;
- + pty_driver.set_termios = pty_set_termios;
- +
- + pty_slave_driver = pty_driver;
- + pty_slave_driver.name = "ttyp";
- + pty_slave_driver.subtype = PTY_TYPE_SLAVE;
- + pty_slave_driver.major = PTY_SLAVE_MAJOR;
- + pty_slave_driver.minor_start = 0;
- + pty_slave_driver.init_termios = tty_std_termios;
- + pty_slave_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
- + pty_slave_driver.table = ttyp_table;
- + pty_slave_driver.termios = ttyp_termios;
- + pty_slave_driver.termios_locked = ttyp_termios_locked;
- + pty_slave_driver.other = &pty_driver;
- +
- + old_pty_driver = pty_driver;
- + old_pty_driver.major = TTY_MAJOR;
- + old_pty_driver.minor_start = 128;
- + old_pty_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS;
- + old_pty_driver.other = &old_pty_slave_driver;
- +
- + old_pty_slave_driver = pty_slave_driver;
- + old_pty_slave_driver.major = TTY_MAJOR;
- + old_pty_slave_driver.minor_start = 192;
- + old_pty_slave_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS;
- + old_pty_slave_driver.other = &old_pty_driver;
- +
- + tmp_buf = 0;
- +
- + if (tty_register_driver(&pty_driver))
- + panic("Couldn't register pty driver");
- + if (tty_register_driver(&pty_slave_driver))
- + panic("Couldn't register pty slave driver");
- + if (tty_register_driver(&old_pty_driver))
- + panic("Couldn't register compat pty driver");
- + if (tty_register_driver(&old_pty_slave_driver))
- + panic("Couldn't register compat pty slave driver");
- +
- + return 0;
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/selection.c linux/arch/arm/drivers/char/selection.c
- --- linux.orig/arch/arm/drivers/char/selection.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/selection.c Sun Jun 23 11:38:41 1996
- @@ -0,0 +1,298 @@
- +/*
- + * linux/arch/arm/drivers/char/console/selection.c
- + *
- + * This module exports the functions:
- + *
- + * 'int set_selection (const unsigned long arg)'
- + * 'void clear_selection (void)'
- + * 'int paste_selection (struct tty_struct *tty)'
- + * 'int sel_loadlut (const unsigned long arg)'
- + *
- + * Now that /dev/vcs exists, most of this can disappear again.
- + */
- +
- +#include <linux/tty.h>
- +#include <linux/sched.h>
- +#include <linux/mm.h>
- +#include <linux/malloc.h>
- +#include <linux/types.h>
- +
- +#include <asm/segment.h>
- +
- +#include "vt_kern.h"
- +#include "consolemap.h"
- +#include "selection.h"
- +
- +#ifndef MIN
- +#define MIN(a,b) ((a) < (b) ? (a) : (b))
- +#endif
- +
- +/* Don't take this from <ctype.h>: 011-015 in the buffer aren't spaces */
- +#define isspace(c) ((c) == ' ')
- +
- +#define sel_pos(n) inverse_translate(scrw2glyph(screen_word(vtdata.select.vt, n)))
- +
- +/*
- + * clear_selection, highlight and highlight_pointer can be called
- + * from interrupt (via scrollback/front)
- + */
- +
- +/*
- + * set reverse video on characters s-e of console with selection.
- + */
- +static inline void highlight (const int s, const int e)
- +{
- + invert_screen (vtdata.select.vt, s, e - s);
- +}
- +
- +/*
- + * use complementary color to show the pointer
- + */
- +static inline void highlight_pointer (const int where)
- +{
- + complement_pos (vtdata.select.vt, where);
- +}
- +
- +/*
- + * Remove the current selection highlight, if any,
- + * from the console holding selection.
- + */
- +void clear_selection (void)
- +{
- + highlight_pointer (-1); /* hide the pointer */
- + if (vtdata.select.start != -1) {
- + highlight (vtdata.select.start, vtdata.select.end);
- + vtdata.select.start = -1;
- + }
- +}
- +
- +/*
- + * User settable table: what characters are to be considered alphabetic?
- + * 256 bits
- + */
- +static u32 inwordLut[8]={
- + 0x00000000, /* control chars */
- + 0x03FF0000, /* digits */
- + 0x87FFFFFE, /* uppercase and '_' */
- + 0x07FFFFFE, /* lowercase */
- + 0x00000000,
- + 0x00000000,
- + 0xFF7FFFFF, /* latin-1 accented letters, not multiplication sign */
- + 0xFF7FFFFF /* latin-1 accented letters, not division sign */
- +};
- +
- +static inline int inword (const unsigned char c)
- +{
- + return (inwordLut[c>>5] >> (c & 31)) & 1;
- +}
- +
- +/*
- + * set inwordLut contents. Invoked by ioctl().
- + */
- +int sel_loadlut (const unsigned long arg)
- +{
- + int i = verify_area (VERIFY_READ, (char *) arg, 36);
- + if (i)
- + return i;
- + memcpy_fromfs (inwordLut, (u32 *)(arg + 4), 32);
- + return 0;
- +}
- +
- +/*
- + * does buffer offset p correspond to character at LH/RH edge of screen?
- + */
- +static inline int atedge (const int p)
- +{
- + return (!(p % vtdata.numcolumns) || !((p + 1) % vtdata.numcolumns));
- +}
- +
- +/*
- + * constrain v such that v <= u
- + */
- +static inline int limit (const int v, const int u)
- +{
- + return ((v > u) ? u : v);
- +}
- +
- +/*
- + * set the current selection. Invoked by ioctl().
- + */
- +int set_selection (const unsigned long arg, struct tty_struct *tty)
- +{
- + struct vt * vt = vtdata.fgconsole;
- + int sel_mode, new_sel_start, new_sel_end, spc;
- + char *bp, *obp;
- + int i, ps, pe;
- +
- + vt_do_unblankscreen ();
- +
- + {
- + unsigned short *args, xs, ys, xe, ye;
- +
- + args = (unsigned short *)(arg + 1);
- + xs = get_user (args ++) - 1;
- + ys = get_user (args ++) - 1;
- + xe = get_user (args ++) - 1;
- + ye = get_user (args ++) - 1;
- + sel_mode = get_user (args);
- +
- + xs = limit (xs, vtdata.numcolumns - 1);
- + ys = limit (ys, vtdata.numrows - 1);
- + xe = limit (xe, vtdata.numcolumns - 1);
- + ye = limit (ye, vtdata.numrows - 1);
- + ps = ys * vtdata.numcolumns + xs;
- + pe = ye * vtdata.numcolumns + xe;
- +
- + if (sel_mode == 4) {
- + /* useful for screendump without selection highlights */
- + clear_selection ();
- + return 0;
- + }
- +
- + if (vt->vcd->report_mouse && sel_mode & 16) {
- + mouse_report (tty, sel_mode & 15, xs, ys);
- + return 0;
- + }
- + }
- +
- + if (ps > pe) { /* make sel_start <= sel_end */
- + ps ^= pe;
- + pe ^= ps;
- + ps ^= pe;
- + }
- +
- + if (vt != vtdata.select.vt) {
- + clear_selection ();
- + vtdata.select.vt = vt;
- + }
- +
- + switch (sel_mode) {
- + case 0: /* character-by-character selection */
- + new_sel_start = ps;
- + new_sel_end = pe;
- + break;
- + case 1: /* word-by-word selection */
- + spc = isspace (sel_pos (ps));
- + for (new_sel_start = ps; ; ps --) {
- + if (( spc && !isspace (sel_pos (ps))) ||
- + (!spc && !inword (sel_pos (ps))))
- + break;
- + new_sel_start = ps;
- + if (!(ps % vtdata.numcolumns))
- + break;
- + }
- + spc = isspace (sel_pos (pe));
- + for (new_sel_end = pe; ; pe ++) {
- + if (( spc && !isspace (sel_pos (pe))) ||
- + (!spc && !inword (sel_pos (pe))))
- + break;
- + new_sel_end = pe;
- + if (!((pe + 1) % vtdata.numcolumns))
- + break;
- + }
- + break;
- + case 2: /* line-by-line selection */
- + new_sel_start = ps - ps % vtdata.numcolumns;
- + new_sel_end = pe + vtdata.numcolumns - pe % vtdata.numcolumns - 1;
- + break;
- + case 3:
- + highlight_pointer (pe);
- + return 0;
- + default:
- + return -EINVAL;
- + }
- +
- + /* remove the pointer */
- + highlight_pointer (-1);
- +
- + /* select to end of line if on trailing space */
- + if (new_sel_end > new_sel_start && !atedge(new_sel_end) && isspace(sel_pos(new_sel_end))) {
- + for (pe = new_sel_end + 1; ; pe ++)
- + if (!isspace (sel_pos (pe)) || atedge (pe))
- + break;
- + if (isspace (sel_pos (pe)))
- + new_sel_end = pe;
- + }
- + if (vtdata.select.start == -1) /* no current selection */
- + highlight (new_sel_start, new_sel_end);
- + else if (new_sel_start == vtdata.select.start) {
- + if (new_sel_end == vtdata.select.end) /* no action required */
- + return 0;
- + else if (new_sel_end > vtdata.select.end) /* extend to right */
- + highlight (vtdata.select.end + 1, new_sel_end);
- + else /* contract from right */
- + highlight (new_sel_end + 1, vtdata.select.end);
- + } else if (new_sel_end == vtdata.select.end) {
- + if (new_sel_start < vtdata.select.start) /* extend to left */
- + highlight (new_sel_start, vtdata.select.start - 1);
- + else /* contract from left */
- + highlight (vtdata.select.start, new_sel_start - 1);
- + } else { /* some other case; start selection from scratch */
- + clear_selection ();
- + highlight (new_sel_start, new_sel_end);
- + }
- + vtdata.select.start = new_sel_start;
- + vtdata.select.end = new_sel_end;
- +
- + if (vtdata.select.buffer)
- + kfree (vtdata.select.buffer);
- + vtdata.select.buffer = kmalloc (vtdata.select.end - vtdata.select.start + 1, GFP_KERNEL);
- + if (!vtdata.select.buffer) {
- + printk ("selection: kmalloc() failed\n");
- + clear_selection ();
- + return -ENOMEM;
- + }
- +
- + obp = bp = vtdata.select.buffer;
- + for (i = vtdata.select.start; i <= vtdata.select.end; i++) {
- + *bp = sel_pos (i);
- + if (!isspace (*bp++))
- + obp = bp;
- + if (!((i + 1) % vtdata.numcolumns)) {
- + /* strip trailing blanks from line and add newline,
- + * unless non-space at end of line.
- + */
- + if (obp != bp) {
- + bp = obp;
- + *bp++ = '\r';
- + }
- + obp = bp;
- + }
- + }
- + vtdata.select.length = bp - vtdata.select.buffer;
- + return 0;
- +}
- +
- +/* Insert the contents of the selection buffer into the queue of the
- + * tty associated with the current console. Invoked by ioctl().
- + */
- +int paste_selection (struct tty_struct *tty)
- +{
- + struct wait_queue wait = { current, NULL };
- + struct vt_struct *vt = ((struct vt *)tty->driver_data)->vtd;
- + char *bp = vtdata.select.buffer;
- + int c = vtdata.select.length;
- + int l;
- +
- + if (!bp || !c)
- + return 0;
- +
- + vt_do_unblankscreen ();
- +
- + current->state = TASK_INTERRUPTIBLE;
- + add_wait_queue (&vt->paste_wait, &wait);
- +
- + while (c) {
- + if (test_bit(TTY_THROTTLED, &tty->flags)) {
- + schedule();
- + continue;
- + }
- + l = MIN(c, tty->ldisc.receive_room(tty));
- + tty->ldisc.receive_buf(tty, bp, 0, l);
- + c -= l;
- + bp += l;
- + }
- + current->state = TASK_RUNNING;
- + return 0;
- +}
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/selection.h linux/arch/arm/drivers/char/selection.h
- --- linux.orig/arch/arm/drivers/char/selection.h Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/selection.h Fri May 10 21:09:16 1996
- @@ -0,0 +1,22 @@
- +/*
- + * selection.h
- + *
- + * Interface between console.c, tty_io.c, vt.c, vc_screen.c and selection.c
- + */
- +
- +extern void clear_selection (void);
- +extern int set_selection (const unsigned long arg, struct tty_struct *tty);
- +extern int paste_selection (struct tty_struct *tty);
- +extern int sel_loadlut (const unsigned long arg);
- +extern int mouse_reporting (void);
- +extern void mouse_report (struct tty_struct *tty, int butt, int mrx, int mry);
- +
- +extern void vt_do_unblankscreen (void);
- +extern unsigned long *screen_pos (const struct vt *const vt, int offset);
- +extern unsigned long screen_word (const struct vt *const vt, int offset);
- +extern int scrw2glyph (unsigned long scr_word);
- +extern void invert_screen (const struct vt *const vt, int offset, int count);
- +
- +extern void getconsxy (const struct vt *const vt, char *p);
- +extern void putconsxy (const struct vt *const vt, char *p);
- +extern void update_scrmem (const struct vt *const vt, int offset, int length);
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/serial-atomwide.c linux/arch/arm/drivers/char/serial-atomwide.c
- --- linux.orig/arch/arm/drivers/char/serial-atomwide.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/serial-atomwide.c Tue Jul 30 17:49:18 1996
- @@ -0,0 +1,140 @@
- +/*
- + * linux/arch/arm/drivers/char/serial-atomwide.c
- + *
- + * Copyright (c) 1996 Russell King.
- + *
- + * Changelog:
- + * 02-05-1996 RMK Created
- + * 07-05-1996 RMK Altered for greater number of cards.
- + * 30-07-1996 RMK Now uses generic card code.
- + */
- +
- +#if 0
- +#include <linux/module.h>
- +#include <linux/serial.h>
- +#include <linux/errno.h>
- +
- +#include <asm/ecard.h>
- +
- +#define NUM_SERIALS 3 * MAX_ECARDS
- +
- +static const int serial_prods[] = { 0x0090 };
- +static const int serial_manus[] = { 0x0017 };
- +
- +#ifndef MODULE
- +
- +int atomwide_serial_init (void)
- +{
- + struct expansion_card *ec;
- + struct serial_struct req;
- + unsigned long port;
- + int prt, i;
- +
- + req.baud_base = 7372800 / 16;
- +
- + while (1) {
- +
- + if ((ec = ecard_find (0, sizeof (serial_prods) / sizeof (int), serial_prods, serial_manus))
- + == NULL)
- + break;
- +
- + port = ((unsigned long)ecard_address (ec->slot_no, ECARD_IOC, ECARD_SLOW) + 0x2000) >> 2;
- +
- + for (prt = 0; prt < 3; prt ++) {
- + req.irq = ec->irq;
- + req.port = port + 0x200 - prt * 0x100;
- +
- + i = register_pre_init_serial (&req);
- + if (i < 0)
- + break;
- + }
- +
- + if (prt)
- + ecard_claim (ec);
- +
- + if (i < 0)
- + break;
- + }
- +
- + return 0;
- +}
- +
- +#else
- +
- +static int atomwide_serial_lines[NUM_SERIALS];
- +static int atomwide_num_lines;
- +static struct expansion_card *ec[MAX_ECARDS];
- +
- +static inline void atomwide_serial_register (int slot_no, int irq_no)
- +{
- + struct serial_struct req;
- + unsigned long port;
- + int line, prt;
- +
- + port = ((unsigned long)ecard_address (slot_no, ECARD_IOC, ECARD_SLOW) + 0x2000) >> 2;
- +
- + req.baud_base = 7372800 / 16;
- +
- + for (prt = 0; prt < 3; prt ++) {
- + req.irq = irq_no;
- + req.port = port + 0x200 - prt * 0x100;
- +
- + line = register_serial (&req);
- + if (line >= 0)
- + atomwide_serial_lines[atomwide_num_lines++] = line;
- + }
- +}
- +
- +int init_module (void)
- +{
- + int i;
- +
- + for (i = 0; i < NUM_SERIALS; i ++)
- + atomwide_serial_lines[i] = -1;
- +
- + atomwide_num_lines = 0;
- +
- + for (i = 0; i < MAX_ECARDS; i ++)
- + ec[i] = NULL;
- +
- + i = 0;
- +
- + do {
- + if ((ec[i] = ecard_find (0, sizeof (serial_prods) / sizeof (int), serial_prods, serial_manus))
- + == NULL)
- + break;
- +
- + ecard_claim (ec[i]);
- + atomwide_serial_register (ec[i]->slot_no, ec[i]->irq);
- + i++;
- + } while (i < MAX_ECARDS);
- +
- + return i ? 0 : -ENODEV;
- +}
- +
- +void cleanup_module (void)
- +{
- + int i;
- + for (i = 0; i < atomwide_num_lines; i++)
- + unregister_serial (atomwide_serial_lines[i]);
- +
- + for (i = 0; i < MAX_ECARDS; i++)
- + if (ec[i]) {
- + ecard_release (ec[i]);
- + ec[i] = NULL;
- + }
- +}
- +#endif
- +
- +#else
- +#define MY_PRODS 0x0090
- +#define MY_MANUS 0x0017
- +#define MY_NUMPORTS 3
- +#define MY_BAUD_BASE (7372800 / 16)
- +#define MY_INIT atomwide_serial_init
- +#define MY_BASE_ADDRESS(ec) \
- + ((unsigned long)ecard_address ((ec)->slot_no, ECARD_IOC, ECARD_SLOW) + 0x2000) >> 2
- +#define MY_PORT_ADDRESS(port,cardaddr) \
- + ((cardaddr) + 0x200 - (port) * 0x100)
- +#include "serial-card.c"
- +#endif
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/serial-card.c linux/arch/arm/drivers/char/serial-card.c
- --- linux.orig/arch/arm/drivers/char/serial-card.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/serial-card.c Tue Jul 30 17:05:14 1996
- @@ -0,0 +1,108 @@
- +/*
- + * linux/arch/arm/drivers/char/serial-module.c
- + *
- + * Copyright (c) 1996 Russell King.
- + *
- + * A generic handler of serial expansion cards that use 16550s or
- + * the like.
- + *
- + * Definitions:
- + * MY_PRODS Product numbers to identify this card by
- + * MY_MANUS Manufacturer numbers to identify this card by
- + * MY_NUMPORTS Number of ports per card
- + * MY_BAUD_BASE Baud base for the card
- + * MY_INIT Initialisation routine name
- + * MY_BASE_ADDRESS(ec) Return base address for ports
- + * MY_PORT_ADDRESS
- + * (port,cardaddr) Return address for port using base address
- + * from above.
- + *
- + * Changelog:
- + * 30-07-1996 RMK Created
- + */
- +#include <linux/module.h>
- +#include <linux/serial.h>
- +#include <linux/errno.h>
- +#include <asm/ecard.h>
- +
- +#ifndef NUM_SERIALS
- +#define NUM_SERIALS MY_NUMPORTS * MAX_ECARDS
- +#endif
- +
- +#ifdef MODULE
- +static int __serial_ports[NUM_SERIALS];
- +static int __serial_pcount;
- +static struct expansion_card *expcard[MAX_ECARDS];
- +#define ADD_ECARD(ec,card) expcard[(card)] = (ec)
- +#define ADD_PORT(port) __serial_ports[__serial_pcount++] = (port)
- +#undef MY_INIT
- +#define MY_INIT init_module
- +#else
- +#define ADD_ECARD(ec,card)
- +#define ADD_PORT(port)
- +#endif
- +
- +static const int serial_prods[] = { MY_PRODS };
- +static const int serial_manus[] = { MY_MANUS };
- +
- +static inline int serial_register_onedev (int at_boot, unsigned long port, int irq)
- +{
- + struct serial_struct req;
- + int line;
- +
- + req.baud_base = MY_BAUD_BASE;
- + req.irq = irq;
- + req.port = port;
- +
- + if (at_boot)
- + line = register_pre_init_serial (&req);
- + else
- + line = register_serial (&req);
- + return line;
- +}
- +
- +int MY_INIT (void)
- +{
- + int card = 0;
- + do {
- + struct expansion_card *ec;
- + unsigned long cardaddr;
- + int port;
- +
- + ec = ecard_find (0, sizeof (serial_prods) / sizeof (int), serial_prods, serial_manus);
- + if (!ec)
- + break;
- +
- + cardaddr = MY_BASE_ADDRESS(ec);
- +
- + for (port = 0; port < MY_NUMPORTS; port ++) {
- + int line;
- +
- + line = serial_register_onedev (1, MY_PORT_ADDRESS(port, cardaddr), ec->irq);
- + if (line < 0)
- + break;
- + ADD_PORT(line);
- + }
- +
- + if (port) {
- + ecard_claim (ec);
- + ADD_ECARD(ec, card);
- + } else
- + break;
- + } while (++card < MAX_ECARDS);
- + return card ? 0 : -ENODEV;
- +}
- +
- +#ifdef MODULE
- +void cleanup_module (void)
- +{
- + int i;
- +
- + for (i = 0; i < __serial_pcount; i++)
- + unregister_serial (__serial_port[i]);
- +
- + for (i = 0; i < MAX_ECARDS; i++)
- + if (expcard[i])
- + ecard_release (expcard[i]);
- +}
- +#endif
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/serial-dualsp.c linux/arch/arm/drivers/char/serial-dualsp.c
- --- linux.orig/arch/arm/drivers/char/serial-dualsp.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/serial-dualsp.c Tue Jul 30 17:04:37 1996
- @@ -0,0 +1,19 @@
- +/*
- + * linux/arch/arm/drivers/char/serial-dualsp.c
- + *
- + * Copyright (c) 1996 Russell King.
- + *
- + * Changelog:
- + * 30-07-1996 RMK Created
- + */
- +#define MY_PRODS 0x00B9
- +#define MY_MANUS 0x003F
- +#define MY_NUMPORTS 2
- +#define MY_BAUD_BASE (3686400 / 16)
- +#define MY_INIT dualsp_serial_init
- +#define MY_BASE_ADDRESS(ec) \
- + ((unsigned long)ecard_address (ec->slot_no, ECARD_IOC, ECARD_SLOW) + 0x2000) >> 2
- +#define MY_PORT_ADDRESS(port,cardaddress) \
- + ((cardaddress) + (port) * 8)
- +#include "serial-card.c"
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/serial.c linux/arch/arm/drivers/char/serial.c
- --- linux.orig/arch/arm/drivers/char/serial.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/serial.c Tue Jul 30 17:07:42 1996
- @@ -0,0 +1,2955 @@
- +/*
- + * linux/arch/arm/drivers/char/serial.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + *
- + * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now
- + * much more extensible to support other serial cards based on the
- + * 16450/16550A UART's. Added support for the AST FourPort and the
- + * Accent Async board.
- + *
- + * set_serial_info fixed to set the flags, custom divisor, and uart
- + * type fields. Fix suggested by Michael K. Johnson 12/12/92.
- + *
- + * 11/95: TIOCMIWAIT, TIOCGICOUNT by Angelo Haritsis <ah@doc.ic.ac.uk>
- + *
- + * 03/96: Modularised by Angelo Haritsis <ah@doc.ic.ac.uk>
- + *
- + * rs_set_termios fixed to look also for changes of the input
- + * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
- + * Bernd Anhäupl 05/17/96.
- + *
- + * This module exports the following rs232 io functions:
- + *
- + * int rs_init(void);
- + * int rs_open(struct tty_struct * tty, struct file * filp)
- + *
- + * Slight modifications for ARM Copyright (C) 1995, 1996 Russell King
- + */
- +
- +#include <linux/module.h>
- +#include <linux/errno.h>
- +#include <linux/signal.h>
- +#include <linux/sched.h>
- +#include <linux/timer.h>
- +#include <linux/interrupt.h>
- +#include <linux/tty.h>
- +#include <linux/tty_flip.h>
- +#include <linux/serial.h>
- +#include <linux/serial_reg.h>
- +#include <linux/config.h>
- +#include <linux/major.h>
- +#include <linux/string.h>
- +#include <linux/fcntl.h>
- +#include <linux/ptrace.h>
- +#include <linux/ioport.h>
- +#include <linux/mm.h>
- +
- +#include <asm/system.h>
- +#include <asm/io.h>
- +#include <asm/segment.h>
- +#include <asm/bitops.h>
- +
- +static char *serial_name = "Serial driver";
- +static char *serial_version = "4.13";
- +
- +DECLARE_TASK_QUEUE(tq_serial);
- +
- +struct tty_driver serial_driver, callout_driver;
- +static int serial_refcount;
- +
- +/* serial subtype definitions */
- +#define SERIAL_TYPE_NORMAL 1
- +#define SERIAL_TYPE_CALLOUT 2
- +
- +/* number of characters left in xmit buffer before we ask for more */
- +#define WAKEUP_CHARS 256
- +
- +/*
- + * Serial driver configuration section. Here are the various options:
- + *
- + * CONFIG_HUB6
- + * Enables support for the venerable Bell Technologies
- + * HUB6 card.
- + *
- + * SERIAL_PARANOIA_CHECK
- + * Check the magic number for the async_structure where
- + * ever possible.
- + */
- +
- +#define SERIAL_PARANOIA_CHECK
- +#define CONFIG_SERIAL_NOPAUSE_IO
- +#define SERIAL_DO_RESTART
- +
- +#undef SERIAL_DEBUG_INTR
- +#undef SERIAL_DEBUG_OPEN
- +#undef SERIAL_DEBUG_FLOW
- +
- +#define RS_STROBE_TIME (10*HZ)
- +#define RS_ISR_PASS_LIMIT 256
- +
- +#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
- +
- +#define _INLINE_ inline
- +
- +#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
- +#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
- + kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s)
- +#else
- +#define DBG_CNT(s)
- +#endif
- +
- +/*
- + * IRQ_timeout - How long the timeout should be for each IRQ
- + * should be after the IRQ has been active.
- + */
- +
- +static struct async_struct *IRQ_ports[32];
- +static struct rs_multiport_struct rs_multiport[32];
- +static int IRQ_timeout[32];
- +static volatile int rs_irq_triggered;
- +static volatile int rs_triggered;
- +static int rs_wild_int_mask;
- +
- +static void autoconfig(struct async_struct * info);
- +static void change_speed(struct async_struct *info);
- +
- +/*
- + * This assumes you have a 1.8432 MHz clock for your UART.
- + *
- + * It'd be nice if someone built a serial card with a 24.576 MHz
- + * clock, since the 16550A is capable of handling a top speed of 1.5
- + * megabits/second; but this requires the faster clock.
- + */
- +#define BASE_BAUD ( 1843200 / 16 )
- +
- +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
- +
- +struct async_struct rs_table[] = {
- + /* UART CLK PORT IRQ FLAGS */
- +#ifndef CONFIG_ARCH_ARC
- + { 0, BASE_BAUD, 0x3F8, 10, STD_COM_FLAGS }, /* ttyS0 */
- + { 0, BASE_BAUD, 0x2F8, 10, STD_COM_FLAGS }, /* ttyS1 */
- +#else
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS2 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS3 */
- +#endif
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS2 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS3 */
- +
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS4 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS5 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS6 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS7 */
- +
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS8 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS9 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS10 */
- + { 0, BASE_BAUD, 0 , 0, STD_COM_FLAGS }, /* ttyS11 */
- +
- +};
- +
- +#define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct))
- +
- +static struct tty_struct *serial_table[NR_PORTS];
- +static struct termios *serial_termios[NR_PORTS];
- +static struct termios *serial_termios_locked[NR_PORTS];
- +
- +#ifndef MIN
- +#define MIN(a,b) ((a) < (b) ? (a) : (b))
- +#endif
- +
- +/*
- + * tmp_buf is used as a temporary buffer by serial_write. We need to
- + * lock it in case the memcpy_fromfs blocks while swapping in a page,
- + * and some other program tries to do a serial write at the same time.
- + * Since the lock will only come under contention when the system is
- + * swapping and available memory is low, it makes sense to share one
- + * buffer across all the serial ports, since it significantly saves
- + * memory if large numbers of serial ports are open.
- + */
- +static unsigned char *tmp_buf;
- +static struct semaphore tmp_buf_sem = MUTEX;
- +
- +static inline int serial_paranoia_check(struct async_struct *info,
- + kdev_t device, const char *routine)
- +{
- +#ifdef SERIAL_PARANOIA_CHECK
- + static const char *badmagic =
- + "Warning: bad magic number for serial struct (%s) in %s\n";
- + static const char *badinfo =
- + "Warning: null async_struct for (%s) in %s\n";
- +
- + if (!info) {
- + printk(badinfo, kdevname(device), routine);
- + return 1;
- + }
- + if (info->magic != SERIAL_MAGIC) {
- + printk(badmagic, kdevname(device), routine);
- + return 1;
- + }
- +#endif
- + return 0;
- +}
- +
- +/*
- + * This is used to figure out the divisor speeds and the timeouts
- + */
- +static int baud_table[] = {
- + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- + 9600, 19200, 38400, 57600, 115200, 0 };
- +
- +static inline unsigned int serial_in(struct async_struct *info, int offset)
- +{
- +#ifdef CONFIG_HUB6
- + if (info->hub6) {
- + outb(info->hub6 - 1 + offset, info->port);
- + return inb(info->port+1);
- + } else
- +#endif
- + return inb(info->port + offset);
- +}
- +
- +static inline unsigned int serial_inp(struct async_struct *info, int offset)
- +{
- +#ifdef CONFIG_HUB6
- + if (info->hub6) {
- + outb(info->hub6 - 1 + offset, info->port);
- + return inb_p(info->port+1);
- + } else
- +#endif
- +#ifdef CONFIG_SERIAL_NOPAUSE_IO
- + return inb(info->port + offset);
- +#else
- + return inb_p(info->port + offset);
- +#endif
- +}
- +
- +static inline void serial_out(struct async_struct *info, int offset, int value)
- +{
- +#ifdef CONFIG_HUB6
- + if (info->hub6) {
- + outb(info->hub6 - 1 + offset, info->port);
- + outb(value, info->port+1);
- + } else
- +#endif
- + outb(value, info->port+offset);
- +}
- +
- +static inline void serial_outp(struct async_struct *info, int offset,
- + int value)
- +{
- +#ifdef CONFIG_HUB6
- + if (info->hub6) {
- + outb(info->hub6 - 1 + offset, info->port);
- + outb_p(value, info->port+1);
- + } else
- +#endif
- +#ifdef CONFIG_SERIAL_NOPAUSE_IO
- + outb(value, info->port+offset);
- +#else
- + outb_p(value, info->port+offset);
- +#endif
- +}
- +
- +/*
- + * ------------------------------------------------------------
- + * rs_stop() and rs_start()
- + *
- + * This routines are called before setting or resetting tty->stopped.
- + * They enable or disable transmitter interrupts, as necessary.
- + * ------------------------------------------------------------
- + */
- +static void rs_stop(struct tty_struct *tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- + unsigned long flags;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_stop"))
- + return;
- +
- + save_flags_cli (flags);
- + if (info->IER & UART_IER_THRI) {
- + info->IER &= ~UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + }
- + restore_flags(flags);
- +}
- +
- +static void rs_start(struct tty_struct *tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- + unsigned long flags;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_start"))
- + return;
- +
- + save_flags_cli (flags);
- + if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) {
- + info->IER |= UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + }
- + restore_flags(flags);
- +}
- +
- +/*
- + * ----------------------------------------------------------------------
- + *
- + * Here starts the interrupt handling routines. All of the following
- + * subroutines are declared as inline and are folded into
- + * rs_interrupt(). They were separated out for readability's sake.
- + *
- + * Note: rs_interrupt() is a "fast" interrupt, which means that it
- + * runs with interrupts turned off. People who may want to modify
- + * rs_interrupt() should try to keep the interrupt handler as fast as
- + * possible. After you are done making modifications, it is not a bad
- + * idea to do:
- + *
- + * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
- + *
- + * and look at the resulting assemble code in serial.s.
- + *
- + * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
- + * -----------------------------------------------------------------------
- + */
- +
- +/*
- + * This is the serial driver's interrupt routine while we are probing
- + * for submarines.
- + */
- +static void rs_probe(int irq, void *dev_id, struct pt_regs * regs)
- +{
- + rs_irq_triggered = irq;
- + rs_triggered |= 1 << irq;
- + return;
- +}
- +
- +/*
- + * This routine is used by the interrupt handler to schedule
- + * processing in the software interrupt portion of the driver.
- + */
- +static _INLINE_ void rs_sched_event(struct async_struct *info,
- + int event)
- +{
- + info->event |= 1 << event;
- + queue_task_irq_off(&info->tqueue, &tq_serial);
- + mark_bh(SERIAL_BH);
- +}
- +
- +static _INLINE_ void receive_chars(struct async_struct *info,
- + int *status)
- +{
- + struct tty_struct *tty = info->tty;
- + unsigned char ch;
- + int ignored = 0;
- +
- + do {
- + ch = serial_inp(info, UART_RX);
- + if (*status & info->ignore_status_mask) {
- + if (++ignored > 100)
- + break;
- + goto ignore_char;
- + }
- + if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- + break;
- + tty->flip.count++;
- + if (*status & (UART_LSR_BI)) {
- +#ifdef SERIAL_DEBUG_INTR
- + printk("handling break....");
- +#endif
- + *tty->flip.flag_buf_ptr++ = TTY_BREAK;
- + if (info->flags & ASYNC_SAK)
- + do_SAK(tty);
- + } else if (*status & UART_LSR_PE)
- + *tty->flip.flag_buf_ptr++ = TTY_PARITY;
- + else if (*status & UART_LSR_FE)
- + *tty->flip.flag_buf_ptr++ = TTY_FRAME;
- + else if (*status & UART_LSR_OE)
- + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
- + else
- + *tty->flip.flag_buf_ptr++ = 0;
- + *tty->flip.char_buf_ptr++ = ch;
- + ignore_char:
- + *status = serial_inp(info, UART_LSR) & info->read_status_mask;
- + } while (*status & UART_LSR_DR);
- + queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
- +#ifdef SERIAL_DEBUG_INTR
- + printk("DR...");
- +#endif
- +}
- +
- +static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
- +{
- + int count;
- +
- + if (info->x_char) {
- + serial_outp(info, UART_TX, info->x_char);
- + info->x_char = 0;
- + if (intr_done)
- + *intr_done = 0;
- + return;
- + }
- + if ((info->xmit_cnt <= 0) || info->tty->stopped ||
- + info->tty->hw_stopped) {
- + info->IER &= ~UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + return;
- + }
- +
- + count = info->xmit_fifo_size;
- + do {
- + serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]);
- + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
- + if (--info->xmit_cnt <= 0)
- + break;
- + } while (--count > 0);
- +
- + if (info->xmit_cnt < WAKEUP_CHARS)
- + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
- +
- +#ifdef SERIAL_DEBUG_INTR
- + printk("THRE...");
- +#endif
- + if (intr_done)
- + *intr_done = 0;
- +
- + if (info->xmit_cnt <= 0) {
- + info->IER &= ~UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + }
- +}
- +
- +static _INLINE_ void check_modem_status(struct async_struct *info)
- +{
- + int status;
- +
- + status = serial_in(info, UART_MSR);
- +
- + if (status & UART_MSR_ANY_DELTA) {
- + /* update input line counters */
- + if (status & UART_MSR_TERI)
- + info->icount.rng++;
- + if (status & UART_MSR_DDSR)
- + info->icount.dsr++;
- + if (status & UART_MSR_DDCD)
- + info->icount.dcd++;
- + if (status & UART_MSR_DCTS)
- + info->icount.cts++;
- + wake_up_interruptible(&info->delta_msr_wait);
- + }
- +
- + if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
- +#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
- + printk("ttys%d CD now %s...", info->line,
- + (status & UART_MSR_DCD) ? "on" : "off");
- +#endif
- + if (status & UART_MSR_DCD)
- + wake_up_interruptible(&info->open_wait);
- + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
- + (info->flags & ASYNC_CALLOUT_NOHUP))) {
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("scheduling hangup...");
- +#endif
- + queue_task_irq_off(&info->tqueue_hangup,
- + &tq_scheduler);
- + }
- + }
- + if (info->flags & ASYNC_CTS_FLOW) {
- + if (info->tty->hw_stopped) {
- + if (status & UART_MSR_CTS) {
- +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
- + printk("CTS tx start...");
- +#endif
- + info->tty->hw_stopped = 0;
- + info->IER |= UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
- + return;
- + }
- + } else {
- + if (!(status & UART_MSR_CTS)) {
- +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
- + printk("CTS tx stop...");
- +#endif
- + info->tty->hw_stopped = 1;
- + info->IER &= ~UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + }
- + }
- + }
- +}
- +
- +/*
- + * This is the serial driver's generic interrupt routine
- + */
- +static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
- +{
- + int status;
- + struct async_struct * info;
- + int pass_counter = 0;
- + struct async_struct *end_mark = 0;
- + int first_multi = 0;
- + struct rs_multiport_struct *multi;
- +
- +#ifdef SERIAL_DEBUG_INTR
- + printk("rs_interrupt(%d)...", irq);
- +#endif
- +
- + info = IRQ_ports[irq];
- + if (!info)
- + return;
- +
- + multi = &rs_multiport[irq];
- + if (multi->port_monitor)
- + first_multi = inb(multi->port_monitor);
- +
- + do {
- + if (!info->tty ||
- + (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) {
- + if (!end_mark)
- + end_mark = info;
- + goto next;
- + }
- + end_mark = 0;
- +
- + info->last_active = jiffies;
- +
- + status = serial_inp(info, UART_LSR) & info->read_status_mask;
- +#ifdef SERIAL_DEBUG_INTR
- + printk("status = %x...", status);
- +#endif
- + if (status & UART_LSR_DR)
- + receive_chars(info, &status);
- + check_modem_status(info);
- + if (status & UART_LSR_THRE)
- + transmit_chars(info, 0);
- +
- + next:
- + info = info->next_port;
- + if (!info) {
- + info = IRQ_ports[irq];
- + if (pass_counter++ > RS_ISR_PASS_LIMIT) {
- +#if 0
- + printk("rs loop break\n");
- +#endif
- + break; /* Prevent infinite loops */
- + }
- + continue;
- + }
- + } while (end_mark != info);
- + if (multi->port_monitor)
- + printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n",
- + info->irq, first_multi, inb(multi->port_monitor));
- +#ifdef SERIAL_DEBUG_INTR
- + printk("end.\n");
- +#endif
- +}
- +
- +/*
- + * This is the serial driver's interrupt routine for a single port
- + */
- +static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
- +{
- + int status;
- + int pass_counter = 0;
- + int first_multi = 0;
- + struct async_struct * info;
- + struct rs_multiport_struct *multi;
- +
- +#ifdef SERIAL_DEBUG_INTR
- + printk("rs_interrupt_single(%d)...", irq);
- +#endif
- +
- + info = IRQ_ports[irq];
- + if (!info || !info->tty)
- + return;
- +
- + multi = &rs_multiport[irq];
- + if (multi->port_monitor)
- + first_multi = inb(multi->port_monitor);
- +
- + do {
- + status = serial_inp(info, UART_LSR) & info->read_status_mask;
- +#ifdef SERIAL_DEBUG_INTR
- + printk("status = %x...", status);
- +#endif
- + if (status & UART_LSR_DR)
- + receive_chars(info, &status);
- + check_modem_status(info);
- + if (status & UART_LSR_THRE)
- + transmit_chars(info, 0);
- + if (pass_counter++ > RS_ISR_PASS_LIMIT) {
- +#if 0
- + printk("rs_single loop break.\n");
- +#endif
- + break;
- + }
- + } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
- + info->last_active = jiffies;
- + if (multi->port_monitor)
- + printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",
- + info->irq, first_multi, inb(multi->port_monitor));
- +#ifdef SERIAL_DEBUG_INTR
- + printk("end.\n");
- +#endif
- +}
- +
- +/*
- + * This is the serial driver's for multiport boards
- + */
- +static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs * regs)
- +{
- + int status;
- + struct async_struct * info;
- + int pass_counter = 0;
- + int first_multi= 0;
- + struct rs_multiport_struct *multi;
- +
- +#ifdef SERIAL_DEBUG_INTR
- + printk("rs_interrupt_multi(%d)...", irq);
- +#endif
- +
- + info = IRQ_ports[irq];
- + if (!info)
- + return;
- + multi = &rs_multiport[irq];
- + if (!multi->port1) {
- + /* Should never happen */
- + printk("rs_interrupt_multi: NULL port1!\n");
- + return;
- + }
- + if (multi->port_monitor)
- + first_multi = inb(multi->port_monitor);
- +
- + while (1) {
- + if (!info->tty ||
- + (serial_in(info, UART_IIR) & UART_IIR_NO_INT))
- + goto next;
- +
- + info->last_active = jiffies;
- +
- + status = serial_inp(info, UART_LSR) & info->read_status_mask;
- +#ifdef SERIAL_DEBUG_INTR
- + printk("status = %x...", status);
- +#endif
- + if (status & UART_LSR_DR)
- + receive_chars(info, &status);
- + check_modem_status(info);
- + if (status & UART_LSR_THRE)
- + transmit_chars(info, 0);
- +
- + next:
- + info = info->next_port;
- + if (info)
- + continue;
- +
- + info = IRQ_ports[irq];
- + if (pass_counter++ > RS_ISR_PASS_LIMIT) {
- +#if 1
- + printk("rs_multi loop break\n");
- +#endif
- + break; /* Prevent infinite loops */
- + }
- + if (multi->port_monitor)
- + printk("rs port monitor irq %d: 0x%x, 0x%x\n",
- + info->irq, first_multi,
- + inb(multi->port_monitor));
- + if ((inb(multi->port1) & multi->mask1) != multi->match1)
- + continue;
- + if (!multi->port2)
- + break;
- + if ((inb(multi->port2) & multi->mask2) != multi->match2)
- + continue;
- + if (!multi->port3)
- + break;
- + if ((inb(multi->port3) & multi->mask3) != multi->match3)
- + continue;
- + if (!multi->port4)
- + break;
- + if ((inb(multi->port4) & multi->mask4) == multi->match4)
- + continue;
- + break;
- + }
- +#ifdef SERIAL_DEBUG_INTR
- + printk("end.\n");
- +#endif
- +}
- +
- +
- +/*
- + * -------------------------------------------------------------------
- + * Here ends the serial interrupt routines.
- + * -------------------------------------------------------------------
- + */
- +
- +/*
- + * This routine is used to handle the "bottom half" processing for the
- + * serial driver, known also the "software interrupt" processing.
- + * This processing is done at the kernel interrupt level, after the
- + * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
- + * is where time-consuming activities which can not be done in the
- + * interrupt driver proper are done; the interrupt driver schedules
- + * them using rs_sched_event(), and they get done here.
- + */
- +static void do_serial_bh(void)
- +{
- + run_task_queue(&tq_serial);
- +}
- +
- +static void do_softint(void *private_)
- +{
- + struct async_struct *info = (struct async_struct *) private_;
- + struct tty_struct *tty;
- +
- + tty = info->tty;
- + if (!tty)
- + return;
- +
- + if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
- + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- + tty->ldisc.write_wakeup)
- + (tty->ldisc.write_wakeup)(tty);
- + wake_up_interruptible(&tty->write_wait);
- + }
- +}
- +
- +/*
- + * This routine is called from the scheduler tqueue when the interrupt
- + * routine has signalled that a hangup has occurred. The path of
- + * hangup processing is:
- + *
- + * serial interrupt routine -> (scheduler tqueue) ->
- + * do_serial_hangup() -> tty->hangup() -> rs_hangup()
- + *
- + */
- +static void do_serial_hangup(void *private_)
- +{
- + struct async_struct *info = (struct async_struct *) private_;
- + struct tty_struct *tty;
- +
- + tty = info->tty;
- + if (!tty)
- + return;
- +
- + tty_hangup(tty);
- +}
- +
- +
- +/*
- + * This subroutine is called when the RS_TIMER goes off. It is used
- + * by the serial driver to handle ports that do not have an interrupt
- + * (irq=0). This doesn't work very well for 16450's, but gives barely
- + * passable results for a 16550A. (Although at the expense of much
- + * CPU overhead).
- + */
- +static void rs_timer(void)
- +{
- + static unsigned long last_strobe = 0;
- + struct async_struct *info;
- + unsigned int i;
- +
- + if ((jiffies - last_strobe) >= RS_STROBE_TIME) {
- + for (i=0; i < 32; i++) {
- + info = IRQ_ports[i];
- + if (!info)
- + continue;
- + cli();
- + if (info->next_port) {
- + do {
- + serial_out(info, UART_IER, 0);
- + info->IER |= UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + info = info->next_port;
- + } while (info);
- + if (rs_multiport[i].port1)
- + rs_interrupt_multi(i, NULL, NULL);
- + else
- + rs_interrupt(i, NULL, NULL);
- + } else
- + rs_interrupt_single(i, NULL, NULL);
- + sti();
- + }
- + }
- + last_strobe = jiffies;
- + timer_table[RS_TIMER].expires = jiffies + RS_STROBE_TIME;
- + timer_active |= 1 << RS_TIMER;
- +
- + if (IRQ_ports[0]) {
- + cli();
- + rs_interrupt(0, NULL, NULL);
- + sti();
- +
- + timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2;
- + }
- +}
- +
- +/*
- + * ---------------------------------------------------------------
- + * Low level utility subroutines for the serial driver: routines to
- + * figure out the appropriate timeout for an interrupt chain, routines
- + * to initialize and startup a serial port, and routines to shutdown a
- + * serial port. Useful stuff like that.
- + * ---------------------------------------------------------------
- + */
- +
- +/*
- + * Grab all interrupts in preparation for doing an automatic irq
- + * detection. dontgrab is a mask of irq's _not_ to grab. Returns a
- + * mask of irq's which were grabbed and should therefore be freed
- + * using free_all_interrupts().
- + */
- +static int grab_all_interrupts(int dontgrab)
- +{
- + int irq_lines = 0;
- + int i, mask;
- +
- + for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
- + if (!(mask & dontgrab) && !request_irq(i, rs_probe, SA_INTERRUPT, "serial probe", NULL)) {
- + irq_lines |= mask;
- + }
- + }
- + return irq_lines;
- +}
- +
- +/*
- + * Release all interrupts grabbed by grab_all_interrupts
- + */
- +static void free_all_interrupts(int irq_lines)
- +{
- + int i;
- +
- + for (i = 0; i < 16; i++) {
- + if (irq_lines & (1 << i))
- + free_irq(i, NULL);
- + }
- +}
- +
- +/*
- + * This routine figures out the correct timeout for a particular IRQ.
- + * It uses the smallest timeout of all of the serial ports in a
- + * particular interrupt chain. Now only used for IRQ 0....
- + */
- +static void figure_IRQ_timeout(int irq)
- +{
- + struct async_struct *info;
- + int timeout = 60*HZ; /* 60 seconds === a long time :-) */
- +
- + info = IRQ_ports[irq];
- + if (!info) {
- + IRQ_timeout[irq] = 60*HZ;
- + return;
- + }
- + while (info) {
- + if (info->timeout < timeout)
- + timeout = info->timeout;
- + info = info->next_port;
- + }
- + if (!irq)
- + timeout = timeout / 2;
- + IRQ_timeout[irq] = timeout ? timeout : 1;
- +}
- +
- +static int startup(struct async_struct * info)
- +{
- + unsigned short ICP;
- + unsigned long flags;
- + int retval;
- + void (*handler)(int, void *, struct pt_regs *);
- + unsigned long page;
- +
- + page = get_free_page(GFP_KERNEL);
- + if (!page)
- + return -ENOMEM;
- +
- +
- + save_flags_cli(flags);
- +
- + if (info->flags & ASYNC_INITIALIZED) {
- + free_page(page);
- + restore_flags(flags);
- + return 0;
- + }
- +
- + if (!info->port || !info->type) {
- + if (info->tty)
- + set_bit(TTY_IO_ERROR, &info->tty->flags);
- + free_page(page);
- + restore_flags(flags);
- + return 0;
- + }
- + if (info->xmit_buf)
- + free_page(page);
- + else
- + info->xmit_buf = (unsigned char *) page;
- +
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("starting up ttys%d (irq %d)...", info->line, info->irq);
- +#endif
- +
- + /*
- + * Clear the FIFO buffers and disable them
- + * (they will be reenabled in change_speed())
- + */
- + if (info->type == PORT_16650) {
- + serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
- + UART_FCR_CLEAR_XMIT));
- + info->xmit_fifo_size = 1; /* disabled for now */
- + } else if (info->type == PORT_16550A) {
- + serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
- + UART_FCR_CLEAR_XMIT));
- + info->xmit_fifo_size = 16;
- + } else
- + info->xmit_fifo_size = 1;
- +
- + /*
- + * At this point there's no way the LSR could still be 0xFF;
- + * if it is, then bail out, because there's likely no UART
- + * here.
- + */
- + if (serial_inp(info, UART_LSR) == 0xff) {
- + restore_flags(flags);
- + if (suser()) {
- + if (info->tty)
- + set_bit(TTY_IO_ERROR, &info->tty->flags);
- + return 0;
- + } else
- + return -ENODEV;
- + }
- +
- + /*
- + * Allocate the IRQ if necessary
- + */
- + if (info->irq && (!IRQ_ports[info->irq] ||
- + !IRQ_ports[info->irq]->next_port)) {
- + if (IRQ_ports[info->irq]) {
- + free_irq(info->irq, NULL);
- + if (rs_multiport[info->irq].port1)
- + handler = rs_interrupt_multi;
- + else
- + handler = rs_interrupt;
- + } else
- + handler = rs_interrupt_single;
- +
- + retval = request_irq(info->irq, handler, IRQ_T(info),
- + "serial", NULL);
- + if (retval) {
- + restore_flags(flags);
- + if (suser()) {
- + if (info->tty)
- + set_bit(TTY_IO_ERROR,
- + &info->tty->flags);
- + return 0;
- + } else
- + return retval;
- + }
- + }
- +
- + /*
- + * Clear the interrupt registers.
- + */
- + /* (void) serial_inp(info, UART_LSR); */ /* (see above) */
- + (void) serial_inp(info, UART_RX);
- + (void) serial_inp(info, UART_IIR);
- + (void) serial_inp(info, UART_MSR);
- +
- + /*
- + * Now, initialize the UART
- + */
- + serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
- + if (info->flags & ASYNC_FOURPORT) {
- + info->MCR = UART_MCR_DTR | UART_MCR_RTS;
- + info->MCR_noint = UART_MCR_DTR | UART_MCR_OUT1;
- + } else {
- + info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
- + info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;
- + }
- +#if defined(__alpha__) && !defined(CONFIG_PCI)
- + info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
- + info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2;
- +#endif
- + if (info->irq == 0)
- + info->MCR = info->MCR_noint;
- + serial_outp(info, UART_MCR, info->MCR);
- +
- + /*
- + * Finally, enable interrupts
- + */
- + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
- + serial_outp(info, UART_IER, info->IER); /* enable interrupts */
- +
- + if (info->flags & ASYNC_FOURPORT) {
- + /* Enable interrupts on the AST Fourport board */
- + ICP = (info->port & 0xFE0) | 0x01F;
- + outb_p(0x80, ICP);
- + (void) inb_p(ICP);
- + }
- +
- + /*
- + * And clear the interrupt registers again for luck.
- + */
- + (void)serial_inp(info, UART_LSR);
- + (void)serial_inp(info, UART_RX);
- + (void)serial_inp(info, UART_IIR);
- + (void)serial_inp(info, UART_MSR);
- +
- + if (info->tty)
- + clear_bit(TTY_IO_ERROR, &info->tty->flags);
- + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- +
- + /*
- + * Insert serial port into IRQ chain.
- + */
- + info->prev_port = 0;
- + info->next_port = IRQ_ports[info->irq];
- + if (info->next_port)
- + info->next_port->prev_port = info;
- + IRQ_ports[info->irq] = info;
- + figure_IRQ_timeout(info->irq);
- +
- + /*
- + * Set up serial timers...
- + */
- + timer_table[RS_TIMER].expires = jiffies + 2*HZ/100;
- + timer_active |= 1 << RS_TIMER;
- +
- + /*
- + * and set the speed of the serial port
- + */
- + change_speed(info);
- +
- + info->flags |= ASYNC_INITIALIZED;
- + restore_flags(flags);
- + return 0;
- +}
- +
- +/*
- + * This routine will shutdown a serial port; interrupts are disabled, and
- + * DTR is dropped if the hangup on close termio flag is on.
- + */
- +static void shutdown(struct async_struct * info)
- +{
- + unsigned long flags;
- + int retval;
- +
- + if (!(info->flags & ASYNC_INITIALIZED))
- + return;
- +
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("Shutting down serial port %d (irq %d)....", info->line,
- + info->irq);
- +#endif
- +
- + save_flags_cli(flags); /* Disable interrupts */
- +
- + /*
- + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
- + * here so the queue might never be waken up
- + */
- + wake_up_interruptible(&info->delta_msr_wait);
- +
- + /*
- + * First unlink the serial port from the IRQ chain...
- + */
- + if (info->next_port)
- + info->next_port->prev_port = info->prev_port;
- + if (info->prev_port)
- + info->prev_port->next_port = info->next_port;
- + else
- + IRQ_ports[info->irq] = info->next_port;
- + figure_IRQ_timeout(info->irq);
- +
- + /*
- + * Free the IRQ, if necessary
- + */
- + if (info->irq && (!IRQ_ports[info->irq] ||
- + !IRQ_ports[info->irq]->next_port)) {
- + if (IRQ_ports[info->irq]) {
- + free_irq(info->irq, NULL);
- + retval = request_irq(info->irq, rs_interrupt_single,
- + IRQ_T(info), "serial", NULL);
- +
- + if (retval)
- + printk("serial shutdown: request_irq: error %d"
- + " Couldn't reacquire IRQ.\n", retval);
- + } else
- + free_irq(info->irq, NULL);
- + }
- +
- + if (info->xmit_buf) {
- + free_page((unsigned long) info->xmit_buf);
- + info->xmit_buf = 0;
- + }
- +
- + info->IER = 0;
- + serial_outp(info, UART_IER, 0x00); /* disable all intrs */
- + if (info->flags & ASYNC_FOURPORT) {
- + /* reset interrupts on the AST Fourport board */
- + (void) inb((info->port & 0xFE0) | 0x01F);
- + }
- +
- + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
- + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
- + info->MCR_noint &= ~(UART_MCR_DTR|UART_MCR_RTS);
- + }
- + serial_outp(info, UART_MCR, info->MCR_noint);
- +
- + /* disable FIFO's */
- + serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
- + UART_FCR_CLEAR_XMIT));
- + (void)serial_in(info, UART_RX); /* read data port to reset things */
- +
- + if (info->tty)
- + set_bit(TTY_IO_ERROR, &info->tty->flags);
- +
- + info->flags &= ~ASYNC_INITIALIZED;
- + restore_flags(flags);
- +}
- +
- +/*
- + * This routine is called to set the UART divisor registers to match
- + * the specified baud rate for a serial port.
- + */
- +static void change_speed(struct async_struct *info)
- +{
- + unsigned short port;
- + int quot = 0;
- + unsigned cflag,cval,fcr;
- + int i;
- +
- + if (!info->tty || !info->tty->termios)
- + return;
- + cflag = info->tty->termios->c_cflag;
- + if (!(port = info->port))
- + return;
- + i = cflag & CBAUD;
- + if (i & CBAUDEX) {
- + i &= ~CBAUDEX;
- + if (i < 1 || i > 2)
- + info->tty->termios->c_cflag &= ~CBAUDEX;
- + else
- + i += 15;
- + }
- + if (i == 15) {
- + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- + i += 1;
- + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- + i += 2;
- + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
- + quot = info->custom_divisor;
- + }
- + if (quot) {
- + info->timeout = ((info->xmit_fifo_size*HZ*15*quot) /
- + info->baud_base) + 2;
- + } else if (baud_table[i] == 134) {
- + quot = (2*info->baud_base / 269);
- + info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
- + } else if (baud_table[i]) {
- + quot = info->baud_base / baud_table[i];
- + info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
- + } else {
- + quot = 0;
- + info->timeout = 0;
- + }
- + if (quot) {
- + info->MCR |= UART_MCR_DTR;
- + info->MCR_noint |= UART_MCR_DTR;
- + cli();
- + serial_out(info, UART_MCR, info->MCR);
- + sti();
- + } else {
- + info->MCR &= ~UART_MCR_DTR;
- + info->MCR_noint &= ~UART_MCR_DTR;
- + cli();
- + serial_out(info, UART_MCR, info->MCR);
- + sti();
- + return;
- + }
- + /* byte size and parity */
- + switch (cflag & CSIZE) {
- + case CS5: cval = 0x00; break;
- + case CS6: cval = 0x01; break;
- + case CS7: cval = 0x02; break;
- + case CS8: cval = 0x03; break;
- + default: cval = 0x00; break; /* too keep GCC shut... */
- + }
- + if (cflag & CSTOPB) {
- + cval |= 0x04;
- + }
- + if (cflag & PARENB)
- + cval |= UART_LCR_PARITY;
- + if (!(cflag & PARODD))
- + cval |= UART_LCR_EPAR;
- + if (info->type == PORT_16550A) {
- + if ((info->baud_base / quot) < 2400)
- + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
- + else
- + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
- + } else if (info->type == PORT_16650) {
- + /*
- + * On the 16650, we disable the FIFOs altogether
- + * because of a design bug in how the implement
- + * things. We could support it by completely changing
- + * how we handle the interrupt driver, but not today....
- + *
- + * N.B. Because there's no way to set a FIFO trigger
- + * at 1 char, we'd probably disable at speed below
- + * 2400 baud anyway...
- + */
- + fcr = 0;
- + } else
- + fcr = 0;
- +
- + /* CTS flow control flag and modem status interrupts */
- + info->IER &= ~UART_IER_MSI;
- + if (cflag & CRTSCTS) {
- + info->flags |= ASYNC_CTS_FLOW;
- + info->IER |= UART_IER_MSI;
- + } else
- + info->flags &= ~ASYNC_CTS_FLOW;
- + if (cflag & CLOCAL)
- + info->flags &= ~ASYNC_CHECK_CD;
- + else {
- + info->flags |= ASYNC_CHECK_CD;
- + info->IER |= UART_IER_MSI;
- + }
- + serial_out(info, UART_IER, info->IER);
- +
- + /*
- + * Set up parity check flag
- + */
- +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
- +
- + info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
- + if (I_INPCK(info->tty))
- + info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
- + if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
- + info->read_status_mask |= UART_LSR_BI;
- +
- + info->ignore_status_mask = 0;
- +#if 0
- + /* This should be safe, but for some broken bits of hardware... */
- + if (I_IGNPAR(info->tty)) {
- + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
- + info->read_status_mask |= UART_LSR_PE | UART_LSR_FE;
- + }
- +#endif
- + if (I_IGNBRK(info->tty)) {
- + info->ignore_status_mask |= UART_LSR_BI;
- + info->read_status_mask |= UART_LSR_BI;
- + /*
- + * If we're ignore parity and break indicators, ignore
- + * overruns too. (For real raw support).
- + */
- + if (I_IGNPAR(info->tty)) {
- + info->ignore_status_mask |= UART_LSR_OE |
- + UART_LSR_PE | UART_LSR_FE;
- + info->read_status_mask |= UART_LSR_OE |
- + UART_LSR_PE | UART_LSR_FE;
- + }
- + }
- + cli();
- + serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
- + serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */
- + serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */
- + serial_outp(info, UART_LCR, cval); /* reset DLAB */
- + serial_outp(info, UART_FCR, fcr); /* set fcr */
- + sti();
- +}
- +
- +static void rs_put_char(struct tty_struct *tty, unsigned char ch)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- + unsigned long flags;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_put_char"))
- + return;
- +
- + if (!tty || !info->xmit_buf)
- + return;
- +
- + save_flags_cli (flags);
- + if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
- + restore_flags(flags);
- + return;
- + }
- +
- + info->xmit_buf[info->xmit_head++] = ch;
- + info->xmit_head &= SERIAL_XMIT_SIZE-1;
- + info->xmit_cnt++;
- + restore_flags(flags);
- +}
- +
- +static void rs_flush_chars(struct tty_struct *tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- + unsigned long flags;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
- + return;
- +
- + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
- + !info->xmit_buf)
- + return;
- +
- + save_flags_cli (flags);
- + info->IER |= UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + restore_flags(flags);
- +}
- +
- +static int rs_write(struct tty_struct * tty, int from_user,
- + const unsigned char *buf, int count)
- +{
- + int c, total = 0;
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- + unsigned long flags;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_write"))
- + return 0;
- +
- + if (!tty || !info->xmit_buf || !tmp_buf)
- + return 0;
- +
- + if (from_user)
- + down(&tmp_buf_sem);
- + save_flags(flags);
- + while (1) {
- + cli();
- + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
- + SERIAL_XMIT_SIZE - info->xmit_head));
- + if (c <= 0)
- + break;
- +
- + if (from_user) {
- + memcpy_fromfs(tmp_buf, buf, c);
- + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
- + SERIAL_XMIT_SIZE - info->xmit_head));
- + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
- + } else
- + memcpy(info->xmit_buf + info->xmit_head, buf, c);
- + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
- + info->xmit_cnt += c;
- + restore_flags(flags);
- + buf += c;
- + count -= c;
- + total += c;
- + }
- + if (from_user)
- + up(&tmp_buf_sem);
- + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
- + !(info->IER & UART_IER_THRI)) {
- + info->IER |= UART_IER_THRI;
- + serial_out(info, UART_IER, info->IER);
- + }
- + restore_flags(flags);
- + return total;
- +}
- +
- +static int rs_write_room(struct tty_struct *tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- + int ret;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_write_room"))
- + return 0;
- + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
- + if (ret < 0)
- + ret = 0;
- + return ret;
- +}
- +
- +static int rs_chars_in_buffer(struct tty_struct *tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
- + return 0;
- + return info->xmit_cnt;
- +}
- +
- +static void rs_flush_buffer(struct tty_struct *tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
- + return;
- + cli();
- + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- + sti();
- + wake_up_interruptible(&tty->write_wait);
- + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- + tty->ldisc.write_wakeup)
- + (tty->ldisc.write_wakeup)(tty);
- +}
- +
- +/*
- + * ------------------------------------------------------------
- + * rs_throttle()
- + *
- + * This routine is called by the upper-layer tty layer to signal that
- + * incoming characters should be throttled.
- + * ------------------------------------------------------------
- + */
- +static void rs_throttle(struct tty_struct * tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- +#ifdef SERIAL_DEBUG_THROTTLE
- + char buf[64];
- +
- + printk("throttle %s: %d....\n", _tty_name(tty, buf),
- + tty->ldisc.chars_in_buffer(tty));
- +#endif
- +
- + if (serial_paranoia_check(info, tty->device, "rs_throttle"))
- + return;
- +
- + if (I_IXOFF(tty))
- + info->x_char = STOP_CHAR(tty);
- +
- + info->MCR &= ~UART_MCR_RTS;
- + info->MCR_noint &= ~UART_MCR_RTS;
- + cli();
- + serial_out(info, UART_MCR, info->MCR);
- + sti();
- +}
- +
- +static void rs_unthrottle(struct tty_struct * tty)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- +#ifdef SERIAL_DEBUG_THROTTLE
- + char buf[64];
- +
- + printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
- + tty->ldisc.chars_in_buffer(tty));
- +#endif
- +
- + if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
- + return;
- +
- + if (I_IXOFF(tty)) {
- + if (info->x_char)
- + info->x_char = 0;
- + else
- + info->x_char = START_CHAR(tty);
- + }
- + info->MCR |= UART_MCR_RTS;
- + info->MCR_noint |= UART_MCR_RTS;
- + cli();
- + serial_out(info, UART_MCR, info->MCR);
- + sti();
- +}
- +
- +/*
- + * ------------------------------------------------------------
- + * rs_ioctl() and friends
- + * ------------------------------------------------------------
- + */
- +
- +static int get_serial_info(struct async_struct * info,
- + struct serial_struct * retinfo)
- +{
- + struct serial_struct tmp;
- +
- + if (!retinfo)
- + return -EFAULT;
- + memset(&tmp, 0, sizeof(tmp));
- + tmp.type = info->type;
- + tmp.line = info->line;
- + tmp.port = info->port;
- + tmp.irq = info->irq;
- + tmp.flags = info->flags;
- + tmp.baud_base = info->baud_base;
- + tmp.close_delay = info->close_delay;
- + tmp.closing_wait = info->closing_wait;
- + tmp.custom_divisor = info->custom_divisor;
- + tmp.hub6 = info->hub6;
- + memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
- + return 0;
- +}
- +
- +static int set_serial_info(struct async_struct * info,
- + struct serial_struct * new_info)
- +{
- + struct serial_struct new_serial;
- + struct async_struct old_info;
- + unsigned int i,change_irq,change_port;
- + int retval = 0;
- +
- + if (!new_info)
- + return -EFAULT;
- + memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
- + old_info = *info;
- +
- + change_irq = new_serial.irq != info->irq;
- + change_port = (new_serial.port != info->port) || (new_serial.hub6 != info->hub6);
- +
- + if (!suser()) {
- + if (change_irq || change_port ||
- + (new_serial.baud_base != info->baud_base) ||
- + (new_serial.type != info->type) ||
- + (new_serial.close_delay != info->close_delay) ||
- + ((new_serial.flags & ~ASYNC_USR_MASK) !=
- + (info->flags & ~ASYNC_USR_MASK)))
- + return -EPERM;
- + info->flags = ((info->flags & ~ASYNC_USR_MASK) |
- + (new_serial.flags & ASYNC_USR_MASK));
- + info->custom_divisor = new_serial.custom_divisor;
- + goto check_and_exit;
- + }
- +#if 0
- + if (new_serial.irq == 2)
- + new_serial.irq = 9;
- +
- + if ((new_serial.irq > 15) || (new_serial.port > 0xffff) ||
- + (new_serial.type < PORT_UNKNOWN) || (new_serial.type > PORT_MAX)) {
- + return -EINVAL;
- + }
- +#else
- + if ((new_serial.irq > 31) || (new_serial.type < PORT_UNKNOWN) ||
- + (new_serial.type > PORT_MAX)) {
- + return -EINVAL;
- + }
- +#endif
- +
- + /* Make sure address is not already in use */
- + if (new_serial.type) {
- + for (i = 0 ; i < NR_PORTS; i++)
- + if ((info != &rs_table[i]) &&
- + (rs_table[i].port == new_serial.port) &&
- + rs_table[i].type)
- + return -EADDRINUSE;
- + }
- +
- + if ((change_port || change_irq) && (info->count > 1))
- + return -EBUSY;
- +
- + /*
- + * OK, past this point, all the error checking has been done.
- + * At this point, we start making changes.....
- + */
- +
- + info->baud_base = new_serial.baud_base;
- + info->flags = ((info->flags & ~ASYNC_FLAGS) |
- + (new_serial.flags & ASYNC_FLAGS));
- + info->custom_divisor = new_serial.custom_divisor;
- + info->type = new_serial.type;
- + info->close_delay = new_serial.close_delay * HZ/100;
- + info->closing_wait = new_serial.closing_wait * HZ/100;
- +
- + release_region(info->port,8);
- + if (change_port || change_irq) {
- + /*
- + * We need to shutdown the serial port at the old
- + * port/irq combination.
- + */
- + shutdown(info);
- + info->irq = new_serial.irq;
- + info->port = new_serial.port;
- + info->hub6 = new_serial.hub6;
- + }
- + if(info->type != PORT_UNKNOWN)
- + request_region(info->port,8,"serial(set)");
- +
- +
- +check_and_exit:
- + if (!info->port || !info->type)
- + return 0;
- + if (info->flags & ASYNC_INITIALIZED) {
- + if (((old_info.flags & ASYNC_SPD_MASK) !=
- + (info->flags & ASYNC_SPD_MASK)) ||
- + (old_info.custom_divisor != info->custom_divisor))
- + change_speed(info);
- + } else
- + retval = startup(info);
- + return retval;
- +}
- +
- +
- +/*
- + * get_lsr_info - get line status register info
- + *
- + * Purpose: Let user call ioctl() to get info when the UART physically
- + * is emptied. On bus types like RS485, the transmitter must
- + * release the bus after transmitting. This must be done when
- + * the transmit shift register is empty, not be done when the
- + * transmit holding register is empty. This functionality
- + * allows an RS485 driver to be written in user space.
- + */
- +static int get_lsr_info(struct async_struct * info, unsigned int *value)
- +{
- + unsigned char status;
- + unsigned int result;
- +
- + cli();
- + status = serial_in(info, UART_LSR);
- + sti();
- + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
- + put_user(result,value);
- + return 0;
- +}
- +
- +
- +static int get_modem_info(struct async_struct * info, unsigned int *value)
- +{
- + unsigned char control, status;
- + unsigned int result;
- +
- + control = info->MCR;
- + cli();
- + status = serial_in(info, UART_MSR);
- + sti();
- + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
- + | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
- + | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
- + | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
- + | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
- + | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
- + put_user(result,value);
- + return 0;
- +}
- +
- +static int set_modem_info(struct async_struct * info, unsigned int cmd,
- + unsigned int *value)
- +{
- + int error;
- + unsigned int arg;
- +
- + error = verify_area(VERIFY_READ, value, sizeof(int));
- + if (error)
- + return error;
- + arg = get_user(value);
- + switch (cmd) {
- + case TIOCMBIS:
- + if (arg & TIOCM_RTS) {
- + info->MCR |= UART_MCR_RTS;
- + info->MCR_noint |= UART_MCR_RTS;
- + }
- + if (arg & TIOCM_DTR) {
- + info->MCR |= UART_MCR_DTR;
- + info->MCR_noint |= UART_MCR_DTR;
- + }
- + break;
- + case TIOCMBIC:
- + if (arg & TIOCM_RTS) {
- + info->MCR &= ~UART_MCR_RTS;
- + info->MCR_noint &= ~UART_MCR_RTS;
- + }
- + if (arg & TIOCM_DTR) {
- + info->MCR &= ~UART_MCR_DTR;
- + info->MCR_noint &= ~UART_MCR_DTR;
- + }
- + break;
- + case TIOCMSET:
- + info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
- + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
- + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
- + info->MCR_noint = ((info->MCR_noint
- + & ~(UART_MCR_RTS | UART_MCR_DTR))
- + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
- + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
- + break;
- + default:
- + return -EINVAL;
- + }
- + cli();
- + serial_out(info, UART_MCR, info->MCR);
- + sti();
- + return 0;
- +}
- +
- +static int do_autoconfig(struct async_struct * info)
- +{
- + int retval;
- +
- + if (!suser())
- + return -EPERM;
- +
- + if (info->count > 1)
- + return -EBUSY;
- +
- + shutdown(info);
- +
- + cli();
- + autoconfig(info);
- + sti();
- +
- + retval = startup(info);
- + if (retval)
- + return retval;
- + return 0;
- +}
- +
- +
- +/*
- + * This routine sends a break character out the serial port.
- + */
- +static void send_break( struct async_struct * info, int duration)
- +{
- + if (!info->port)
- + return;
- + current->state = TASK_INTERRUPTIBLE;
- + current->timeout = jiffies + duration;
- + cli();
- + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
- + schedule();
- + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
- + sti();
- +}
- +
- +/*
- + * This routine returns a bitfield of "wild interrupts". Basically,
- + * any unclaimed interrupts which is flapping around.
- + */
- +static int check_wild_interrupts(int doprint)
- +{
- + int i, mask;
- + int wild_interrupts = 0;
- + int irq_lines;
- + unsigned long timeout;
- + unsigned long flags;
- +
- + /* Turn on interrupts (they may be off) */
- + save_flags(flags); sti();
- +
- + irq_lines = grab_all_interrupts(0);
- +
- + /*
- + * Delay for 0.1 seconds -- we use a busy loop since this may
- + * occur during the bootup sequence
- + */
- + timeout = jiffies+HZ/10;
- + while (timeout >= jiffies)
- + ;
- +
- + rs_triggered = 0; /* Reset after letting things settle */
- +
- + timeout = jiffies+HZ/10;
- + while (timeout >= jiffies)
- + ;
- +
- + for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
- + if ((rs_triggered & (1 << i)) &&
- + (irq_lines & (1 << i))) {
- + wild_interrupts |= mask;
- + if (doprint)
- + printk("Wild interrupt? (IRQ %d)\n", i);
- + }
- + }
- + free_all_interrupts(irq_lines);
- + restore_flags(flags);
- + return wild_interrupts;
- +}
- +
- +static int get_multiport_struct(struct async_struct * info,
- + struct serial_multiport_struct *retinfo)
- +{
- + struct serial_multiport_struct ret;
- + struct rs_multiport_struct *multi;
- +
- + multi = &rs_multiport[info->irq];
- +
- + ret.port_monitor = multi->port_monitor;
- +
- + ret.port1 = multi->port1;
- + ret.mask1 = multi->mask1;
- + ret.match1 = multi->match1;
- +
- + ret.port2 = multi->port2;
- + ret.mask2 = multi->mask2;
- + ret.match2 = multi->match2;
- +
- + ret.port3 = multi->port3;
- + ret.mask3 = multi->mask3;
- + ret.match3 = multi->match3;
- +
- + ret.port4 = multi->port4;
- + ret.mask4 = multi->mask4;
- + ret.match4 = multi->match4;
- +
- + ret.irq = info->irq;
- +
- + memcpy_tofs(retinfo,&ret,sizeof(*retinfo));
- + return 0;
- +
- +}
- +
- +static int set_multiport_struct(struct async_struct * info,
- + struct serial_multiport_struct *in_multi)
- +{
- + struct serial_multiport_struct new_multi;
- + struct rs_multiport_struct *multi;
- + int was_multi, now_multi;
- + int retval;
- + void (*handler)(int, void *, struct pt_regs *);
- +
- + if (!suser())
- + return -EPERM;
- + if (!in_multi)
- + return -EFAULT;
- + memcpy_fromfs(&new_multi, in_multi,
- + sizeof(struct serial_multiport_struct));
- +
- + if (new_multi.irq != info->irq || info->irq == 0 ||
- + !IRQ_ports[info->irq])
- + return -EINVAL;
- +
- + multi = &rs_multiport[info->irq];
- + was_multi = (multi->port1 != 0);
- +
- + multi->port_monitor = new_multi.port_monitor;
- +
- + if (multi->port1)
- + release_region(multi->port1,1);
- + multi->port1 = new_multi.port1;
- + multi->mask1 = new_multi.mask1;
- + multi->match1 = new_multi.match1;
- + if (multi->port1)
- + request_region(multi->port1,1,"serial(multiport1)");
- +
- + if (multi->port2)
- + release_region(multi->port2,1);
- + multi->port2 = new_multi.port2;
- + multi->mask2 = new_multi.mask2;
- + multi->match2 = new_multi.match2;
- + if (multi->port2)
- + request_region(multi->port2,1,"serial(multiport2)");
- +
- + if (multi->port3)
- + release_region(multi->port3,1);
- + multi->port3 = new_multi.port3;
- + multi->mask3 = new_multi.mask3;
- + multi->match3 = new_multi.match3;
- + if (multi->port3)
- + request_region(multi->port3,1,"serial(multiport3)");
- +
- + if (multi->port4)
- + release_region(multi->port4,1);
- + multi->port4 = new_multi.port4;
- + multi->mask4 = new_multi.mask4;
- + multi->match4 = new_multi.match4;
- + if (multi->port4)
- + request_region(multi->port4,1,"serial(multiport4)");
- +
- + now_multi = (multi->port1 != 0);
- +
- + if (IRQ_ports[info->irq]->next_port &&
- + (was_multi != now_multi)) {
- + free_irq(info->irq, NULL);
- + if (now_multi)
- + handler = rs_interrupt_multi;
- + else
- + handler = rs_interrupt;
- +
- + retval = request_irq(info->irq, handler, IRQ_T(info),
- + "serial", NULL);
- + if (retval) {
- + printk("Couldn't reallocate serial interrupt "
- + "driver!!\n");
- + }
- + }
- +
- + return 0;
- +}
- +
- +static int rs_ioctl(struct tty_struct *tty, struct file * file,
- + unsigned int cmd, unsigned long arg)
- +{
- + int error;
- + struct async_struct * info = (struct async_struct *)tty->driver_data;
- + int retval;
- + struct async_icount cprev, cnow; /* kernel counter temps */
- + struct serial_icounter_struct *p_cuser; /* user space */
- +
- + if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
- + return -ENODEV;
- +
- + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
- + (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
- + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
- + if (tty->flags & (1 << TTY_IO_ERROR))
- + return -EIO;
- + }
- +
- + switch (cmd) {
- + case TCSBRK: /* SVID version: non-zero arg --> no break */
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- + tty_wait_until_sent(tty, 0);
- + if (!arg)
- + send_break(info, HZ/4); /* 1/4 second */
- + return 0;
- + case TCSBRKP: /* support for POSIX tcsendbreak() */
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- + tty_wait_until_sent(tty, 0);
- + send_break(info, arg ? arg*(HZ/10) : HZ/4);
- + return 0;
- + case TIOCGSOFTCAR:
- + error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long));
- + if (error)
- + return error;
- + put_fs_long(C_CLOCAL(tty) ? 1 : 0,
- + (unsigned long *) arg);
- + return 0;
- + case TIOCSSOFTCAR:
- + error = verify_area(VERIFY_READ, (void *) arg,sizeof(long));
- + if (error)
- + return error;
- + arg = get_fs_long((unsigned long *) arg);
- + tty->termios->c_cflag =
- + ((tty->termios->c_cflag & ~CLOCAL) |
- + (arg ? CLOCAL : 0));
- + return 0;
- + case TIOCMGET:
- + error = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(unsigned int));
- + if (error)
- + return error;
- + return get_modem_info(info, (unsigned int *) arg);
- + case TIOCMBIS:
- + case TIOCMBIC:
- + case TIOCMSET:
- + return set_modem_info(info, cmd, (unsigned int *) arg);
- + case TIOCGSERIAL:
- + error = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(struct serial_struct));
- + if (error)
- + return error;
- + return get_serial_info(info,
- + (struct serial_struct *) arg);
- + case TIOCSSERIAL:
- + error = verify_area(VERIFY_READ, (void *) arg,
- + sizeof(struct serial_struct));
- + if (error)
- + return error;
- + return set_serial_info(info,
- + (struct serial_struct *) arg);
- + case TIOCSERCONFIG:
- + return do_autoconfig(info);
- +
- + case TIOCSERGWILD:
- + error = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(int));
- + if (error)
- + return error;
- + put_fs_long(rs_wild_int_mask, (unsigned long *) arg);
- + return 0;
- +
- + case TIOCSERGETLSR: /* Get line status register */
- + error = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(unsigned int));
- + if (error)
- + return error;
- + else
- + return get_lsr_info(info, (unsigned int *) arg);
- +
- + case TIOCSERSWILD:
- + if (!suser())
- + return -EPERM;
- + error = verify_area(VERIFY_READ, (void *) arg,sizeof(long));
- + if (error)
- + return error;
- + rs_wild_int_mask = get_fs_long((unsigned long *) arg);
- + if (rs_wild_int_mask < 0)
- + rs_wild_int_mask = check_wild_interrupts(0);
- + return 0;
- +
- + case TIOCSERGSTRUCT:
- + error = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(struct async_struct));
- + if (error)
- + return error;
- + memcpy_tofs((struct async_struct *) arg,
- + info, sizeof(struct async_struct));
- + return 0;
- +
- + case TIOCSERGETMULTI:
- + error = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(struct serial_multiport_struct));
- + if (error)
- + return error;
- + return get_multiport_struct(info,
- + (struct serial_multiport_struct *) arg);
- + case TIOCSERSETMULTI:
- + error = verify_area(VERIFY_READ, (void *) arg,
- + sizeof(struct serial_multiport_struct));
- + if (error)
- + return error;
- + return set_multiport_struct(info,
- + (struct serial_multiport_struct *) arg);
- + /*
- + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
- + * - mask passed in arg for lines of interest
- + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
- + * Caller should use TIOCGICOUNT to see which one it was
- + */
- + case TIOCMIWAIT:
- + cli();
- + cprev = info->icount; /* note the counters on entry */
- + sti();
- + while (1) {
- + interruptible_sleep_on(&info->delta_msr_wait);
- + /* see if a signal did it */
- + if (current->signal & ~current->blocked)
- + return -ERESTARTSYS;
- + cli();
- + cnow = info->icount; /* atomic copy */
- + sti();
- + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
- + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
- + return -EIO; /* no change => error */
- + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
- + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
- + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
- + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
- + return 0;
- + }
- + cprev = cnow;
- + }
- + /* NOTREACHED */
- +
- + /*
- + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
- + * Return: write counters to the user passed counter struct
- + * NB: both 1->0 and 0->1 transitions are counted except for
- + * RI where only 0->1 is counted.
- + */
- + case TIOCGICOUNT:
- + error = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(struct serial_icounter_struct));
- + if (error)
- + return error;
- + cli();
- + cnow = info->icount;
- + sti();
- + p_cuser = (struct serial_icounter_struct *) arg;
- + put_user(cnow.cts, &p_cuser->cts);
- + put_user(cnow.dsr, &p_cuser->dsr);
- + put_user(cnow.rng, &p_cuser->rng);
- + put_user(cnow.dcd, &p_cuser->dcd);
- + return 0;
- +
- + default:
- + return -ENOIOCTLCMD;
- + }
- + return 0;
- +}
- +
- +static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
- +{
- + struct async_struct *info = (struct async_struct *)tty->driver_data;
- +
- + if ( (tty->termios->c_cflag == old_termios->c_cflag)
- + && ( RELEVANT_IFLAG(tty->termios->c_iflag)
- + == RELEVANT_IFLAG(old_termios->c_iflag)))
- + return;
- +
- + change_speed(info);
- +
- + if ((old_termios->c_cflag & CRTSCTS) &&
- + !(tty->termios->c_cflag & CRTSCTS)) {
- + tty->hw_stopped = 0;
- + rs_start(tty);
- + }
- +
- +#if 0
- + /*
- + * No need to wake up processes in open wait, since they
- + * sample the CLOCAL flag once, and don't recheck it.
- + * XXX It's not clear whether the current behavior is correct
- + * or not. Hence, this may change.....
- + */
- + if (!(old_termios->c_cflag & CLOCAL) &&
- + (tty->termios->c_cflag & CLOCAL))
- + wake_up_interruptible(&info->open_wait);
- +#endif
- +}
- +
- +/*
- + * ------------------------------------------------------------
- + * rs_close()
- + *
- + * This routine is called when the serial port gets closed. First, we
- + * wait for the last remaining data to be sent. Then, we unlink its
- + * async structure from the interrupt chain if necessary, and we free
- + * that IRQ if nothing is left in the chain.
- + * ------------------------------------------------------------
- + */
- +static void rs_close(struct tty_struct *tty, struct file * filp)
- +{
- + struct async_struct * info = (struct async_struct *)tty->driver_data;
- + unsigned long flags;
- + unsigned long timeout;
- +
- + if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
- + return;
- +
- + save_flags_cli (flags);
- +
- + if (tty_hung_up_p(filp)) {
- + DBG_CNT("before DEC-hung");
- + MOD_DEC_USE_COUNT;
- + restore_flags(flags);
- + return;
- + }
- +
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("rs_close ttys%d, count = %d\n", info->line, info->count);
- +#endif
- + if ((tty->count == 1) && (info->count != 1)) {
- + /*
- + * Uh, oh. tty->count is 1, which means that the tty
- + * structure will be freed. Info->count should always
- + * be one in these conditions. If it's greater than
- + * one, we've got real problems, since it means the
- + * serial port won't be shutdown.
- + */
- + printk("rs_close: bad serial port count; tty->count is 1, "
- + "info->count is %d\n", info->count);
- + info->count = 1;
- + }
- + if (--info->count < 0) {
- + printk("rs_close: bad serial port count for ttys%d: %d\n",
- + info->line, info->count);
- + info->count = 0;
- + }
- + if (info->count) {
- + DBG_CNT("before DEC-2");
- + MOD_DEC_USE_COUNT;
- + restore_flags(flags);
- + return;
- + }
- + info->flags |= ASYNC_CLOSING;
- + /*
- + * Save the termios structure, since this port may have
- + * separate termios for callout and dialin.
- + */
- + if (info->flags & ASYNC_NORMAL_ACTIVE)
- + info->normal_termios = *tty->termios;
- + if (info->flags & ASYNC_CALLOUT_ACTIVE)
- + info->callout_termios = *tty->termios;
- + /*
- + * Now we wait for the transmit buffer to clear; and we notify
- + * the line discipline to only process XON/XOFF characters.
- + */
- + tty->closing = 1;
- + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
- + tty_wait_until_sent(tty, info->closing_wait);
- + /*
- + * At this point we stop accepting input. To do this, we
- + * disable the receive line status interrupts, and tell the
- + * interrupt driver to stop checking the data ready bit in the
- + * line status register.
- + */
- + info->IER &= ~UART_IER_RLSI;
- + info->read_status_mask &= ~UART_LSR_DR;
- + if (info->flags & ASYNC_INITIALIZED) {
- + serial_out(info, UART_IER, info->IER);
- + /*
- + * Before we drop DTR, make sure the UART transmitter
- + * has completely drained; this is especially
- + * important if there is a transmit FIFO!
- + */
- + timeout = jiffies+HZ;
- + while (!(serial_inp(info, UART_LSR) & UART_LSR_TEMT)) {
- + current->state = TASK_INTERRUPTIBLE;
- + current->timeout = jiffies + info->timeout;
- + schedule();
- + if (jiffies > timeout)
- + break;
- + }
- + }
- + shutdown(info);
- + if (tty->driver.flush_buffer)
- + tty->driver.flush_buffer(tty);
- + if (tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer(tty);
- + tty->closing = 0;
- + info->event = 0;
- + info->tty = 0;
- + if (info->blocked_open) {
- + if (info->close_delay) {
- + current->state = TASK_INTERRUPTIBLE;
- + current->timeout = jiffies + info->close_delay;
- + schedule();
- + }
- + wake_up_interruptible(&info->open_wait);
- + }
- + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
- + ASYNC_CLOSING);
- + wake_up_interruptible(&info->close_wait);
- + MOD_DEC_USE_COUNT;
- + restore_flags(flags);
- +}
- +
- +/*
- + * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
- + */
- +void rs_hangup(struct tty_struct *tty)
- +{
- + struct async_struct * info = (struct async_struct *)tty->driver_data;
- +
- + if (serial_paranoia_check(info, tty->device, "rs_hangup"))
- + return;
- +
- + rs_flush_buffer(tty);
- + shutdown(info);
- + info->event = 0;
- + info->count = 0;
- + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
- + info->tty = 0;
- + wake_up_interruptible(&info->open_wait);
- +}
- +
- +/*
- + * ------------------------------------------------------------
- + * rs_open() and friends
- + * ------------------------------------------------------------
- + */
- +static int block_til_ready(struct tty_struct *tty, struct file * filp,
- + struct async_struct *info)
- +{
- + struct wait_queue wait = { current, NULL };
- + int retval;
- + int do_clocal = 0;
- +
- + /*
- + * If the device is in the middle of being closed, then block
- + * until it's done, and then try again.
- + */
- + if (tty_hung_up_p(filp) ||
- + (info->flags & ASYNC_CLOSING)) {
- + if (info->flags & ASYNC_CLOSING)
- + interruptible_sleep_on(&info->close_wait);
- +#ifdef SERIAL_DO_RESTART
- + if (info->flags & ASYNC_HUP_NOTIFY)
- + return -EAGAIN;
- + else
- + return -ERESTARTSYS;
- +#else
- + return -EAGAIN;
- +#endif
- + }
- +
- + /*
- + * If this is a callout device, then just make sure the normal
- + * device isn't being used.
- + */
- + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
- + if (info->flags & ASYNC_NORMAL_ACTIVE)
- + return -EBUSY;
- + if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
- + (info->flags & ASYNC_SESSION_LOCKOUT) &&
- + (info->session != current->session))
- + return -EBUSY;
- + if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
- + (info->flags & ASYNC_PGRP_LOCKOUT) &&
- + (info->pgrp != current->pgrp))
- + return -EBUSY;
- + info->flags |= ASYNC_CALLOUT_ACTIVE;
- + return 0;
- + }
- +
- + /*
- + * If non-blocking mode is set, or the port is not enabled,
- + * then make the check up front and then exit.
- + */
- + if ((filp->f_flags & O_NONBLOCK) ||
- + (tty->flags & (1 << TTY_IO_ERROR))) {
- + if (info->flags & ASYNC_CALLOUT_ACTIVE)
- + return -EBUSY;
- + info->flags |= ASYNC_NORMAL_ACTIVE;
- + return 0;
- + }
- +
- + if (info->flags & ASYNC_CALLOUT_ACTIVE) {
- + if (info->normal_termios.c_cflag & CLOCAL)
- + do_clocal = 1;
- + } else {
- + if (tty->termios->c_cflag & CLOCAL)
- + do_clocal = 1;
- + }
- +
- + /*
- + * Block waiting for the carrier detect and the line to become
- + * free (i.e., not in use by the callout). While we are in
- + * this loop, info->count is dropped by one, so that
- + * rs_close() knows when to free things. We restore it upon
- + * exit, either normal or abnormal.
- + */
- + retval = 0;
- + add_wait_queue(&info->open_wait, &wait);
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("block_til_ready before block: ttys%d, count = %d\n",
- + info->line, info->count);
- +#endif
- + cli();
- + if (!tty_hung_up_p(filp))
- + info->count--;
- + sti();
- + info->blocked_open++;
- + while (1) {
- + cli();
- + if (!(info->flags & ASYNC_CALLOUT_ACTIVE))
- + serial_out(info, UART_MCR,
- + serial_inp(info, UART_MCR) |
- + (UART_MCR_DTR | UART_MCR_RTS));
- + sti();
- + current->state = TASK_INTERRUPTIBLE;
- + if (tty_hung_up_p(filp) ||
- + !(info->flags & ASYNC_INITIALIZED)) {
- +#ifdef SERIAL_DO_RESTART
- + if (info->flags & ASYNC_HUP_NOTIFY)
- + retval = -EAGAIN;
- + else
- + retval = -ERESTARTSYS;
- +#else
- + retval = -EAGAIN;
- +#endif
- + break;
- + }
- + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
- + !(info->flags & ASYNC_CLOSING) &&
- + (do_clocal || (serial_in(info, UART_MSR) &
- + UART_MSR_DCD)))
- + break;
- + if (current->signal & ~current->blocked) {
- + retval = -ERESTARTSYS;
- + break;
- + }
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("block_til_ready blocking: ttys%d, count = %d\n",
- + info->line, info->count);
- +#endif
- + schedule();
- + }
- + current->state = TASK_RUNNING;
- + remove_wait_queue(&info->open_wait, &wait);
- + if (!tty_hung_up_p(filp))
- + info->count++;
- + info->blocked_open--;
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("block_til_ready after blocking: ttys%d, count = %d\n",
- + info->line, info->count);
- +#endif
- + if (retval)
- + return retval;
- + info->flags |= ASYNC_NORMAL_ACTIVE;
- + return 0;
- +}
- +
- +/*
- + * This routine is called whenever a serial port is opened. It
- + * enables interrupts for a serial port, linking in its async structure into
- + * the IRQ chain. It also performs the serial-specific
- + * initialization for the tty structure.
- + */
- +int rs_open(struct tty_struct *tty, struct file * filp)
- +{
- + struct async_struct *info;
- + int retval, line;
- + unsigned long page;
- +
- + line = MINOR(tty->device) - tty->driver.minor_start;
- + if ((line < 0) || (line >= NR_PORTS))
- + return -ENODEV;
- + info = rs_table + line;
- + if (serial_paranoia_check(info, tty->device, "rs_open"))
- + return -ENODEV;
- +
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
- + info->count);
- +#endif
- + info->count++;
- + tty->driver_data = info;
- + info->tty = tty;
- +
- + if (!tmp_buf) {
- + page = get_free_page(GFP_KERNEL);
- + if (!page)
- + return -ENOMEM;
- + if (tmp_buf)
- + free_page(page);
- + else
- + tmp_buf = (unsigned char *) page;
- + }
- +
- + /*
- + * Start up serial port
- + */
- + retval = startup(info);
- + if (retval)
- + return retval;
- +
- + MOD_INC_USE_COUNT;
- + retval = block_til_ready(tty, filp, info);
- + if (retval) {
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("rs_open returning after block_til_ready with %d\n",
- + retval);
- +#endif
- + return retval;
- + }
- +
- + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
- + if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
- + *tty->termios = info->normal_termios;
- + else
- + *tty->termios = info->callout_termios;
- + change_speed(info);
- + }
- +
- + info->session = current->session;
- + info->pgrp = current->pgrp;
- +
- +#ifdef SERIAL_DEBUG_OPEN
- + printk("rs_open ttys%d successful...", info->line);
- +#endif
- + return 0;
- +}
- +
- +/*
- + * ---------------------------------------------------------------------
- + * rs_init() and friends
- + *
- + * rs_init() is called at boot-time to initialize the serial driver.
- + * ---------------------------------------------------------------------
- + */
- +
- +/*
- + * This routine prints out the appropriate serial driver version
- + * number, and identifies which options were configured into this
- + * driver.
- + */
- +static void show_serial_version(void)
- +{
- + printk(KERN_INFO "%s version %s with", serial_name, serial_version);
- +#ifdef CONFIG_HUB6
- + printk(" HUB-6");
- +#define SERIAL_OPT
- +#endif
- +#ifdef SERIAL_OPT
- + printk(" enabled\n");
- +#else
- + printk(" no serial options enabled\n");
- +#endif
- +#undef SERIAL_OPT
- +}
- +
- +/*
- + * This routine is called by do_auto_irq(); it attempts to determine
- + * which interrupt a serial port is configured to use. It is not
- + * fool-proof, but it works a large part of the time.
- + */
- +static int get_auto_irq(struct async_struct *info)
- +{
- + unsigned char save_MCR, save_IER, save_ICP=0;
- + unsigned short ICP=0, port = info->port;
- + unsigned long timeout;
- +
- + /*
- + * Enable interrupts and see who answers
- + */
- + rs_irq_triggered = 0;
- + cli();
- + save_IER = serial_inp(info, UART_IER);
- + save_MCR = serial_inp(info, UART_MCR);
- + if (info->flags & ASYNC_FOURPORT) {
- + serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
- + serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
- + ICP = (port & 0xFE0) | 0x01F;
- + save_ICP = inb_p(ICP);
- + outb_p(0x80, ICP);
- + (void) inb_p(ICP);
- + } else {
- + serial_outp(info, UART_MCR,
- + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
- + serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
- + }
- + sti();
- + /*
- + * Next, clear the interrupt registers.
- + */
- + (void)serial_inp(info, UART_LSR);
- + (void)serial_inp(info, UART_RX);
- + (void)serial_inp(info, UART_IIR);
- + (void)serial_inp(info, UART_MSR);
- +
- + timeout = jiffies+2*HZ/100;
- + while (timeout >= jiffies) {
- + if (rs_irq_triggered)
- + break;
- + }
- + /*
- + * Now check to see if we got any business, and clean up.
- + */
- + cli();
- + serial_outp(info, UART_IER, save_IER);
- + serial_outp(info, UART_MCR, save_MCR);
- + if (info->flags & ASYNC_FOURPORT)
- + outb_p(save_ICP, ICP);
- + sti();
- + return(rs_irq_triggered);
- +}
- +
- +/*
- + * Calls get_auto_irq() multiple times, to make sure we don't get
- + * faked out by random interrupts
- + */
- +static int do_auto_irq(struct async_struct * info)
- +{
- + unsigned port = info->port;
- + int irq_lines = 0;
- + int irq_try_1 = 0, irq_try_2 = 0;
- + int retries;
- + unsigned long flags;
- +
- + if (!port)
- + return 0;
- +
- + /* Turn on interrupts (they may be off) */
- + save_flags(flags); sti();
- +
- + irq_lines = grab_all_interrupts(rs_wild_int_mask);
- +
- + for (retries = 0; retries < 5; retries++) {
- + if (!irq_try_1)
- + irq_try_1 = get_auto_irq(info);
- + if (!irq_try_2)
- + irq_try_2 = get_auto_irq(info);
- + if (irq_try_1 && irq_try_2) {
- + if (irq_try_1 == irq_try_2)
- + break;
- + irq_try_1 = irq_try_2 = 0;
- + }
- + }
- + restore_flags(flags);
- + free_all_interrupts(irq_lines);
- + return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
- +}
- +
- +/*
- + * This routine is called by rs_init() to initialize a specific serial
- + * port. It determines what type of UART chip this serial port is
- + * using: 8250, 16450, 16550, 16550A. The important question is
- + * whether or not this UART is a 16550A or not, since this will
- + * determine whether or not we can use its FIFO features or not.
- + */
- +static void autoconfig(struct async_struct * info)
- +{
- + unsigned char status1, status2, scratch, scratch2;
- + unsigned port = info->port;
- + unsigned long flags;
- +
- + info->type = PORT_UNKNOWN;
- +
- + if (!port)
- + return;
- +
- + save_flags_cli (flags);
- +
- + /*
- + * Do a simple existence test first; if we fail this, there's
- + * no point trying anything else.
- + *
- + * 0x80 is used as a nonsense port to prevent against false
- + * positives due to ISA bus float. The assumption is that
- + * 0x80 is a non-existent port; which should be safe since
- + * include/asm/io.h also makes this assumption.
- + */
- + scratch = serial_inp(info, UART_IER);
- + serial_outp(info, UART_IER, 0);
- + outb(0xff, 0x080);
- + scratch2 = serial_inp(info, UART_IER);
- + serial_outp(info, UART_IER, scratch);
- + if (scratch2) {
- + restore_flags(flags);
- + return; /* We failed; there's nothing here */
- + }
- +
- + /*
- + * Check to see if a UART is really there. Certain broken
- + * internal modems based on the Rockwell chipset fail this
- + * test, because they apparently don't implement the loopback
- + * test mode. So this test is skipped on the COM 1 through
- + * COM 4 ports. This *should* be safe, since no board
- + * manufacturer would be stupid enough to design a board
- + * that conflicts with COM 1-4 --- we hope!
- + */
- + if (!(info->flags & ASYNC_SKIP_TEST)) {
- + scratch = serial_inp(info, UART_MCR);
- + serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch);
- + scratch2 = serial_inp(info, UART_MSR);
- + serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A);
- + status1 = serial_inp(info, UART_MSR) & 0xF0;
- + serial_outp(info, UART_MCR, scratch);
- + serial_outp(info, UART_MSR, scratch2);
- + if (status1 != 0x90) {
- + restore_flags(flags);
- + return;
- + }
- + }
- +
- + /*
- + * If the AUTO_IRQ flag is set, try to do the automatic IRQ
- + * detection.
- + */
- + if (info->flags & ASYNC_AUTO_IRQ)
- + info->irq = do_auto_irq(info);
- +
- + scratch2 = serial_in(info, UART_LCR);
- + serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
- + serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */
- + serial_outp(info, UART_LCR, scratch2);
- + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
- + scratch = serial_in(info, UART_IIR) >> 6;
- + info->xmit_fifo_size = 1;
- + switch (scratch) {
- + case 0:
- + info->type = PORT_16450;
- + break;
- + case 1:
- + info->type = PORT_UNKNOWN;
- + break;
- + case 2:
- + info->type = PORT_16550;
- + break;
- + case 3:
- + serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
- + if (serial_in(info, UART_EFR) == 0) {
- + info->type = PORT_16650;
- + info->xmit_fifo_size = 32;
- + } else {
- + info->type = PORT_16550A;
- + info->xmit_fifo_size = 16;
- + }
- + serial_outp(info, UART_LCR, scratch2);
- + break;
- + }
- + if (info->type == PORT_16450) {
- + scratch = serial_in(info, UART_SCR);
- + serial_outp(info, UART_SCR, 0xa5);
- + status1 = serial_in(info, UART_SCR);
- + serial_outp(info, UART_SCR, 0x5a);
- + status2 = serial_in(info, UART_SCR);
- + serial_outp(info, UART_SCR, scratch);
- +
- + if ((status1 != 0xa5) || (status2 != 0x5a))
- + info->type = PORT_8250;
- + }
- + request_region(info->port,8,"serial(auto)");
- +
- + /*
- + * Reset the UART.
- + */
- +#if defined(__alpha__) && !defined(CONFIG_PCI)
- + /*
- + * I wonder what DEC did to the OUT1 and OUT2 lines?
- + * clearing them results in endless interrupts.
- + */
- + serial_outp(info, UART_MCR, 0x0c);
- +#else
- + serial_outp(info, UART_MCR, 0x00);
- +#endif
- + serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
- + UART_FCR_CLEAR_XMIT));
- + (void)serial_in(info, UART_RX);
- +
- + restore_flags(flags);
- +}
- +
- +int register_serial(struct serial_struct *req);
- +void unregister_serial(int line);
- +
- +static struct symbol_table serial_syms = {
- +#include <linux/symtab_begin.h>
- + X(register_serial),
- + X(unregister_serial),
- +#include <linux/symtab_end.h>
- +};
- +
- +/*
- + * The serial driver boot-time initialization code!
- + */
- +int rs_init(void)
- +{
- + int i;
- + struct async_struct * info;
- +#ifdef CONFIG_ATOMWIDE_SERIAL
- + extern void atomwide_serial_init (void);
- +
- + atomwide_serial_init ();
- +#endif
- +
- + init_bh(SERIAL_BH, do_serial_bh);
- + timer_table[RS_TIMER].fn = rs_timer;
- + timer_table[RS_TIMER].expires = 0;
- +#ifdef CONFIG_AUTO_IRQ
- + rs_wild_int_mask = check_wild_interrupts(1);
- +#endif
- +
- + for (i = 0; i < 32; i++) {
- + IRQ_ports[i] = 0;
- + IRQ_timeout[i] = 0;
- + memset(&rs_multiport[i], 0, sizeof(struct rs_multiport_struct));
- + }
- +
- + show_serial_version();
- +
- + /* Initialize the tty_driver structure */
- +
- + memset(&serial_driver, 0, sizeof(struct tty_driver));
- + serial_driver.magic = TTY_DRIVER_MAGIC;
- + serial_driver.name = "ttyS";
- + serial_driver.major = TTY_MAJOR;
- + serial_driver.minor_start = 64;
- + serial_driver.num = NR_PORTS;
- + serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
- + serial_driver.subtype = SERIAL_TYPE_NORMAL;
- + serial_driver.init_termios = tty_std_termios;
- + serial_driver.init_termios.c_cflag =
- + B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- + serial_driver.flags = TTY_DRIVER_REAL_RAW;
- + serial_driver.refcount = &serial_refcount;
- + serial_driver.table = serial_table;
- + serial_driver.termios = serial_termios;
- + serial_driver.termios_locked = serial_termios_locked;
- +
- + serial_driver.open = rs_open;
- + serial_driver.close = rs_close;
- + serial_driver.write = rs_write;
- + serial_driver.put_char = rs_put_char;
- + serial_driver.flush_chars = rs_flush_chars;
- + serial_driver.write_room = rs_write_room;
- + serial_driver.chars_in_buffer = rs_chars_in_buffer;
- + serial_driver.flush_buffer = rs_flush_buffer;
- + serial_driver.ioctl = rs_ioctl;
- + serial_driver.throttle = rs_throttle;
- + serial_driver.unthrottle = rs_unthrottle;
- + serial_driver.set_termios = rs_set_termios;
- + serial_driver.stop = rs_stop;
- + serial_driver.start = rs_start;
- + serial_driver.hangup = rs_hangup;
- +
- + /*
- + * The callout device is just like normal device except for
- + * major number and the subtype code.
- + */
- + callout_driver = serial_driver;
- + callout_driver.name = "cua";
- + callout_driver.major = TTYAUX_MAJOR;
- + callout_driver.subtype = SERIAL_TYPE_CALLOUT;
- +
- + if (tty_register_driver(&serial_driver))
- + panic("Couldn't register serial driver\n");
- + if (tty_register_driver(&callout_driver))
- + panic("Couldn't register callout driver\n");
- +
- + for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
- + info->magic = SERIAL_MAGIC;
- + info->line = i;
- + info->tty = 0;
- + info->type = PORT_UNKNOWN;
- + info->custom_divisor = 0;
- + info->close_delay = 5*HZ/10;
- + info->closing_wait = 30*HZ;
- + info->x_char = 0;
- + info->event = 0;
- + info->count = 0;
- + info->blocked_open = 0;
- + info->tqueue.routine = do_softint;
- + info->tqueue.data = info;
- + info->tqueue_hangup.routine = do_serial_hangup;
- + info->tqueue_hangup.data = info;
- + info->callout_termios =callout_driver.init_termios;
- + info->normal_termios = serial_driver.init_termios;
- + info->open_wait = 0;
- + info->close_wait = 0;
- + info->delta_msr_wait = 0;
- + info->icount.cts = info->icount.dsr =
- + info->icount.rng = info->icount.dcd = 0;
- + info->next_port = 0;
- + info->prev_port = 0;
- +#if 0
- + if (info->irq == 2)
- + info->irq = 9;
- +#endif
- + if (info->type == PORT_UNKNOWN) {
- + if (!(info->flags & ASYNC_BOOT_AUTOCONF))
- + continue;
- + autoconfig(info);
- + if (info->type == PORT_UNKNOWN)
- + continue;
- + }
- + printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d)", info->line,
- + (info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
- + info->port, info->irq);
- + switch (info->type) {
- + case PORT_8250:
- + printk(" is a 8250\n");
- + break;
- + case PORT_16450:
- + printk(" is a 16450\n");
- + break;
- + case PORT_16550:
- + printk(" is a 16550\n");
- + break;
- + case PORT_16550A:
- + printk(" is a 16550A\n");
- + break;
- + case PORT_16650:
- + printk(" is a 16650\n");
- + break;
- + default:
- + printk("\n");
- + break;
- + }
- + }
- + register_symtab(&serial_syms);
- + return 0;
- +}
- +
- +int register_pre_init_serial (struct serial_struct *req)
- +{
- + int i;
- + struct async_struct *info;
- +
- + for (i = 0; i < NR_PORTS; i++) {
- + if (rs_table[i].port == req->port)
- + break;
- + }
- +
- + if (i == NR_PORTS) {
- + for (i = 0; i < NR_PORTS; i++)
- + if (!rs_table[i].port)
- + break;
- + }
- + if (i == NR_PORTS)
- + return -1;
- +
- + info = &rs_table[i];
- + if (info->count) {
- + printk ("Couldn't configure serial #%d (port=%d, irq=%d): "
- + "device already open\n", i, req->port, req->irq);
- + return -1;
- + }
- + info->irq = req->irq;
- + info->port = req->port;
- + info->baud_base = req->baud_base;
- + serial_outp (info, UART_IER, 0);
- + return 0;
- +}
- +
- +/*
- + * register_serial and unregister_serial allows for serial ports to be
- + * configured at run-time, to support PCMCIA modems.
- + */
- +int register_serial(struct serial_struct *req)
- +{
- + int i;
- + unsigned long flags;
- + struct async_struct *info;
- +
- + save_flags_cli (flags);
- + for (i = 0; i < NR_PORTS; i++) {
- + if (rs_table[i].port == req->port)
- + break;
- + }
- + if (i == NR_PORTS) {
- + for (i = 0; i < NR_PORTS; i++)
- + if ((rs_table[i].type == PORT_UNKNOWN) &&
- + (rs_table[i].count == 0))
- + break;
- + }
- + if (i == NR_PORTS) {
- + restore_flags(flags);
- + return -1;
- + }
- + info = &rs_table[i];
- + if (rs_table[i].count) {
- + restore_flags(flags);
- + printk("Couldn't configure serial #%d (port=%d,irq=%d): "
- + "device already open\n", i, req->port, req->irq);
- + return -1;
- + }
- + info->irq = req->irq;
- + info->port = req->port;
- + if (req->baud_base) /* rmk: need this to set the baud rate... */
- + info->baud_base = req->baud_base;
- + serial_outp(info, UART_IER, 0); /* rmk: need to make sure port is disabled */
- + info->flags = req->flags;
- + autoconfig(info);
- + if (info->type == PORT_UNKNOWN) {
- + restore_flags(flags);
- + printk("register_serial(): autoconfig failed\n");
- + return -1;
- + }
- + printk(KERN_INFO "tty%02d at 0x%04x (irq = %d)", info->line,
- + info->port, info->irq);
- + switch (info->type) {
- + case PORT_8250:
- + printk(" is a 8250\n"); break;
- + case PORT_16450:
- + printk(" is a 16450\n"); break;
- + case PORT_16550:
- + printk(" is a 16550\n"); break;
- + case PORT_16550A:
- + printk(" is a 16550A\n"); break;
- + default:
- + printk("\n"); break;
- + }
- + restore_flags(flags);
- + register_symtab(&serial_syms);
- + return info->line;
- +}
- +
- +void unregister_serial(int line)
- +{
- + unsigned long flags;
- + struct async_struct *info = &rs_table[line];
- +
- + save_flags_cli (flags);
- + if (info->tty)
- + tty_hangup(info->tty);
- + info->type = PORT_UNKNOWN;
- + release_region(info->port,8);
- + printk(KERN_INFO "tty%02d unloaded\n", info->line);
- + restore_flags(flags);
- +}
- +
- +#ifdef MODULE
- +int init_module(void)
- +{
- + return rs_init();
- +}
- +
- +void cleanup_module(void)
- +{
- + unsigned long flags;
- + int e1, e2;
- + int i;
- +
- + /* printk("Unloading %s: version %s\n", serial_name, serial_version); */
- + save_flags(flags);
- + cli();
- + timer_active &= ~(1 << RS_TIMER);
- + timer_table[RS_TIMER].fn = NULL;
- + timer_table[RS_TIMER].expires = 0;
- + if ((e1 = tty_unregister_driver(&serial_driver)))
- + printk("SERIAL: failed to unregister serial driver (%d)\n",
- + e1);
- + if ((e2 = tty_unregister_driver(&callout_driver)))
- + printk("SERIAL: failed to unregister callout driver (%d)\n",
- + e2);
- + restore_flags(flags);
- +
- + for (i = 0; i < NR_PORTS; i++) {
- + if (rs_table[i].type != PORT_UNKNOWN)
- + release_region(rs_table[i].port, 8);
- + }
- +}
- +#endif /* MODULE */
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/serial6850.c linux/arch/arm/drivers/char/serial6850.c
- --- linux.orig/arch/arm/drivers/char/serial6850.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/serial6850.c Mon Mar 11 21:32:33 1996
- @@ -0,0 +1,23 @@
- +/*
- + * Dummy serial functions
- + *
- + * Copyright (C) 1995, 1996 Russell King
- + */
- +
- +#include <linux/errno.h>
- +#include <linux/sched.h>
- +#include <linux/serial.h>
- +
- +int rs_init (void)
- +{
- + return 0;
- +}
- +
- +int register_serial (struct serial_struct *dev)
- +{
- + return -1;
- +}
- +
- +void unregister_serial (int line)
- +{
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/tty_io.c linux/arch/arm/drivers/char/tty_io.c
- --- linux.orig/arch/arm/drivers/char/tty_io.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/tty_io.c Fri Sep 6 21:20:42 1996
- @@ -0,0 +1,1723 @@
- +/*
- + * linux/arch/arm/drivers/char/tty_io.c
- + *
- + * Copyright (C) 1991, 1992 Linus Torvalds
- + *
- + * Modifications for ARM processor Copright (C) 1995, 1996 Russell King.
- + */
- +
- +/*
- + * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
- + * or rs-channels. It also implements echoing, cooked mode etc.
- + *
- + * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
- + *
- + * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the
- + * tty_struct and tty_queue structures. Previously there was a array
- + * of 256 tty_struct's which was statically allocated, and the
- + * tty_queue structures were allocated at boot time. Both are now
- + * dynamically allocated only when the tty is open.
- + *
- + * Also restructured routines so that there is more of a separation
- + * between the high-level tty routines (tty_io.c and tty_ioctl.c) and
- + * the low-level tty routines (serial.c, pty.c, console.c). This
- + * makes for cleaner and more compact code. -TYT, 9/17/92
- + *
- + * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
- + * which can be dynamically activated and de-activated by the line
- + * discipline handling modules (like SLIP).
- + *
- + * NOTE: pay no attention to the line discipline code (yet); its
- + * interface is still subject to change in this version...
- + * -- TYT, 1/31/92
- + *
- + * Added functionality to the OPOST tty handling. No delays, but all
- + * other bits should be there.
- + * -- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993.
- + *
- + * Rewrote canonical mode and added more termios flags.
- + * -- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
- + *
- + * Reorganized FASYNC support so mouse code can share it.
- + * -- ctm@ardi.com, 9Sep95
- + *
- + * Altered get_free_page to kmalloc's for tty structures to save memory
- + * on the ARM processor
- + * -- rmk92@ecs.soton.ac.uk
- + *
- + * New TIOCLINUX variants added.
- + * -- mj@k332.feld.cvut.cz, 19-Nov-95
- + */
- +
- +#include <linux/config.h>
- +#include <linux/types.h>
- +#include <linux/major.h>
- +#include <linux/errno.h>
- +#include <linux/signal.h>
- +#include <linux/fcntl.h>
- +#include <linux/sched.h>
- +#include <linux/interrupt.h>
- +#include <linux/tty.h>
- +#include <linux/tty_flip.h>
- +#include <linux/timer.h>
- +#include <linux/ctype.h>
- +#include <linux/kd.h>
- +#include <linux/mm.h>
- +#include <linux/string.h>
- +#include <linux/malloc.h>
- +
- +#include <asm/segment.h>
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +
- +/*#include <linux/scc.h>*/
- +
- +#include "kbd_kern.h"
- +#include "vt_kern.h"
- +#include "selection.h"
- +
- +#ifdef CONFIG_KERNELD
- +#include <linux/kerneld.h>
- +#endif
- +
- +#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
- +#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
- +
- +#undef TTY_DEBUG_HANGUP
- +
- +#define TTY_PARANOIA_CHECK
- +#define CHECK_TTY_COUNT
- +
- +/*extern void set_vesa_blanking(const unsigned long arg);*/
- +
- +struct termios tty_std_termios; /* for the benefit of tty drivers */
- +struct tty_driver *tty_drivers; /* linked list of tty drivers */
- +struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */
- +
- +int kmsg_redirect;
- +struct tty_struct * redirect;
- +struct wait_queue * keypress_wait;
- +
- +static void initialize_tty_struct(struct tty_struct *tty);
- +
- +static int tty_read(struct inode *, struct file *, char *, int);
- +static int tty_write(struct inode *, struct file *, const char *, int);
- +static int tty_select(struct inode *, struct file *, int, select_table *);
- +static int tty_open(struct inode *, struct file *);
- +static void tty_release(struct inode *, struct file *);
- +static int tty_ioctl(struct inode * inode, struct file * file,
- + unsigned int cmd, unsigned long arg);
- +static int tty_fasync(struct inode * inode, struct file * filp, int on);
- +
- +#ifndef MIN
- +#define MIN(a,b) ((a) < (b) ? (a) : (b))
- +#endif
- +
- +/*
- + * These two routines return the name of tty. tty_name() should NOT
- + * be used in interrupt drivers, since it's not re-entrant. Use
- + * _tty_name() instead.
- + */
- +char *_tty_name(struct tty_struct *tty, char *buf)
- +{
- + if (tty)
- + sprintf(buf, "%s%d", tty->driver.name,
- + MINOR(tty->device) - tty->driver.minor_start +
- + tty->driver.name_base);
- + else
- + strcpy(buf, "NULL tty");
- + return buf;
- +}
- +
- +char *tty_name(struct tty_struct *tty)
- +{
- + static char buf[64];
- +
- + return(_tty_name(tty, buf));
- +}
- +
- +inline int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
- + const char *routine)
- +{
- +#ifdef TTY_PARANOIA_CHECK
- + static const char *badmagic =
- + "Warning: bad magic number for tty struct (%s) in %s\n";
- + static const char *badtty =
- + "Warning: null TTY for (%s) in %s\n";
- +
- + if (!tty) {
- + printk(badtty, kdevname(device), routine);
- + return 1;
- + }
- + if (tty->magic != TTY_MAGIC) {
- + printk(badmagic, kdevname(device), routine);
- + return 1;
- + }
- +#endif
- + return 0;
- +}
- +
- +static int check_tty_count(struct tty_struct *tty, const char *routine)
- +{
- +#ifdef CHECK_TTY_COUNT
- + struct file *f;
- + int i, count = 0;
- +
- + for (f = first_file, i=0; i<nr_files; i++, f = f->f_next) {
- + if (!f->f_count)
- + continue;
- + if (f->private_data == tty) {
- + count++;
- + }
- + }
- + if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
- + tty->driver.subtype == PTY_TYPE_SLAVE &&
- + tty->link && tty->link->count)
- + count++;
- + if (tty->count != count) {
- + printk("Warning: dev (%s) tty->count(%d) != #fd's(%d) in %s\n",
- + kdevname(tty->device), tty->count, count, routine);
- + return count;
- + }
- +#endif
- + return 0;
- +}
- +
- +int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
- +{
- + if (disc < N_TTY || disc >= NR_LDISCS)
- + return -EINVAL;
- +
- + if (new_ldisc) {
- + ldiscs[disc] = *new_ldisc;
- + ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
- + ldiscs[disc].num = disc;
- + } else
- + memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc));
- +
- + return 0;
- +}
- +
- +/* Set the discipline of a tty line. */
- +static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
- +{
- + int retval = 0;
- + struct tty_ldisc o_ldisc;
- +
- + if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS))
- + return -EINVAL;
- +#ifdef CONFIG_KERNELD
- + /* Eduardo Blanco <ejbs@cs.cs.com.uy> */
- + if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) {
- + char modname [20];
- + sprintf(modname, "tty-ldisc-%d", ldisc);
- + request_module (modname);
- + }
- +#endif
- + if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
- + return -EINVAL;
- +
- + if (tty->ldisc.num == ldisc)
- + return 0; /* We are already in the desired discipline */
- + o_ldisc = tty->ldisc;
- +
- + tty_wait_until_sent(tty, 0);
- +
- + /* Shutdown the current discipline. */
- + if (tty->ldisc.close)
- + (tty->ldisc.close)(tty);
- +
- + /* Now set up the new line discipline. */
- + tty->ldisc = ldiscs[ldisc];
- + tty->termios->c_line = ldisc;
- + if (tty->ldisc.open)
- + retval = (tty->ldisc.open)(tty);
- + if (retval < 0) {
- + tty->ldisc = o_ldisc;
- + tty->termios->c_line = tty->ldisc.num;
- + if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) {
- + tty->ldisc = ldiscs[N_TTY];
- + tty->termios->c_line = N_TTY;
- + if (tty->ldisc.open) {
- + int r = tty->ldisc.open(tty);
- +
- + if (r < 0)
- + panic("Couldn't open N_TTY ldisc for "
- + "%s --- error %d.",
- + tty_name(tty), r);
- + }
- + }
- + }
- + if (tty->ldisc.num != o_ldisc.num && tty->driver.set_ldisc)
- + tty->driver.set_ldisc(tty);
- + return retval;
- +}
- +
- +/*
- + * This routine returns a tty driver structure, given a device number
- + */
- +struct tty_driver *get_tty_driver(kdev_t device)
- +{
- + int major, minor;
- + struct tty_driver *p;
- +
- + minor = MINOR(device);
- + major = MAJOR(device);
- +
- + for (p = tty_drivers; p; p = p->next) {
- + if (p->major != major)
- + continue;
- + if (minor < p->minor_start)
- + continue;
- + if (minor >= p->minor_start + p->num)
- + continue;
- + return p;
- + }
- + return NULL;
- +}
- +
- +/*
- + * If we try to write to, or set the state of, a terminal and we're
- + * not in the foreground, send a SIGTTOU. If the signal is blocked or
- + * ignored, go ahead and perform the operation. (POSIX 7.2)
- + */
- +int tty_check_change(struct tty_struct * tty)
- +{
- + if (current->tty != tty)
- + return 0;
- + if (tty->pgrp <= 0) {
- + printk("tty_check_change: tty->pgrp <= 0!\n");
- + return 0;
- + }
- + if (current->pgrp == tty->pgrp)
- + return 0;
- + if (is_ignored(SIGTTOU))
- + return 0;
- + if (is_orphaned_pgrp(current->pgrp))
- + return -EIO;
- + (void) kill_pg(current->pgrp,SIGTTOU,1);
- + return -ERESTARTSYS;
- +}
- +
- +static int hung_up_tty_read(struct inode * inode, struct file * file, char * buf, int count)
- +{
- + return 0;
- +}
- +
- +static int hung_up_tty_write(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + return -EIO;
- +}
- +
- +static int hung_up_tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
- +{
- + return 1;
- +}
- +
- +static int hung_up_tty_ioctl(struct inode * inode, struct file * file,
- + unsigned int cmd, unsigned long arg)
- +{
- + return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
- +}
- +
- +static int tty_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
- +{
- + return -ESPIPE;
- +}
- +
- +static struct file_operations tty_fops = {
- + tty_lseek,
- + tty_read,
- + tty_write,
- + NULL, /* tty_readdir */
- + tty_select,
- + tty_ioctl,
- + NULL, /* tty_mmap */
- + tty_open,
- + tty_release,
- + NULL, /* tty_fsync */
- + tty_fasync
- +};
- +
- +static struct file_operations hung_up_tty_fops = {
- + tty_lseek,
- + hung_up_tty_read,
- + hung_up_tty_write,
- + NULL, /* hung_up_tty_readdir */
- + hung_up_tty_select,
- + hung_up_tty_ioctl,
- + NULL, /* hung_up_tty_mmap */
- + NULL, /* hung_up_tty_open */
- + tty_release, /* hung_up_tty_release */
- + NULL, /* hung_up_tty_fsync */
- + NULL /* hung_up_tty_fasync */
- +};
- +
- +void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
- +{
- + int i;
- + struct file * filp;
- + struct task_struct *p;
- +
- + if (!tty)
- + return;
- + check_tty_count(tty, "do_tty_hangup");
- + for (filp = first_file, i=0; i<nr_files; i++, filp = filp->f_next) {
- + if (!filp->f_count)
- + continue;
- + if (filp->private_data != tty)
- + continue;
- + if (!filp->f_inode)
- + continue;
- + if (filp->f_inode->i_rdev == CONSOLE_DEV)
- + continue;
- + if (filp->f_op != &tty_fops)
- + continue;
- + tty_fasync(filp->f_inode, filp, 0);
- + filp->f_op = fops;
- + }
- +
- + if (tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer(tty);
- + if (tty->driver.flush_buffer)
- + tty->driver.flush_buffer(tty);
- + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- + tty->ldisc.write_wakeup)
- + (tty->ldisc.write_wakeup)(tty);
- + wake_up_interruptible(&tty->write_wait);
- + wake_up_interruptible(&tty->read_wait);
- +
- + /*
- + * Shutdown the current line discipline, and reset it to
- + * N_TTY.
- + */
- + if (tty->ldisc.num != ldiscs[N_TTY].num) {
- + if (tty->ldisc.close)
- + (tty->ldisc.close)(tty);
- + tty->ldisc = ldiscs[N_TTY];
- + tty->termios->c_line = N_TTY;
- + if (tty->ldisc.open) {
- + i = (tty->ldisc.open)(tty);
- + if (i < 0)
- + printk("do_tty_hangup: N_TTY open: error %d\n",
- + -i);
- + }
- + }
- +
- + for_each_task(p) {
- + if ((tty->session > 0) && (p->session == tty->session) &&
- + p->leader) {
- + send_sig(SIGHUP,p,1);
- + send_sig(SIGCONT,p,1);
- + if (tty->pgrp > 0)
- + p->tty_old_pgrp = tty->pgrp;
- + }
- + if (p->tty == tty)
- + p->tty = NULL;
- + }
- + tty->flags = 0;
- + tty->session = 0;
- + tty->pgrp = -1;
- + tty->ctrl_status = 0;
- + if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
- + *tty->termios = tty->driver.init_termios;
- + if (tty->driver.hangup)
- + (tty->driver.hangup)(tty);
- +}
- +
- +void tty_hangup(struct tty_struct * tty)
- +{
- +#ifdef TTY_DEBUG_HANGUP
- + printk("%s hangup...\n", tty_name(tty));
- +#endif
- + do_tty_hangup(tty, &hung_up_tty_fops);
- +}
- +
- +void tty_vhangup(struct tty_struct * tty)
- +{
- +#ifdef TTY_DEBUG_HANGUP
- + printk("%s vhangup...\n", tty_name(tty));
- +#endif
- + do_tty_hangup(tty, &hung_up_tty_fops);
- +}
- +
- +int tty_hung_up_p(struct file * filp)
- +{
- + return (filp->f_op == &hung_up_tty_fops);
- +}
- +
- +/*
- + * This function is typically called only by the session leader, when
- + * it wants to disassociate itself from its controlling tty.
- + *
- + * It performs the following functions:
- + * (1) Sends a SIGHUP and SIGCONT to the foreground process group
- + * (2) Clears the tty from being controlling the session
- + * (3) Clears the controlling tty for all processes in the
- + * session group.
- + *
- + * The argument on_exit is set to 1 if called when a process is
- + * exiting; it is 0 if called by the ioctl TIOCNOTTY.
- + */
- +void disassociate_ctty(int on_exit)
- +{
- + struct tty_struct *tty = current->tty;
- + struct task_struct *p;
- + int tty_pgrp = -1;
- +
- + if (tty) {
- + tty_pgrp = tty->pgrp;
- + if (on_exit && tty->driver.type != TTY_DRIVER_TYPE_PTY)
- + tty_vhangup(tty);
- + } else {
- + if (current->tty_old_pgrp) {
- + kill_pg(current->tty_old_pgrp, SIGHUP, on_exit);
- + kill_pg(current->tty_old_pgrp, SIGCONT, on_exit);
- + }
- + return;
- + }
- + if (tty_pgrp > 0) {
- + kill_pg(tty_pgrp, SIGHUP, on_exit);
- + if (!on_exit)
- + kill_pg(tty_pgrp, SIGCONT, on_exit);
- + }
- +
- + current->tty_old_pgrp = 0;
- + tty->session = 0;
- + tty->pgrp = -1;
- +
- + for_each_task(p)
- + if (p->session == current->session)
- + p->tty = NULL;
- +}
- +
- +void wait_for_keypress(void)
- +{
- + sleep_on(&keypress_wait);
- +}
- +
- +void stop_tty(struct tty_struct *tty)
- +{
- + if (tty->stopped)
- + return;
- + tty->stopped = 1;
- + if (tty->link && tty->link->packet) {
- + tty->ctrl_status &= ~TIOCPKT_START;
- + tty->ctrl_status |= TIOCPKT_STOP;
- + wake_up_interruptible(&tty->link->read_wait);
- + }
- + if (tty->driver.stop)
- + (tty->driver.stop)(tty);
- +}
- +
- +void start_tty(struct tty_struct *tty)
- +{
- + if (!tty->stopped)
- + return;
- + tty->stopped = 0;
- + if (tty->link && tty->link->packet) {
- + tty->ctrl_status &= ~TIOCPKT_STOP;
- + tty->ctrl_status |= TIOCPKT_START;
- + wake_up_interruptible(&tty->link->read_wait);
- + }
- + if (tty->driver.start)
- + (tty->driver.start)(tty);
- + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- + tty->ldisc.write_wakeup)
- + (tty->ldisc.write_wakeup)(tty);
- + wake_up_interruptible(&tty->write_wait);
- +}
- +
- +static int tty_read(struct inode * inode, struct file * file, char * buf, int count)
- +{
- + int i;
- + struct tty_struct * tty;
- +
- + tty = (struct tty_struct *)file->private_data;
- + if (tty_paranoia_check(tty, inode->i_rdev, "tty_read"))
- + return -EIO;
- + if (!tty || (tty->flags & (1 << TTY_IO_ERROR)))
- + return -EIO;
- +
- + /* This check not only needs to be done before reading, but also
- + whenever read_chan() gets woken up after sleeping, so I've
- + moved it to there. This should only be done for the N_TTY
- + line discipline, anyway. Same goes for write_chan(). -- jlc. */
- +#if 0
- + if ((inode->i_rdev != CONSOLE_DEV) && /* don't stop on /dev/console */
- + (tty->pgrp > 0) &&
- + (current->tty == tty) &&
- + (tty->pgrp != current->pgrp))
- + if (is_ignored(SIGTTIN) || is_orphaned_pgrp(current->pgrp))
- + return -EIO;
- + else {
- + (void) kill_pg(current->pgrp, SIGTTIN, 1);
- + return -ERESTARTSYS;
- + }
- +#endif
- + if (tty->ldisc.read)
- + /* XXX casts are for what kernel-wide prototypes should be. */
- + i = (tty->ldisc.read)(tty,file,(unsigned char *)buf,(unsigned int)count);
- + else
- + i = -EIO;
- + if (i > 0)
- + inode->i_atime = CURRENT_TIME;
- + return i;
- +}
- +
- +/*
- + * Split writes up in sane blocksizes to avoid
- + * denial-of-service type attacks
- + */
- +static inline int do_tty_write(
- + int (*write)(struct tty_struct *, struct file *, const unsigned char *, unsigned int),
- + struct inode *inode,
- + struct tty_struct *tty,
- + struct file *file,
- + const unsigned char *buf,
- + unsigned int count)
- +{
- + int ret = 0, written = 0;
- +
- + for (;;) {
- + unsigned int size = PAGE_SIZE*2;
- + if (size > count)
- + size = count;
- + ret = write(tty, file, buf, size);
- + if (ret <= 0)
- + break;
- + count -= ret;
- + written += ret;
- + if (!count)
- + break;
- + ret = -ERESTARTSYS;
- + if (current->signal & ~current->blocked)
- + break;
- + if (need_resched)
- + schedule();
- + }
- + if (written) {
- + inode->i_mtime = CURRENT_TIME;
- + ret = written;
- + }
- + return ret;
- +}
- +
- +
- +static int tty_write(struct inode * inode, struct file * file, const char * buf, int count)
- +{
- + int is_console;
- + struct tty_struct * tty;
- +
- + is_console = (inode->i_rdev == CONSOLE_DEV);
- +
- + if (is_console && redirect)
- + tty = redirect;
- + else
- + tty = (struct tty_struct *)file->private_data;
- + if (tty_paranoia_check(tty, inode->i_rdev, "tty_write"))
- + return -EIO;
- + if (!tty || !tty->driver.write || (tty->flags & (1 << TTY_IO_ERROR)))
- + return -EIO;
- +#if 0
- + if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) &&
- + (current->tty == tty) && (tty->pgrp != current->pgrp)) {
- + if (is_orphaned_pgrp(current->pgrp))
- + return -EIO;
- + if (!is_ignored(SIGTTOU)) {
- + (void) kill_pg(current->pgrp, SIGTTOU, 1);
- + return -ERESTARTSYS;
- + }
- + }
- +#endif
- + if (!tty->ldisc.write)
- + return -EIO;
- + return do_tty_write(tty->ldisc.write,
- + inode, tty, file,
- + (const unsigned char *)buf,
- + (unsigned int)count);
- +}
- +
- +/*
- + * This is so ripe with races that you should *really* not touch this
- + * unless you know exactly what you are doing. All the changes have to be
- + * made atomically, or there may be incorrect pointers all over the place.
- + */
- +static int init_dev(kdev_t device, struct tty_struct **ret_tty)
- +{
- + struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc;
- + struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
- + struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
- + struct tty_driver *driver;
- + int retval;
- + int idx;
- +
- + driver = get_tty_driver(device);
- + if (!driver)
- + return -ENODEV;
- +
- + idx = MINOR(device) - driver->minor_start;
- + tty = o_tty = NULL;
- + tp = o_tp = NULL;
- + ltp = o_ltp = NULL;
- + o_tty_loc = NULL;
- + o_tp_loc = o_ltp_loc = NULL;
- +
- + tty_loc = &driver->table[idx];
- + tp_loc = &driver->termios[idx];
- + ltp_loc = &driver->termios_locked[idx];
- +
- +repeat:
- + retval = -EIO;
- + if (driver->type == TTY_DRIVER_TYPE_PTY &&
- + driver->subtype == PTY_TYPE_MASTER &&
- + *tty_loc && (*tty_loc)->count)
- + goto end_init;
- + retval = -ENOMEM;
- + if (!*tty_loc && !tty) {
- + if (!(tty = (struct tty_struct*) kmalloc(sizeof(struct tty_struct),GFP_KERNEL)))
- + goto end_init;
- + initialize_tty_struct(tty);
- + tty->device = device;
- + tty->driver = *driver;
- + goto repeat;
- + }
- + if (!*tp_loc && !tp) {
- + tp = (struct termios *) kmalloc(sizeof(struct termios),
- + GFP_KERNEL);
- + if (!tp)
- + goto end_init;
- + *tp = driver->init_termios;
- + goto repeat;
- + }
- + if (!*ltp_loc && !ltp) {
- + ltp = (struct termios *) kmalloc(sizeof(struct termios),
- + GFP_KERNEL);
- + if (!ltp)
- + goto end_init;
- + memset(ltp, 0, sizeof(struct termios));
- + goto repeat;
- + }
- + if (driver->type == TTY_DRIVER_TYPE_PTY) {
- + o_tty_loc = &driver->other->table[idx];
- + o_tp_loc = &driver->other->termios[idx];
- + o_ltp_loc = &driver->other->termios_locked[idx];
- +
- + if (!*o_tty_loc && !o_tty) {
- + kdev_t o_device;
- +
- + o_tty = (struct tty_struct *) kmalloc (sizeof(struct tty_struct),
- + GFP_KERNEL);
- + if (!o_tty)
- + goto end_init;
- + o_device = MKDEV(driver->other->major,
- + driver->other->minor_start + idx);
- + initialize_tty_struct(o_tty);
- + o_tty->device = o_device;
- + o_tty->driver = *driver->other;
- + goto repeat;
- + }
- + if (!*o_tp_loc && !o_tp) {
- + o_tp = (struct termios *)
- + kmalloc(sizeof(struct termios), GFP_KERNEL);
- + if (!o_tp)
- + goto end_init;
- + *o_tp = driver->other->init_termios;
- + goto repeat;
- + }
- + if (!*o_ltp_loc && !o_ltp) {
- + o_ltp = (struct termios *)
- + kmalloc(sizeof(struct termios), GFP_KERNEL);
- + if (!o_ltp)
- + goto end_init;
- + memset(o_ltp, 0, sizeof(struct termios));
- + goto repeat;
- + }
- +
- + }
- + /* Now we have allocated all the structures: update all the pointers.. */
- + if (!*tp_loc) {
- + *tp_loc = tp;
- + tp = NULL;
- + }
- + if (!*ltp_loc) {
- + *ltp_loc = ltp;
- + ltp = NULL;
- + }
- + if (!*tty_loc) {
- + tty->termios = *tp_loc;
- + tty->termios_locked = *ltp_loc;
- + *tty_loc = tty;
- + (*driver->refcount)++;
- + (*tty_loc)->count++;
- + if (tty->ldisc.open) {
- + retval = (tty->ldisc.open)(tty);
- + if (retval < 0) {
- + (*tty_loc)->count--;
- + tty = NULL;
- + goto end_init;
- + }
- + }
- + tty = NULL;
- + } else {
- + if ((*tty_loc)->flags & (1 << TTY_CLOSING)) {
- + printk("Attempt to open closing tty %s.\n",
- + tty_name(*tty_loc));
- + printk("Ack!!!! This should never happen!!\n");
- + return -EINVAL;
- + }
- + (*tty_loc)->count++;
- + }
- + if (driver->type == TTY_DRIVER_TYPE_PTY) {
- + if (!*o_tp_loc) {
- + *o_tp_loc = o_tp;
- + o_tp = NULL;
- + }
- + if (!*o_ltp_loc) {
- + *o_ltp_loc = o_ltp;
- + o_ltp = NULL;
- + }
- + if (!*o_tty_loc) {
- + o_tty->termios = *o_tp_loc;
- + o_tty->termios_locked = *o_ltp_loc;
- + *o_tty_loc = o_tty;
- + (*driver->other->refcount)++;
- + if (o_tty->ldisc.open) {
- + retval = (o_tty->ldisc.open)(o_tty);
- + if (retval < 0) {
- + (*tty_loc)->count--;
- + o_tty = NULL;
- + goto end_init;
- + }
- + }
- + o_tty = NULL;
- + }
- + (*tty_loc)->link = *o_tty_loc;
- + (*o_tty_loc)->link = *tty_loc;
- + if (driver->subtype == PTY_TYPE_MASTER)
- + (*o_tty_loc)->count++;
- + }
- + (*tty_loc)->driver = *driver;
- + *ret_tty = *tty_loc;
- + retval = 0;
- +end_init:
- + if (tty)
- + kfree_s(tty, sizeof(struct tty_struct));
- + if (o_tty)
- + kfree_s(o_tty, sizeof(struct tty_struct));
- + if (tp)
- + kfree_s(tp, sizeof(struct termios));
- + if (o_tp)
- + kfree_s(o_tp, sizeof(struct termios));
- + if (ltp)
- + kfree_s(ltp, sizeof(struct termios));
- + if (o_ltp)
- + kfree_s(o_ltp, sizeof(struct termios));
- + return retval;
- +}
- +
- +/*
- + * Even releasing the tty structures is a tricky business.. We have
- + * to be very careful that the structures are all released at the
- + * same time, as interrupts might otherwise get the wrong pointers.
- + */
- +static void release_dev(struct file * filp)
- +{
- + struct tty_struct *tty, *o_tty;
- + struct termios *tp, *o_tp, *ltp, *o_ltp;
- + struct task_struct **p;
- + int idx;
- +
- + tty = (struct tty_struct *)filp->private_data;
- + if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "release_dev"))
- + return;
- +
- + check_tty_count(tty, "release_dev");
- +
- + tty_fasync(filp->f_inode, filp, 0);
- +
- + tp = tty->termios;
- + ltp = tty->termios_locked;
- +
- + idx = MINOR(tty->device) - tty->driver.minor_start;
- +#ifdef TTY_PARANOIA_CHECK
- + if (idx < 0 || idx >= tty->driver.num) {
- + printk("release_dev: bad idx when trying to free (%s)\n",
- + kdevname(tty->device));
- + return;
- + }
- + if (tty != tty->driver.table[idx]) {
- + printk("release_dev: driver.table[%d] not tty for (%s)\n",
- + idx, kdevname(tty->device));
- + return;
- + }
- + if (tp != tty->driver.termios[idx]) {
- + printk("release_dev: driver.termios[%d] not termios for ("
- + "%s)\n",
- + idx, kdevname(tty->device));
- + return;
- + }
- + if (ltp != tty->driver.termios_locked[idx]) {
- + printk("release_dev: driver.termios_locked[%d] not termios_locked for ("
- + "%s)\n",
- + idx, kdevname(tty->device));
- + return;
- + }
- +#endif
- +
- +#ifdef TTY_DEBUG_HANGUP
- + printk("release_dev of %s (tty count=%d)...", tty_name(tty),
- + tty->count);
- +#endif
- +
- + o_tty = tty->link;
- + o_tp = (o_tty) ? o_tty->termios : NULL;
- + o_ltp = (o_tty) ? o_tty->termios_locked : NULL;
- +
- +#ifdef TTY_PARANOIA_CHECK
- + if (tty->driver.other) {
- + if (o_tty != tty->driver.other->table[idx]) {
- + printk("release_dev: other->table[%d] not o_tty for ("
- + "%s)\n",
- + idx, kdevname(tty->device));
- + return;
- + }
- + if (o_tp != tty->driver.other->termios[idx]) {
- + printk("release_dev: other->termios[%d] not o_termios for ("
- + "%s)\n",
- + idx, kdevname(tty->device));
- + return;
- + }
- + if (o_ltp != tty->driver.other->termios_locked[idx]) {
- + printk("release_dev: other->termios_locked[%d] not o_termios_locked for ("
- + "%s)\n",
- + idx, kdevname(tty->device));
- + return;
- + }
- +
- + if (o_tty->link != tty) {
- + printk("release_dev: bad pty pointers\n");
- + return;
- + }
- + }
- +#endif
- +
- + if (tty->driver.close)
- + tty->driver.close(tty, filp);
- + if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
- + tty->driver.subtype == PTY_TYPE_MASTER) {
- + if (--tty->link->count < 0) {
- + printk("release_dev: bad pty slave count (%d) for %s\n",
- + tty->count, tty_name(tty));
- + tty->link->count = 0;
- + }
- + }
- + if (--tty->count < 0) {
- + printk("release_dev: bad tty->count (%d) for %s\n",
- + tty->count, tty_name(tty));
- + tty->count = 0;
- + }
- + if (tty->count)
- + return;
- +
- + /*
- + * We're committed; at this point, we must not block!
- + */
- + if (o_tty) {
- + if (o_tty->count)
- + return;
- + tty->driver.other->table[idx] = NULL;
- + tty->driver.other->termios[idx] = NULL;
- + kfree_s(o_tp, sizeof(struct termios));
- + }
- +
- +#ifdef TTY_DEBUG_HANGUP
- + printk("freeing tty structure...");
- +#endif
- + tty->flags |= (1 << TTY_CLOSING);
- +
- + /*
- + * Make sure there aren't any processes that still think this
- + * tty is their controlling tty.
- + */
- + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
- + if (*p == 0)
- + continue;
- + if ((*p)->tty == tty)
- + (*p)->tty = NULL;
- + if (o_tty && (*p)->tty == o_tty)
- + (*p)->tty = NULL;
- + }
- +
- + /*
- + * Shutdown the current line discipline, and reset it to
- + * N_TTY.
- + */
- + if (tty->ldisc.close)
- + (tty->ldisc.close)(tty);
- + tty->ldisc = ldiscs[N_TTY];
- + tty->termios->c_line = N_TTY;
- + if (o_tty) {
- + if (o_tty->ldisc.close)
- + (o_tty->ldisc.close)(o_tty);
- + o_tty->ldisc = ldiscs[N_TTY];
- + }
- +
- + tty->driver.table[idx] = NULL;
- + if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
- + tty->driver.termios[idx] = NULL;
- + kfree_s(tp, sizeof(struct termios));
- + }
- + if (tty == redirect || o_tty == redirect)
- + redirect = NULL;
- + /*
- + * Make sure that the tty's task queue isn't activated. If it
- + * is, take it out of the linked list.
- + */
- + cli();
- + if (tty->flip.tqueue.sync) {
- + struct tq_struct *tq, *prev;
- +
- + for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
- + if (tq == &tty->flip.tqueue) {
- + if (prev)
- + prev->next = tq->next;
- + else
- + tq_timer = tq->next;
- + break;
- + }
- + }
- + }
- + sti();
- + tty->magic = 0;
- + (*tty->driver.refcount)--;
- + kfree_s(tty, sizeof(struct tty_struct));
- + filp->private_data = 0;
- + if (o_tty) {
- + o_tty->magic = 0;
- + (*o_tty->driver.refcount)--;
- + kfree_s(o_tty, sizeof(struct tty_struct));
- + }
- +}
- +
- +/*
- + * tty_open and tty_release keep up the tty count that contains the
- + * number of opens done on a tty. We cannot use the inode-count, as
- + * different inodes might point to the same tty.
- + *
- + * Open-counting is needed for pty masters, as well as for keeping
- + * track of serial lines: DTR is dropped when the last close happens.
- + * (This is not done solely through tty->count, now. - Ted 1/27/92)
- + *
- + * The termios state of a pty is reset on first open so that
- + * settings don't persist across reuse.
- + */
- +static int tty_open(struct inode * inode, struct file * filp)
- +{
- + struct tty_struct *tty;
- + int minor;
- + int noctty, retval;
- + kdev_t device;
- +
- +retry_open:
- + noctty = filp->f_flags & O_NOCTTY;
- + device = inode->i_rdev;
- + if (device == TTY_DEV) {
- + if (!current->tty)
- + return -ENXIO;
- + device = current->tty->device;
- + /* noctty = 1; */
- + }
- + if (device == CONSOLE_DEV) {
- + device = MKDEV(TTY_MAJOR, vtdata.fgconsole->num);
- + noctty = 1;
- + }
- + minor = MINOR(device);
- +
- + retval = init_dev(device, &tty);
- + if (retval)
- + return retval;
- + filp->private_data = tty;
- + check_tty_count(tty, "tty_open");
- + if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
- + tty->driver.subtype == PTY_TYPE_MASTER)
- + noctty = 1;
- +#ifdef TTY_DEBUG_HANGUP
- + printk("opening %s...", tty_name(tty));
- +#endif
- + if (tty->driver.open)
- + retval = tty->driver.open(tty, filp);
- + else
- + retval = -ENODEV;
- +
- + if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
- + retval = -EBUSY;
- +
- + if (retval) {
- +#ifdef TTY_DEBUG_HANGUP
- + printk("error %d in opening %s...", retval, tty_name(tty));
- +#endif
- +
- + release_dev(filp);
- + if (retval != -ERESTARTSYS)
- + return retval;
- + if (current->signal & ~current->blocked)
- + return retval;
- + schedule();
- + /*
- + * Need to reset f_op in case a hangup happened.
- + */
- + filp->f_op = &tty_fops;
- + goto retry_open;
- + }
- + if (!noctty &&
- + current->leader &&
- + !current->tty &&
- + tty->session == 0) {
- + current->tty = tty;
- + current->tty_old_pgrp = 0;
- + tty->session = current->session;
- + tty->pgrp = current->pgrp;
- + }
- + return 0;
- +}
- +
- +/*
- + * Note that releasing a pty master also releases the child, so
- + * we have to make the redirection checks after that and on both
- + * sides of a pty.
- + */
- +static void tty_release(struct inode * inode, struct file * filp)
- +{
- + release_dev(filp);
- +}
- +
- +static int tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
- +{
- + struct tty_struct * tty;
- +
- + tty = (struct tty_struct *)filp->private_data;
- + if (tty_paranoia_check(tty, inode->i_rdev, "tty_select"))
- + return 0;
- +
- + if (tty->ldisc.select)
- + return (tty->ldisc.select)(tty, inode, filp, sel_type, wait);
- + return 0;
- +}
- +
- +/*
- + * fasync_helper() is used by some character device drivers (mainly mice)
- + * to set up the fasync queue. It returns negative on error, 0 if it did
- + * no changes and positive if it added/deleted the entry.
- + */
- +int fasync_helper(struct inode * inode, struct file * filp, int on, struct fasync_struct **fapp)
- +{
- + struct fasync_struct *fa, **fp;
- + unsigned long flags;
- +
- + for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
- + if (fa->fa_file == filp)
- + break;
- + }
- +
- + if (on) {
- + if (fa)
- + return 0;
- + fa = (struct fasync_struct *)kmalloc(sizeof(struct fasync_struct), GFP_KERNEL);
- + if (!fa)
- + return -ENOMEM;
- + fa->magic = FASYNC_MAGIC;
- + fa->fa_file = filp;
- + save_flags_cli (flags);
- + fa->fa_next = *fapp;
- + *fapp = fa;
- + restore_flags(flags);
- + return 1;
- + }
- + if (!fa)
- + return 0;
- + save_flags_cli (flags);
- + *fp = fa->fa_next;
- + restore_flags(flags);
- + kfree(fa);
- + return 1;
- +}
- +
- +static int tty_fasync(struct inode * inode, struct file * filp, int on)
- +{
- + struct tty_struct * tty;
- + int retval;
- +
- + tty = (struct tty_struct *)filp->private_data;
- + if (tty_paranoia_check(tty, inode->i_rdev, "tty_fasync"))
- + return 0;
- +
- + retval = fasync_helper(inode, filp, on, &tty->fasync);
- + if (retval <= 0)
- + return retval;
- +
- + if (on) {
- + if (!waitqueue_active(&tty->read_wait))
- + tty->minimum_to_wake = 1;
- + if (filp->f_owner == 0) {
- + if (tty->pgrp)
- + filp->f_owner = -tty->pgrp;
- + else
- + filp->f_owner = current->pid;
- + }
- + } else {
- + if (!tty->fasync && !waitqueue_active(&tty->read_wait))
- + tty->minimum_to_wake = N_TTY_BUF_SIZE;
- + }
- + return 0;
- +}
- +
- +#if 0
- +/*
- + * XXX does anyone use this anymore?!?
- + */
- +static int do_get_ps_info(unsigned long arg)
- +{
- + struct tstruct {
- + int flag;
- + int present[NR_TASKS];
- + struct task_struct tasks[NR_TASKS];
- + };
- + struct tstruct *ts = (struct tstruct *)arg;
- + struct task_struct **p;
- + char *c, *d;
- + int i, n = 0;
- +
- + i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct));
- + if (i)
- + return i;
- + for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++)
- + if (*p)
- + {
- + c = (char *)(*p);
- + d = (char *)(ts->tasks+n);
- + for (i=0 ; i<sizeof(struct task_struct) ; i++)
- + put_user(*c++, d++);
- + put_user(1, ts->present+n);
- + }
- + else
- + put_user(0, ts->present+n);
- + return(0);
- +}
- +#endif
- +
- +static int tty_ioctl(struct inode * inode, struct file * file,
- + unsigned int cmd, unsigned long arg)
- +{
- + int retval;
- + struct tty_struct * tty;
- + struct tty_struct * real_tty;
- + struct winsize tmp_ws;
- + pid_t pgrp;
- + unsigned char ch;
- + char mbz = 0;
- +
- + tty = (struct tty_struct *)file->private_data;
- + if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
- + return -EINVAL;
- +
- + if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
- + tty->driver.subtype == PTY_TYPE_MASTER)
- + real_tty = tty->link;
- + else
- + real_tty = tty;
- +
- + switch (cmd) {
- + case TIOCSTI:
- + if ((current->tty != tty) && !suser())
- + return -EPERM;
- + retval = verify_area(VERIFY_READ, (void *) arg, 1);
- + if (retval)
- + return retval;
- + ch = get_user((char *) arg);
- + tty->ldisc.receive_buf(tty, &ch, &mbz, 1);
- + return 0;
- + case TIOCGWINSZ:
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof (struct winsize));
- + if (retval)
- + return retval;
- + memcpy_tofs((struct winsize *) arg, &tty->winsize,
- + sizeof (struct winsize));
- + return 0;
- + case TIOCSWINSZ:
- + retval = verify_area(VERIFY_READ, (void *) arg,
- + sizeof (struct winsize));
- + if (retval)
- + return retval;
- + memcpy_fromfs(&tmp_ws, (struct winsize *) arg,
- + sizeof (struct winsize));
- + if (memcmp(&tmp_ws, &tty->winsize,
- + sizeof(struct winsize))) {
- + if (tty->pgrp > 0)
- + kill_pg(tty->pgrp, SIGWINCH, 1);
- + if ((real_tty->pgrp != tty->pgrp) &&
- + (real_tty->pgrp > 0))
- + kill_pg(real_tty->pgrp, SIGWINCH, 1);
- + }
- + tty->winsize = tmp_ws;
- + real_tty->winsize = tmp_ws;
- + return 0;
- + case TIOCCONS:
- + if (tty->driver.type == TTY_DRIVER_TYPE_CONSOLE) {
- + if (!suser())
- + return -EPERM;
- + redirect = NULL;
- + return 0;
- + }
- + if (redirect)
- + return -EBUSY;
- + redirect = real_tty;
- + return 0;
- + case FIONBIO:
- + retval = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
- + if (retval)
- + return retval;
- + arg = get_user((unsigned int *) arg);
- + if (arg)
- + file->f_flags |= O_NONBLOCK;
- + else
- + file->f_flags &= ~O_NONBLOCK;
- + return 0;
- + case TIOCEXCL:
- + set_bit(TTY_EXCLUSIVE, &tty->flags);
- + return 0;
- + case TIOCNXCL:
- + clear_bit(TTY_EXCLUSIVE, &tty->flags);
- + return 0;
- + case TIOCNOTTY:
- + if (current->tty != tty)
- + return -ENOTTY;
- + if (current->leader)
- + disassociate_ctty(0);
- + current->tty = NULL;
- + return 0;
- + case TIOCSCTTY:
- + if (current->leader &&
- + (current->session == tty->session))
- + return 0;
- + /*
- + * The process must be a session leader and
- + * not have a controlling tty already.
- + */
- + if (!current->leader || current->tty)
- + return -EPERM;
- + if (tty->session > 0) {
- + /*
- + * This tty is already the controlling
- + * tty for another session group!
- + */
- + if ((arg == 1) && suser()) {
- + /*
- + * Steal it away
- + */
- + struct task_struct *p;
- +
- + for_each_task(p)
- + if (p->tty == tty)
- + p->tty = NULL;
- + } else
- + return -EPERM;
- + }
- + current->tty = tty;
- + current->tty_old_pgrp = 0;
- + tty->session = current->session;
- + tty->pgrp = current->pgrp;
- + return 0;
- + case TIOCGPGRP:
- + /*
- + * (tty == real_tty) is a cheap way of
- + * testing if the tty is NOT a master pty.
- + */
- + if (tty == real_tty && current->tty != real_tty)
- + return -ENOTTY;
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof (pid_t));
- + if (retval)
- + return retval;
- + put_user(real_tty->pgrp, (pid_t *) arg);
- + return 0;
- + case TIOCSPGRP:
- + retval = tty_check_change(real_tty);
- + if (retval == -EIO)
- + return -ENOTTY;
- + if (retval)
- + return retval;
- + if (!current->tty ||
- + (current->tty != real_tty) ||
- + (real_tty->session != current->session))
- + return -ENOTTY;
- + pgrp = get_user((pid_t *) arg);
- + if (pgrp < 0)
- + return -EINVAL;
- + if (session_of_pgrp(pgrp) != current->session)
- + return -EPERM;
- + real_tty->pgrp = pgrp;
- + return 0;
- + case TIOCGETD:
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof (int));
- + if (retval)
- + return retval;
- + put_user(tty->ldisc.num, (int *) arg);
- + return 0;
- + case TIOCSETD:
- + retval = tty_check_change(tty);
- + if (retval)
- + return retval;
- + retval = verify_area(VERIFY_READ, (void *) arg,
- + sizeof (int));
- + if (retval)
- + return retval;
- + arg = get_user((int *) arg);
- + return tty_set_ldisc(tty, arg);
- + case TIOCLINUX:
- + if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
- + return -EINVAL;
- + if (current->tty != tty && !suser())
- + return -EPERM;
- + retval = verify_area(VERIFY_READ, (void *) arg, 1);
- + if (retval)
- + return retval;
- + switch (retval = get_user((char *)arg))
- + {
- + case 0:
- + case 8:
- + case 9:
- + printk("TIOCLINUX (0/8/9) ioctl is gone - use /dev/vcs\n");
- + return -EINVAL;
- +#if 0
- + case 1:
- + printk("Deprecated TIOCLINUX (1) ioctl\n");
- + return do_get_ps_info(arg);
- +#endif
- + case 2:
- + return set_selection(arg, tty);
- + case 3:
- + return paste_selection(tty);
- + case 4:
- + vt_do_unblankscreen ();
- + return 0;
- + case 5:
- + return sel_loadlut(arg);
- + case 6:
- + /*
- + * Make it possible to react to Shift+Mousebutton.
- + * Note that 'shift_state' is an undocumented
- + * kernel-internal variable; programs not closely
- + * related to the kernel should not use this.
- + */
- + retval = verify_area(VERIFY_WRITE, (void *) arg, 1);
- + if (retval)
- + return retval;
- + put_user(shift_state,(char *) arg);
- + return 0;
- + case 7:
- + retval = verify_area(VERIFY_WRITE, (void *) arg, 1);
- + if (retval)
- + return retval;
- + put_user(mouse_reporting(),(char *) arg);
- + return 0;
- +/* case 10:
- + set_vesa_blanking(arg);
- + return 0;*/
- + case 11: /* set kmsg redirect */
- + if (!suser())
- + return -EPERM;
- + retval = verify_area(VERIFY_READ,
- + (void *) arg+1, 1);
- + if (retval)
- + return retval;
- + kmsg_redirect = get_user((char *)arg+1);
- + return 0;
- + case 12: /* get fg console */
- + return vtdata.fgconsole->num;
- + default:
- + return -EINVAL;
- + }
- +
- + case TIOCTTYGSTRUCT:
- + retval = verify_area(VERIFY_WRITE, (void *) arg,
- + sizeof(struct tty_struct));
- + if (retval)
- + return retval;
- + memcpy_tofs((struct tty_struct *) arg,
- + tty, sizeof(struct tty_struct));
- + return 0;
- + default:
- + if (tty->driver.ioctl) {
- + retval = (tty->driver.ioctl)(tty, file,
- + cmd, arg);
- + if (retval != -ENOIOCTLCMD)
- + return retval;
- + }
- + if (tty->ldisc.ioctl) {
- + retval = (tty->ldisc.ioctl)(tty, file,
- + cmd, arg);
- + if (retval != -ENOIOCTLCMD)
- + return retval;
- + }
- + return -EINVAL;
- + }
- +}
- +
- +
- +/*
- + * This implements the "Secure Attention Key" --- the idea is to
- + * prevent trojan horses by killing all processes associated with this
- + * tty when the user hits the "Secure Attention Key". Required for
- + * super-paranoid applications --- see the Orange Book for more details.
- + *
- + * This code could be nicer; ideally it should send a HUP, wait a few
- + * seconds, then send a INT, and then a KILL signal. But you then
- + * have to coordinate with the init process, since all processes associated
- + * with the current tty must be dead before the new getty is allowed
- + * to spawn.
- + */
- +void do_SAK( struct tty_struct *tty)
- +{
- +#ifdef TTY_SOFT_SAK
- + tty_hangup(tty);
- +#else
- + struct task_struct **p;
- + int session;
- + int i;
- + struct file *filp;
- +
- + if (!tty)
- + return;
- + session = tty->session;
- + if (tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer(tty);
- + if (tty->driver.flush_buffer)
- + tty->driver.flush_buffer(tty);
- + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
- + if (!(*p))
- + continue;
- + if (((*p)->tty == tty) ||
- + ((session > 0) && ((*p)->session == session)))
- + send_sig(SIGKILL, *p, 1);
- + else {
- + for (i=0; i < NR_OPEN; i++) {
- + filp = (*p)->files->fd[i];
- + if (filp && (filp->f_op == &tty_fops) &&
- + (filp->private_data == tty)) {
- + send_sig(SIGKILL, *p, 1);
- + break;
- + }
- + }
- + }
- + }
- +#endif
- +}
- +
- +/*
- + * This routine is called out of the software interrupt to flush data
- + * from the flip buffer to the line discipline.
- + */
- +static void flush_to_ldisc(void *private_)
- +{
- + struct tty_struct *tty = (struct tty_struct *) private_;
- + unsigned char *cp;
- + char *fp;
- + int count;
- +
- + if (tty->flip.buf_num) {
- + cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
- + fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
- + tty->flip.buf_num = 0;
- +
- + cli();
- + tty->flip.char_buf_ptr = tty->flip.char_buf;
- + tty->flip.flag_buf_ptr = tty->flip.flag_buf;
- + } else {
- + cp = tty->flip.char_buf;
- + fp = tty->flip.flag_buf;
- + tty->flip.buf_num = 1;
- +
- + cli();
- + tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
- + tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
- + }
- + count = tty->flip.count;
- + tty->flip.count = 0;
- + sti();
- +
- +#if 0
- + if (count > tty->max_flip_cnt)
- + tty->max_flip_cnt = count;
- +#endif
- + tty->ldisc.receive_buf(tty, cp, fp, count);
- +}
- +
- +/*
- + * This subroutine initializes a tty structure.
- + */
- +static void initialize_tty_struct(struct tty_struct *tty)
- +{
- + memset(tty, 0, sizeof(struct tty_struct));
- + tty->magic = TTY_MAGIC;
- + tty->ldisc = ldiscs[N_TTY];
- + tty->pgrp = -1;
- + tty->flip.char_buf_ptr = tty->flip.char_buf;
- + tty->flip.flag_buf_ptr = tty->flip.flag_buf;
- + tty->flip.tqueue.routine = flush_to_ldisc;
- + tty->flip.tqueue.data = tty;
- +}
- +
- +/*
- + * The default put_char routine if the driver did not define one.
- + */
- +void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
- +{
- + tty->driver.write(tty, 0, &ch, 1);
- +}
- +
- +/*
- + * Called by a tty driver to register itself.
- + */
- +int tty_register_driver(struct tty_driver *driver)
- +{
- + int error;
- +
- + if (driver->flags & TTY_DRIVER_INSTALLED)
- + return 0;
- +
- + error = register_chrdev(driver->major, driver->name, &tty_fops);
- + if (error < 0)
- + return error;
- + else if(driver->major == 0)
- + driver->major = error;
- +
- + if (!driver->put_char)
- + driver->put_char = tty_default_put_char;
- +
- + driver->prev = 0;
- + driver->next = tty_drivers;
- + if (tty_drivers) tty_drivers->prev = driver;
- + tty_drivers = driver;
- + return error;
- +}
- +
- +/*
- + * Called by a tty driver to unregister itself.
- + */
- +int tty_unregister_driver(struct tty_driver *driver)
- +{
- + int retval;
- + struct tty_driver *p;
- + int found = 0;
- + const char *othername = NULL;
- +
- + if (*driver->refcount)
- + return -EBUSY;
- +
- + for (p = tty_drivers; p; p = p->next) {
- + if (p == driver)
- + found++;
- + else if (p->major == driver->major)
- + othername = p->name;
- + }
- +
- + if (!found)
- + return -ENOENT;
- +
- + if (othername == NULL) {
- + retval = unregister_chrdev(driver->major, driver->name);
- + if (retval)
- + return retval;
- + } else
- + register_chrdev(driver->major, othername, &tty_fops);
- +
- + if (driver->prev)
- + driver->prev->next = driver->next;
- + else
- + tty_drivers = driver->next;
- +
- + if (driver->next)
- + driver->next->prev = driver->prev;
- +
- + return 0;
- +}
- +
- +
- +/*
- + * Initialize the console device. This is called *early*, so
- + * we can't necessarily depend on lots of kernel help here.
- + * Just do some early initializations, and do the complex setup
- + * later.
- + */
- +long console_init(long kmem_start, long kmem_end)
- +{
- + /* Setup the default TTY line discipline. */
- + memset(ldiscs, 0, sizeof(ldiscs));
- + (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
- +
- + /*
- + * Set up the standard termios. Individual tty drivers may
- + * deviate from this; this is used as a template.
- + */
- + memset(&tty_std_termios, 0, sizeof(struct termios));
- + memcpy(tty_std_termios.c_cc, INIT_C_CC, NCCS);
- + tty_std_termios.c_iflag = ICRNL | IXON;
- + tty_std_termios.c_oflag = OPOST | ONLCR;
- + tty_std_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL;
- + tty_std_termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
- + ECHOCTL | ECHOKE | IEXTEN;
- +
- + /*
- + * set up the console device so that later boot sequences can
- + * inform about problems etc..
- + */
- + return vt_pre_init(kmem_start);
- +}
- +
- +static struct tty_driver dev_tty_driver, dev_console_driver;
- +
- +/*
- + * Ok, now we can initialize the rest of the tty devices and can count
- + * on memory allocations, interrupts etc..
- + */
- +int tty_init(void)
- +{
- + if (sizeof(struct tty_struct) > PAGE_SIZE)
- + panic("size of tty structure > PAGE_SIZE!");
- + vt_post_init ();
- +
- + /*
- + * dev_tty_driver and dev_console_driver are actually magic
- + * devices which get redirected at open time. Nevertheless,
- + * we register them so that register_chrdev is called
- + * appropriately.
- + */
- + memset(&dev_tty_driver, 0, sizeof(struct tty_driver));
- + dev_tty_driver.magic = TTY_DRIVER_MAGIC;
- + dev_tty_driver.name = "tty";
- + dev_tty_driver.name_base = 0;
- + dev_tty_driver.major = TTY_MAJOR;
- + dev_tty_driver.minor_start = 0;
- + dev_tty_driver.num = 1;
- +
- + if (tty_register_driver(&dev_tty_driver))
- + panic("Couldn't register /dev/tty driver\n");
- +
- + dev_console_driver = dev_tty_driver;
- + dev_console_driver.name = "console";
- + dev_console_driver.major = TTYAUX_MAJOR;
- +
- + if (tty_register_driver(&dev_console_driver))
- + panic("Couldn't register /dev/console driver\n");
- +
- +#ifdef CONFIG_SERIAL
- + rs_init();
- +#endif
- + pty_init();
- + vcs_init();
- + return 0;
- +}
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/uni_hash.tbl linux/arch/arm/drivers/char/uni_hash.tbl
- --- linux.orig/arch/arm/drivers/char/uni_hash.tbl Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/uni_hash.tbl Thu Jul 18 23:41:22 1996
- @@ -0,0 +1,89 @@
- +/*
- + * uni_hash.tbl
- + *
- + * Do not edit this file; it was automatically generated by
- + *
- + * conmakehash cp437.uni > uni_hash.tbl
- + *
- + */
- +
- +#include <linux/types.h>
- +#include <linux/kd.h>
- +
- +static u8 dfont_unicount[256] =
- +{
- + 1, 1, 1, 1, 2, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 2, 2, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 2, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 2, 2, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 5, 1, 2, 1, 4, 1, 1,
- + 1, 5, 1, 2, 1, 1, 1, 5,
- + 1, 1, 2, 1, 1, 4, 1, 1,
- + 1, 2, 1, 1, 1, 1, 1, 2,
- + 1, 2, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 2,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 2, 2, 1, 1, 2, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 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,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 2, 1, 1, 1, 1, 2, 1,
- + 2, 1, 2, 1, 1, 2, 1, 1,
- + 1, 1, 1, 1, 1, 1, 1, 1,
- + 1, 1, 1, 1, 1, 1, 2, 1
- +};
- +
- +static u16 dfont_unitable[297] =
- +{
- + 0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x25c6, 0x2663, 0x2660,
- + 0x2022, 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b,
- + 0x263c, 0x25b6, 0x25ba, 0x25c0, 0x25c4, 0x2195, 0x203c, 0x00b6,
- + 0x00a7, 0x25ac, 0x21a8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221f,
- + 0x2194, 0x25b2, 0x25bc, 0x0020, 0x0021, 0x0022, 0x00a8, 0x0023,
- + 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b,
- + 0x002c, 0x00b8, 0x002d, 0x00ad, 0x002e, 0x002f, 0x0030, 0x0031,
- + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039,
- + 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041,
- + 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x0042, 0x0043, 0x00a9, 0x0044,
- + 0x0045, 0x00c8, 0x00ca, 0x00cb, 0x0046, 0x0047, 0x0048, 0x0049,
- + 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x004a, 0x004b, 0x212a, 0x004c,
- + 0x004d, 0x004e, 0x004f, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x0050,
- + 0x0051, 0x0052, 0x00ae, 0x0053, 0x0054, 0x0055, 0x00d9, 0x00da,
- + 0x00db, 0x0056, 0x0057, 0x0058, 0x0059, 0x00dd, 0x005a, 0x005b,
- + 0x005c, 0x005d, 0x005e, 0x005f, 0xf804, 0x0060, 0x0061, 0x00e3,
- + 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069,
- + 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x00f5, 0x0070,
- + 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
- + 0x00d7, 0x0079, 0x00fd, 0x007a, 0x007b, 0x007c, 0x00a5, 0x007d,
- + 0x007e, 0x2302, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0,
- + 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec,
- + 0x00c4, 0x00c5, 0x212b, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6,
- + 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3,
- + 0x00a5, 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1,
- + 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc,
- + 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524,
- + 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d,
- + 0x255c, 0x255b, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500,
- + 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560,
- + 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558,
- + 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584,
- + 0x258c, 0x2590, 0x2580, 0x03b1, 0x03b2, 0x00df, 0x0393, 0x03c0,
- + 0x03a3, 0x03c3, 0x00b5, 0x03bc, 0x03c4, 0x03a6, 0x00d8, 0x0398,
- + 0x03a9, 0x2126, 0x03b4, 0x221e, 0x03c6, 0x00f8, 0x03b5, 0x2229,
- + 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
- + 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0xfffd,
- + 0x00a0
- +};
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/vc_screen.c linux/arch/arm/drivers/char/vc_screen.c
- --- linux.orig/arch/arm/drivers/char/vc_screen.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/vc_screen.c Fri May 10 23:47:14 1996
- @@ -0,0 +1,268 @@
- +/*
- + * linux/arch/arm/drivers/char/vc_screen.c
- + *
- + * Provide access to virtual console memory.
- + * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
- + * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
- + * [minor: N]
- + *
- + * /dev/vcsaN: idem, but including attributes, and prefixed with
- + * the 4 bytes lines,columns,x,y (as screendump used to give)
- + * [minor: N+128]
- + *
- + * This replaces screendump and part of selection, so that the system
- + * administrator can control access using file system permissions.
- + *
- + * aeb@cwi.nl - efter Friedas begravelse - 950211
- + *
- + * Modified by Russell King (01/01/96) [experimental + in development]
- + */
- +
- +#include <linux/kernel.h>
- +#include <linux/major.h>
- +#include <linux/errno.h>
- +#include <linux/tty.h>
- +#include <linux/fs.h>
- +#include <asm/segment.h>
- +
- +#include "vt_kern.h"
- +#include "selection.h"
- +
- +#define HEADER_SIZE 4
- +
- +static inline int vcs_size (struct inode *inode)
- +{
- + int size = vtdata.numrows * vtdata.numcolumns;
- +
- + if (MINOR(inode->i_rdev) & 128)
- + size = sizeof (unsigned long) * size + HEADER_SIZE;
- + return size;
- +}
- +
- +static int vcs_lseek (struct inode *inode, struct file *file, off_t offset, int orig)
- +{
- + int size = vcs_size(inode);
- +
- + switch (orig) {
- + case 0:
- + file->f_pos = offset;
- + break;
- + case 1:
- + file->f_pos += offset;
- + break;
- + case 2:
- + file->f_pos = size + offset;
- + break;
- + default:
- + return -EINVAL;
- + }
- + if (file->f_pos < 0 || file->f_pos > size)
- + return -EINVAL;
- + return file->f_pos;
- +}
- +
- +static int vcs_read (struct inode *inode, struct file *file, char *buf, int count)
- +{
- + struct vt *vt;
- + unsigned long p = file->f_pos;
- + unsigned int cons = MINOR(inode->i_rdev);
- + int attr, size, read;
- + char *buf0;
- + unsigned long *org, d;
- +
- + attr = (cons & 128);
- + cons = (cons & 127);
- +
- + if (cons == 0)
- + vt = vtdata.fgconsole;
- + else
- + vt = vt_con_data + (cons - 1);
- +
- + if (!vt_allocated (vt))
- + return -ENXIO;
- +
- + size = vcs_size(inode);
- + if (count < 0 || p > size)
- + return -EINVAL;
- +
- + if (count > size - p)
- + count = size - p;
- +
- + buf0 = buf;
- + if (!attr) {
- + org = screen_pos (vt, p);
- + while (count-- > 0)
- + put_user (*org++ & 0xff, buf++);
- + } else {
- + if (p < HEADER_SIZE) {
- + char header[HEADER_SIZE];
- + header[0] = (char) vtdata.numrows;
- + header[1] = (char) vtdata.numcolumns;
- + getconsxy (vt, header + 2);
- + while (p < HEADER_SIZE && count-- > 0)
- + put_user (header[p++], buf++);
- + }
- + p -= HEADER_SIZE;
- + org = screen_pos (vt, p >> 2);
- +
- + if (p & 3) {
- + unsigned long d = *org++;
- +
- + switch (p & 3) {
- + case 1:
- + if (count-- > 0)
- + put_user ((d >> 8) & 255, buf++);
- + case 2:
- + if (count-- > 0)
- + put_user ((d >> 16) & 255, buf++);
- + case 3:
- + if (count-- > 0)
- + put_user (d >> 24, buf++);
- + }
- + }
- +
- + while (count > 3) {
- + put_user (*org++, (unsigned long *) buf);
- + buf += 4;
- + count -= 4;
- + }
- +
- + if (count > 0) {
- + d = *org;
- + put_user (d & 0xff, buf++);
- + if (count > 1)
- + put_user ((d >> 8) & 0xff, buf++);
- + if (count > 2)
- + put_user ((d >> 16) & 0xff, buf++);
- + }
- + }
- + read = buf - buf0;
- + file->f_pos += read;
- + return read;
- +}
- +
- +static int vcs_write (struct inode *inode, struct file *file, const char *buf, int count)
- +{
- + struct vt *vt;
- + unsigned long p = file->f_pos;
- + unsigned int cons = MINOR(inode->i_rdev);
- + int viewed, attr, size, written;
- + const char *buf0;
- + unsigned long *org;
- +
- + attr = (cons & 128);
- + cons = (cons & 127);
- +
- + if (cons == 0) {
- + vt = vtdata.fgconsole;
- + viewed = 1;
- + } else {
- + vt = vt_con_data + (cons - 1);
- + viewed = 0;
- + }
- +
- + if (!vt_allocated (vt))
- + return -ENXIO;
- +
- + size = vcs_size(inode);
- +
- + if (count < 0 || p > size)
- + return -EINVAL;
- +
- + if (count > size - p)
- + count = size - p;
- +
- + buf0 = buf;
- + if (!attr) {
- + org = screen_pos (vt, p);
- + while (count-- > 0) {
- + *org = (*org & 0xffffff00) | get_user (buf++);
- + org++;
- + }
- + } else {
- + if (p < HEADER_SIZE) {
- + char header[HEADER_SIZE];
- + getconsxy (vt, header+2);
- +
- + while (p < HEADER_SIZE && count-- > 0)
- + header[p++] = get_user (buf++);
- + if (!viewed)
- + putconsxy (vt, header + 2);
- + }
- + p -= HEADER_SIZE;
- + org = screen_pos (vt, p >> 2);
- +
- + if (p & 3) {
- + unsigned long d = *org;
- +
- + switch (p & 3) {
- + case 1:
- + if (count-- > 0)
- + d = (d & 0xffff00ff) | (get_user (buf++) << 8);
- + case 2:
- + if (count-- > 0)
- + d = (d & 0xff00ffff) | (get_user (buf++) << 16);
- + case 3:
- + if (count-- > 0)
- + d = (d & 0x00ffffff) | (get_user (buf++) << 24);
- + }
- + *org ++ = d;
- + }
- +
- + while (count > 3) {
- + *org ++ = get_user ((const unsigned long *)buf);
- + buf += 4;
- + count -= 4;
- + }
- +
- + if (count > 0) {
- + unsigned long d;
- +
- + d = (*org >> (count * 8)) << (count * 8);
- + d |= get_user (buf ++);
- +
- + if (count > 1)
- + d |= get_user (buf ++) << 8;
- +
- + if (count > 2)
- + d |= get_user (buf ++) << 16;
- + *org = d;
- + }
- + }
- + written = buf - buf0;
- + update_scrmem (vt, file->f_pos >> 2, (written + 3) >> 2);
- + file->f_pos += written;
- + return written;
- +}
- +
- +static int vcs_open (struct inode *inode, struct file *filp)
- +{
- + unsigned int cons = (MINOR(inode->i_rdev) & 127);
- +
- + if (cons && !vt_allocated (vt_con_data + cons - 1))
- + return -ENXIO;
- + return 0;
- +}
- +
- +static struct file_operations vcs_fops = {
- + vcs_lseek, /* lseek */
- + vcs_read, /* read */
- + vcs_write, /* write */
- + NULL, /* readdir */
- + NULL, /* select */
- + NULL, /* ioctl */
- + NULL, /* mmap */
- + vcs_open, /* open */
- + NULL, /* release */
- + NULL /* fsync */
- +};
- +
- +int vcs_init(void)
- +{
- + int error;
- +
- + error = register_chrdev(VCS_MAJOR, "vcs", &vcs_fops);
- + if (error)
- + printk("unable to get major %d for vcs device", VCS_MAJOR);
- + return error;
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/vt.c linux/arch/arm/drivers/char/vt.c
- --- linux.orig/arch/arm/drivers/char/vt.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/vt.c Sat Sep 7 13:41:49 1996
- @@ -0,0 +1,1029 @@
- +/*
- + * linux/arch/arm/drivers/char/vt.c
- + *
- + * VT routines
- + *
- + * Changelog:
- + * 05-Sep-1996 RMK Fixed race condition between VT switch & initialisation
- + */
- +
- +#include <linux/config.h>
- +#include <linux/sched.h>
- +#include <linux/tty.h>
- +#include <linux/kd.h>
- +#include <linux/errno.h>
- +#include <linux/malloc.h>
- +#include <linux/mm.h>
- +#include <linux/tty.h>
- +#include <linux/major.h>
- +
- +#include <asm/segment.h>
- +
- +#include "kbd_kern.h"
- +#include "vt_kern.h"
- +
- +#define CON_XMIT_SIZE 2048
- +
- +#ifndef MIN
- +#define MIN(a,b) ((a) < (b) ? (a) : (b))
- +#endif
- +
- +/*
- + * VCD functions
- + */
- +extern void vcd_blankscreen (int nopowersave);
- +extern void vcd_disallocate (struct vt *);
- +extern int vcd_init (struct vt *, int kmallocok, unsigned long *kmem);
- +extern int vcd_ioctl (struct vt *, int cmd, unsigned long arg);
- +extern unsigned long vcd_pre_init (unsigned long kmem, struct vt *);
- +extern int vcd_resize (int rows, int columns);
- +extern void vcd_restorestate (const struct vt *);
- +extern void vcd_savestate (const struct vt *, int blanked);
- +extern void vcd_unblankscreen (void);
- +extern int vcd_write (const struct vt *, int from_user, const unsigned char *buf, int count);
- +extern void vcd_setup_graphics (const struct vt *);
- +
- +extern void con_reset_palette (const struct vt *vt);
- +extern void con_set_palette (const struct vt *vt);
- +extern int con_init (void);
- +
- +static int vt_refcount;
- +static struct tty_driver vt_driver;
- +static struct tty_struct *vt_table[MAX_NR_CONSOLES];
- +static struct termios *vt_termios[MAX_NR_CONSOLES];
- +static struct termios *vt_termios_locked[MAX_NR_CONSOLES];
- +
- +extern void vt_do_blankscreen (int nopowersave);
- +extern void vt_do_unblankscreen (void);
- +
- +struct vt_data vtdata;
- +struct vt vt_con_data[MAX_NR_CONSOLES];
- +
- +/*
- + * last_console is the last used console
- + */
- +struct vt *last_console;
- +
- +/*
- + * Sometimes we want to wait until a particular VT has been activated. We
- + * do it in a very simple manner. Everybody waits on a single queue and
- + * get woken up at once. Those that are satisfied go on with their business,
- + * while those not ready go back to sleep. Seems overkill to add a wait
- + * to each vt just for this - usually this does nothing!
- + */
- +static struct wait_queue *vt_activate_queue = NULL;
- +
- +/*
- + * Sleeps until a vt is activated, or the task is interrupted. Returns
- + * 0 if activation, -1 if interrupted.
- + */
- +static int vt_waitonactivate (void)
- +{
- + interruptible_sleep_on (&vt_activate_queue);
- + return (current->signal & ~current->blocked) ? -1 : 0;
- +}
- +
- +#define vt_wake_waitactive() wake_up(&vt_activate_queue)
- +
- +void vt_reset (const struct vt *vt)
- +{
- + vt->kbd->kbdmode = VC_XLATE;
- + vt->vtd->vc_mode = KD_TEXT;
- + vt->vtd->vt_mode.mode = VT_AUTO;
- + vt->vtd->vt_mode.waitv = 0;
- + vt->vtd->vt_mode.relsig = 0;
- + vt->vtd->vt_mode.acqsig = 0;
- + vt->vtd->vt_mode.frsig = 0;
- + vt->vtd->vt_pid = -1;
- + vt->vtd->vt_newvt = NULL;
- + con_reset_palette (vt);
- +}
- +
- +static int vt_allocate (struct vt *vt)
- +{
- + if (!vt_allocated(vt)) {
- + void *data, *p;
- + int r;
- +
- + data = kmalloc (sizeof (*vt->vcd) +
- + sizeof (*vt->kbd) +
- + sizeof (*vt->vtd) + CON_XMIT_SIZE, GFP_KERNEL);
- + if (!data)
- + return -ENOMEM;
- +
- + vt->vcd = data; p = ((struct con_struct *)data + 1);
- + vt->kbd = p; p = ((struct kbd_struct *)p + 1);
- + vt->vtd = p; p = ((struct vt_struct *)p + 1);
- + vt->vtd->xmit_buf = p;
- +
- + if ((r = kbd_struct_init (vt, 1)) < 0) {
- + vt->vcd = NULL;
- + vt->kbd = NULL;
- + vt->vtd = NULL;
- + kfree (data);
- + return r;
- + }
- +
- + if ((r = vcd_init (vt, 1, NULL)) < 0) {
- + vt->vcd = NULL;
- + vt->kbd = NULL;
- + vt->vtd = NULL;
- + kfree (data);
- + return r;
- + }
- + vt_reset (vt);
- + vt->vtd->xmitting = vt->vtd->xmit_cnt =
- + vt->vtd->xmit_out = vt->vtd->xmit_in = 0;
- + vt->allocinit = 1;
- + }
- + return 0;
- +}
- +
- +static int vt_disallocate (struct vt *vt)
- +{
- + if (vt_allocated (vt)) {
- + void *data = vt->vcd;
- +
- + vt->allocinit = 0;
- + vcd_disallocate (vt);
- + vt->vcd = NULL;
- + vt->kbd = NULL;
- + vt->vtd = NULL;
- +
- + kfree (data);
- + }
- + return 0;
- +}
- +
- +static void vt_updatescreen (const struct vt *newvt)
- +{
- + static int lock = 0;
- +
- + if (newvt == vtdata.fgconsole || lock)
- + return;
- +
- + if (!vt_allocated (newvt)) {
- + printk ("updatescreen: tty %d not allocated ??\n", newvt->num);
- + return;
- + }
- +
- + lock = 1;
- +
- + vcd_savestate (vtdata.fgconsole, vtdata.blanked != NULL);
- +
- + vtdata.fgconsole = (struct vt *)newvt;
- +
- + vcd_restorestate (vtdata.fgconsole);
- + compute_shiftstate ();
- +
- + lock = 0;
- +}
- +
- +/*
- + * Performs the back end of a vt switch
- + */
- +void vt_completechangeconsole (const struct vt *new_console)
- +{
- + unsigned char old_vt_mode;
- + struct vt *old_vt = vtdata.fgconsole;
- +
- + if (new_console == old_vt)
- + return;
- + if (!vt_allocated (new_console))
- + return;
- + last_console = old_vt;
- +
- + /*
- + * If we're switching, we could be going from KD_GRAPHICS to
- + * KD_TEXT mode or vice versa, which means we need to blank or
- + * unblank the screen later.
- + */
- + old_vt_mode = old_vt->vtd->vc_mode;
- + vt_updatescreen (new_console);
- +
- + /*
- + * If this new console is under process control, send it a signal
- + * telling it that it has acquired. Also check if it has died and
- + * clean up (similar to logic employed in vt_changeconsole())
- + */
- + if (new_console->vtd->vt_mode.mode == VT_PROCESS) {
- + /*
- + * Send the signal as privileged - kill_proc() will
- + * tell us if the process has gone or something else
- + * is awry
- + */
- + if (kill_proc(new_console->vtd->vt_pid,
- + new_console->vtd->vt_mode.acqsig,
- + 1) != 0) {
- + /*
- + * The controlling process has died, so we revert back to
- + * normal operation. In this case, we'll also change back
- + * to KD_TEXT mode. I'm not sure if this is strictly correct
- + * but it saves the agony when the X server dies and the screen
- + * remains blanked due to KD_GRAPHICS! It would be nice to do
- + * this outside of VT_PROCESS but there is no single process
- + * to account for and tracking tty count may be undesirable.
- + */
- + vt_reset (new_console);
- + }
- + }
- +
- + /*
- + * We do this here because the controlling process above may have
- + * gone, and so there is now a new vc_mode
- + */
- + if (old_vt_mode != new_console->vtd->vc_mode) {
- + if (new_console->vtd->vc_mode == KD_TEXT)
- + vt_do_unblankscreen ();
- + else {
- + vt_do_blankscreen (1);
- + vcd_setup_graphics (new_console);
- + }
- + }
- +
- + /* Set the colour palette for this VT */
- + if (new_console->vtd->vc_mode == KD_TEXT)
- + con_set_palette (new_console);
- +
- + /*
- + * Wake anyone waiting for their VT to activate
- + */
- + vt_wake_waitactive();
- + return;
- +}
- +
- +/*
- + * Performs the front-end of a vt switch
- + */
- +void vt_changeconsole (struct vt *new_console)
- +{
- + struct vt *old_vt = vtdata.fgconsole;
- +
- + if (new_console == old_vt)
- + return;
- + if (!vt_allocated (new_console))
- + return;
- +
- + /*
- + * If this vt is in process mode, then we need to handshake with
- + * that process before switching. Essentially, we store where that
- + * vt wants to switch to and wait for it to tell us when it's done
- + * (via VT_RELDISP ioctl).
- + *
- + * We also check to see if the controlling process still exists.
- + * If it doesn't, we reset this vt to auto mode and continue.
- + * This is a cheap way to track process control. The worst thing
- + * that can happen is: we send a signal to a process, it dies, and
- + * the switch gets "lost" waiting for a response; hopefully, the
- + * user will try again, we'll detect the process is gone (unless
- + * the user waits just the right amount of time :-) and revert the
- + * vt to auto control.
- + */
- + if (old_vt->vtd->vt_mode.mode == VT_PROCESS) {
- + /*
- + * Send the signal as privileged - kill_proc() will
- + * tell us if the process has gone or something else
- + * is awry
- + */
- + if (kill_proc(old_vt->vtd->vt_pid, old_vt->vtd->vt_mode.relsig, 1) == 0) {
- + /*
- + * It worked. Mark the vt to switch to and
- + * return. The process needs to send us a
- + * VT_RELDISP ioctl to complete the switch.
- + */
- + old_vt->vtd->vt_newvt = new_console;
- + return;
- + }
- +
- + /*
- + * The controlling process has died, so we revert back to
- + * normal operation. In this case, we'll also change back
- + * to KD_TEXT mode. I'm not sure if this is strictly correct
- + * but it saves the agony when the X server dies and the screen
- + * remains blanked due to KD_GRAPHICS! It would be nice to do
- + * this outside of VT_PROCESS but there is no single process
- + * to account for and tracking tty count may be undesirable.
- + */
- + vt_reset (old_vt);
- +
- + /*
- + * Fall through to normal (VT_AUTO) handling of the switch...
- + */
- + }
- +
- + /*
- + * Ignore all switches in KD_GRAPHICS+VT_AUTO mode
- + */
- + if (old_vt->vtd->vc_mode == KD_GRAPHICS)
- + return;
- +
- + vt_completechangeconsole (new_console);
- +}
- +
- +/*
- + * If a vt is under process control, the kernel will not switch to it
- + * immediately, but postpone the operation until the process calls this
- + * ioctl, allowing the switch to complete.
- + *
- + * According to the X sources this is the behavior:
- + * 0: pending switch-from not OK
- + * 1: pending switch-from OK
- + * 2: completed switch-to OK
- + */
- +static inline int vt_reldisp (const struct vt *old_vt, int arg)
- +{
- + int i;
- +
- + if (old_vt->vtd->vt_mode.mode != VT_PROCESS)
- + return -EINVAL;
- +
- + if (old_vt->vtd->vt_newvt) {
- + /*
- + * Switching-from response
- + */
- + if (arg == 0)
- + /*
- + * Switch disallowed, so forget we were trying
- + * to do it.
- + */
- + old_vt->vtd->vt_newvt = NULL;
- + else {
- + /*
- + * The current vt has been released, so complete the
- + * switch.
- + */
- + struct vt *new_vt = old_vt->vtd->vt_newvt;
- + old_vt->vtd->vt_newvt = NULL;
- + i = vt_allocate (new_vt);
- + if (i)
- + return i;
- + vt_completechangeconsole (new_vt);
- + }
- + } else {
- + /*
- + * Switched-to response
- + */
- + if (arg != VT_ACKACQ)
- + return -EINVAL;
- + }
- + return 0;
- +}
- +
- +/*
- + * Set the mode of a VT.
- + */
- +static inline int vt_kdsetmode (const struct vt *vt, int mode)
- +{
- + /*
- + * Currently, setting the mode from KD_TEXT to KD_GRAPHICS
- + * doesn't do a whole lot. I'm not sure if it should do any
- + * restoration of modes or what...
- + */
- + switch (mode) {
- + case KD_TEXT0:
- + case KD_TEXT1:
- + mode = KD_TEXT;
- + case KD_GRAPHICS:
- + case KD_TEXT:
- + break;
- + default:
- + return -EINVAL;
- + }
- +
- + if (vt->vtd->vc_mode == mode)
- + return 0;
- +
- + vt->vtd->vc_mode = mode;
- + if (vt != vtdata.fgconsole)
- + return 0;
- +
- + /*
- + * explicitly blank/unblank the screen if switching modes
- + */
- + if (mode == KD_TEXT) {
- + vt_do_unblankscreen ();
- + vcd_restorestate (vt);
- + } else {
- + vt_do_blankscreen (1);
- + vcd_setup_graphics (vt);
- + }
- + return 0;
- +}
- +
- +static inline int vt_setmode (const struct vt *vt, struct vt_mode *vtmode)
- +{
- + if (vtmode->mode != VT_AUTO && vtmode->mode != VT_PROCESS)
- + return -EINVAL;
- +
- + vt->vtd->vt_mode = *vtmode;
- + vt->vtd->vt_mode.frsig = 0;
- + vt->vtd->vt_pid = current->pid;
- + vt->vtd->vt_newvt = NULL;
- + return 0;
- +}
- +
- +void vt_mksound (unsigned int count, unsigned int vol, unsigned int ticks)
- +{
- + extern void mk_beep (unsigned int, unsigned int, unsigned int);
- +#ifdef CONFIG_SOUND
- + mk_beep (count, vol, ticks);
- +#endif
- +}
- +
- +/*
- + * Deallocate memory associated to VT (but leave VT1)
- + */
- +int vt_deallocate (int arg)
- +{
- + int i;
- +
- + if (arg == 0) {
- + /*
- + * deallocate all unused consoles, but leave 0
- + */
- + for (i = 1; i < MAX_NR_CONSOLES; i++)
- + if (!VT_BUSY (i))
- + vt_disallocate (vt_con_data + i);
- + } else {
- + arg -= 1;
- + if (VT_BUSY (arg))
- + return -EBUSY;
- + if (arg)
- + vt_disallocate (vt_con_data + arg);
- + }
- + return 0;
- +}
- +
- +int vt_resize (int columns, int rows)
- +{
- + return vcd_resize (rows, columns);
- +}
- +
- +void vt_pokeblankedconsole (void)
- +{
- + timer_active &= ~(1 << BLANK_TIMER);
- + if (vtdata.fgconsole->vtd->vc_mode == KD_GRAPHICS)
- + return;
- + if (vtdata.blanked) {
- + timer_table[BLANK_TIMER].expires = 0;
- + timer_active |= 1 << BLANK_TIMER;
- + } else
- + if (vtdata.screen.blankinterval) {
- + timer_table[BLANK_TIMER].expires = jiffies + vtdata.screen.blankinterval;
- + timer_active |= 1 << BLANK_TIMER;
- + }
- +}
- +
- +static void vt_blankscreen (void);
- +
- +void vt_do_unblankscreen (void)
- +{
- + if (!vtdata.blanked)
- + return;
- +
- + if (!vt_allocated (vtdata.fgconsole)) {
- + /* impossible... */
- + printk ("unblank_screen: tty %d not allocated ??\n", vtdata.fgconsole->num);
- + return;
- + }
- +
- + timer_table[BLANK_TIMER].fn = vt_blankscreen;
- +
- + if (vtdata.screen.blankinterval) {
- + timer_table[BLANK_TIMER].expires = jiffies + vtdata.screen.blankinterval;
- + timer_active |= 1 << BLANK_TIMER;
- + }
- +
- + vtdata.blanked = NULL;
- +
- + vcd_unblankscreen ();
- +}
- +
- +/*
- + * for panic.c
- + */
- +void do_unblank_screen (void)
- +{
- + vt_do_unblankscreen ();
- +}
- +
- +void vt_do_blankscreen (int nopowersave)
- +{
- + if (vtdata.blanked)
- + return;
- +
- + timer_active &= ~(1 << BLANK_TIMER);
- + timer_table[BLANK_TIMER].fn = do_unblank_screen;
- +
- + vtdata.blanked = vtdata.fgconsole;
- +
- + vcd_blankscreen (nopowersave);
- +}
- +
- +static void vt_blankscreen (void)
- +{
- + vt_do_blankscreen (0);
- +}
- +
- +static int vt_open (struct tty_struct *tty, struct file *filp)
- +{
- + struct vt *vt;
- + unsigned int idx;
- + int i;
- +
- + idx = MINOR(tty->device) - tty->driver.minor_start;
- +
- + vt = vt_con_data + idx;
- +
- + if ((i = vt_allocate (vt)) < 0)
- + return i;
- +
- + tty->driver_data = vt;
- +
- + if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
- + tty->winsize.ws_row = vtdata.numrows;
- + tty->winsize.ws_col = vtdata.numcolumns;
- + }
- + return 0;
- +}
- +
- +static void vt_vtd_flush_chars (const struct vt *vt, struct vt_struct *vtd)
- +{
- + unsigned long flags;
- +
- + save_flags (flags);
- + if (vtd->xmitting)
- + return;
- +
- + vtd->xmitting = 1;
- +
- + while (1) {
- + int c;
- +
- + cli ();
- + c = MIN(vtd->xmit_cnt, CON_XMIT_SIZE - vtd->xmit_out);
- +
- + if (c <= 0)
- + break;
- +
- + restore_flags (flags);
- +
- + c = vcd_write (vt, 0, vtd->xmit_buf + vtd->xmit_out, c);
- +
- + cli ();
- +
- + if (c <= 0)
- + break;
- + vtd->xmit_out = (vtd->xmit_out + c) & (CON_XMIT_SIZE - 1);
- + vtd->xmit_cnt -= c;
- + }
- +
- + vtd->xmitting = 0;
- + restore_flags (flags);
- + if (*vt->tty)
- + wake_up_interruptible (&(*vt->tty)->write_wait);
- +}
- +
- +static int vt_write (struct tty_struct *tty, int from_user,
- + const unsigned char *buf, int count)
- +{
- + static unsigned long last_error = 0;
- + const struct vt *vt = (struct vt *)tty->driver_data;
- +
- + if (vt_allocated (vt)) {
- + if (vt->vtd->xmit_cnt)
- + vt_vtd_flush_chars (vt, vt->vtd);
- + return vcd_write (vt, from_user, buf, count);
- + }
- +
- + if (jiffies > last_error + 10*HZ) {
- + printk ("vt_write: tty %d not allocated\n", vt->num);
- + last_error = jiffies;
- + }
- + return 0;
- +}
- +
- +static void vt_put_char (struct tty_struct *tty, unsigned char ch)
- +{
- + const struct vt *vt = (struct vt *)tty->driver_data;
- + unsigned long flags;
- +
- + if (!vt_allocated (vt))
- + return;
- +
- + save_flags_cli (flags);
- + if (vt->vtd->xmit_cnt < CON_XMIT_SIZE - 1) {
- + vt->vtd->xmit_buf[vt->vtd->xmit_in++] = ch;
- + vt->vtd->xmit_in &= CON_XMIT_SIZE - 1;
- + vt->vtd->xmit_cnt ++;
- + }
- + restore_flags (flags);
- +}
- +
- +static void vt_flush_chars (struct tty_struct *tty)
- +{
- + const struct vt *vt = (struct vt *)tty->driver_data;
- +
- + if (!vt_allocated (vt) || !vt->vtd->xmit_cnt || tty->stopped)
- + return;
- +
- + vt_vtd_flush_chars (vt, vt->vtd);
- +}
- +
- +static int vt_write_room (struct tty_struct *tty)
- +{
- + const struct vt *vt = (struct vt *)tty->driver_data;
- + int r;
- +
- + if (!vt_allocated (vt))
- + return 0;
- +
- + r = CON_XMIT_SIZE - vt->vtd->xmit_cnt - 8; /* allow 8 char overflow */
- + if (r < 0)
- + return 0;
- + else
- + return r;
- +}
- +
- +static int vt_chars_in_buffer (struct tty_struct *tty)
- +{
- + const struct vt *vt = (struct vt *)tty->driver_data;
- +
- + if (!vt_allocated (vt))
- + return CON_XMIT_SIZE;
- + return vt->vtd->xmit_cnt;
- +}
- +#if 0
- +static void vt_flush_buffer (struct tty_struct *tty)
- +{
- + const struct vt *vt = (struct vt *)tty->driver_data;
- +
- + if (!vt_allocated (vt))
- + return;
- +
- + cli ();
- + vt->vtd->xmit_cnt = vt->vtd->xmit_out = vt->vtd->xmit_in = 0;
- + sti ();
- +}
- +#endif
- +static int vt_ioctl (struct tty_struct *tty, struct file *filp,
- + unsigned int cmd, unsigned long arg)
- +{
- + struct vt *vt = tty->driver_data;
- + int perm, i;
- +
- + if (!vt_allocated (vt)) /* impossible ? */
- + return -ENOIOCTLCMD;
- + /*
- + * To have permissions to do most of the vt ioctls, we either
- + * have to be the owner of the tty, or super-user.
- + */
- + perm = 0;
- + if (current->tty == tty || suser ())
- + perm = 1;
- +
- + switch (cmd) {
- + case KDGETMODE:
- + i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
- + if (!i)
- + put_user (vt->vtd->vc_mode, (unsigned long *)arg);
- + return i;
- +
- + case VT_GETMODE:
- + i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (struct vt_mode));
- + if (i)
- + return i;
- + memcpy_tofs ((void *)arg, &vt->vtd->vt_mode, sizeof (struct vt_mode));
- + return 0;
- +
- + case VT_GETSTATE: {
- + /*
- + * Returns global VT state. Note that VT 0 is always open, since
- + * it's an alias for the current VT, and people can't use it here.
- + * We cannot return state for more than 16 VTs, since v_state is short.
- + */
- + struct vt_stat *vtstat = (struct vt_stat *)arg;
- + unsigned short state = 1, mask = 2;
- +
- + i = verify_area (VERIFY_WRITE, vtstat, sizeof (struct vt_stat));
- + if (i)
- + return i;
- +
- + for (i = 0; i < MAX_NR_CONSOLES && mask; ++i, mask <<= 1)
- + if (VT_IS_IN_USE(i))
- + state |= mask;
- +
- + put_user (vtdata.fgconsole->num, &vtstat->v_active);
- + put_user (state, &vtstat->v_state);
- + return 0;
- + }
- +
- + case VT_OPENQRY:
- + /*
- + * Returns the first available (non-open) console
- + */
- + i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
- + if (i)
- + return i;
- +
- + for (i = 0; i < MAX_NR_CONSOLES; i++)
- + if (!VT_IS_IN_USE(i))
- + break;
- + if (i < MAX_NR_CONSOLES)
- + put_user (i + 1, (unsigned long *)arg);
- + else
- + put_user (-1, (unsigned long *)arg);
- + return 0;
- +
- + case VT_DISALLOCATE:
- + if (arg > MAX_NR_CONSOLES)
- + return -ENXIO;
- + return vt_deallocate (arg);
- +
- + case VT_GETSCRINFO:
- + /*
- + * Get screen dimentions
- + */
- + i = verify_area (VERIFY_WRITE, (void *)arg, 5 * sizeof (unsigned long));
- + if (!i) {
- + put_user (0, (unsigned long *)arg);
- + put_user (vtdata.numcolumns * 8, (unsigned long *)(arg + 4));
- + put_user (vtdata.numrows * 8, (unsigned long *)(arg + 8));
- + put_user (vtdata.screen.bitsperpix, (unsigned long *)(arg + 12)); /* bpp */
- + put_user (4, (unsigned long *)(arg + 16)); /* depth */
- + }
- + return i;
- +
- + case KDGKBTYPE:
- + case KDADDIO:
- + case KDDELIO:
- + case KDENABIO:
- + case KDDISABIO:
- + case KDGKBMODE:
- + case KDGKBMETA:
- + case KDGETKEYCODE:
- + case KDGKBENT:
- + case KDGKBSENT:
- + case KDGKBDIACR:
- + case KDGKBLED:
- + case KDGETLED:
- + return kbd_ioctl (vt, cmd, arg);
- +
- + case VT_GETPALETTE:
- + case GIO_FONT:
- + case GIO_SCRNMAP:
- + case GIO_UNISCRNMAP:
- + case GIO_UNIMAP:
- + return vcd_ioctl (vt, cmd, arg);
- + }
- +
- + if (!perm)
- + return -EPERM;
- +
- + switch (cmd) {
- + case KDSETMODE:
- + return vt_kdsetmode (vt, arg);
- +
- + case VT_SETMODE: {
- + struct vt_mode vtmode;
- +
- + if (!perm)
- + return -EPERM;
- + i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct vt_mode));
- + if (i)
- + return i;
- + memcpy_fromfs (&vtmode, (void *)arg, sizeof (struct vt_mode));
- + return vt_setmode (vt, &vtmode);
- + }
- +
- + case VT_ACTIVATE:
- + /*
- + * VT activate will cause us to switch to vt #num with num >= 1
- + * (switches to vt0, our console, are not allowed, just to preserve
- + * sanity)
- + */
- + if (arg == 0 || arg > MAX_NR_CONSOLES)
- + return -ENXIO;
- + vt = vt_con_data + (arg - 1);
- + i = vt_allocate (vt);
- + if (i)
- + return i;
- +
- + vt_changeconsole (vt);
- + return 0;
- +
- + case VT_WAITACTIVE:
- + /*
- + * wait until the specified VT has been activated
- + */
- + if (arg == 0 || arg > MAX_NR_CONSOLES)
- + return -ENXIO;
- + vt = vt_con_data + (arg - 1);
- + while (vt != vtdata.fgconsole) {
- + if (vt_waitonactivate () < 0)
- + return -EINTR;
- + }
- + return 0;
- +
- + case VT_RELDISP:
- + return vt_reldisp (vt, arg);
- +
- + case VT_RESIZE: {
- + struct vt_sizes *vtsizes = (struct vt_sizes *)arg;
- + ushort ll, cc;
- + i = verify_area (VERIFY_READ, (void *)vtsizes, sizeof (struct vt_sizes));
- + if (i)
- + return i;
- + ll = get_user (&vtsizes->v_rows);
- + cc = get_user (&vtsizes->v_cols);
- + return vt_resize (cc, ll);
- + }
- +
- + case KIOCSOUND:
- + vt_mksound (arg, 72, 5);
- + return 0;
- +
- + case KDMKTONE: {
- + unsigned int freq = get_user ((unsigned long *)arg);
- + unsigned int vol = get_user ((unsigned long *)(arg + 4));
- + unsigned int ticks = get_user ((unsigned long *)(arg + 8));
- +
- + /*
- + * Generate the tone for the apropriate number of ticks.
- + * If time is zero, turn off sound ourselves.
- + */
- + vt_mksound (freq, vol, ticks);
- + return 0;
- + }
- +
- + case KDMAPDISP:
- + case KDUNMAPDISP:
- + case KDSKBMODE:
- + case KDSKBMETA:
- + case KDSETKEYCODE:
- + case KDSKBENT:
- + case KDSKBSENT:
- + case KDSKBDIACR:
- + case KDSKBLED:
- + case KDSETLED:
- + case KDSIGACCEPT:
- + return kbd_ioctl (vt, cmd, arg);
- +
- + case VT_SETPALETTE:
- + case PIO_FONT:
- + case PIO_SCRNMAP:
- + case PIO_UNISCRNMAP:
- + case PIO_UNIMAPCLR:
- + case PIO_UNIMAP:
- + return vcd_ioctl (vt, cmd, arg);
- + }
- + return -ENOIOCTLCMD;
- +}
- +/*
- + * Turn the Scroll-Lock LED on when the tty is stopped
- + */
- +static void vt_stop (struct tty_struct *tty)
- +{
- + if (tty) {
- + const struct vt *vt = (struct vt *)tty->driver_data;
- + if (vt_allocated (vt)) {
- + set_vc_kbd_led (vt->kbd, VC_SCROLLOCK);
- + set_leds ();
- + }
- + }
- +}
- +
- +/*
- + * Turn the Scroll-Lock LED off when the tty is started
- + */
- +static void vt_start (struct tty_struct *tty)
- +{
- + if (tty) {
- + const struct vt *vt = (struct vt *)tty->driver_data;
- + if (vt_allocated (vt)) {
- + clr_vc_kbd_led (vt->kbd, VC_SCROLLOCK);
- + set_leds ();
- + }
- + if (vt->vtd->xmit_cnt)
- + vt_vtd_flush_chars (vt, vt->vtd);
- + }
- +}
- +
- +/*
- + * vt_throttle and vt_unthrottle are only used for
- + * paste_selection(), which has to stuff in a large number
- + * of characters...
- + */
- +static void vt_throttle (struct tty_struct *tty)
- +{
- +}
- +
- +static void vt_unthrottle (struct tty_struct *tty)
- +{
- + const struct vt *vt = (struct vt *)tty->driver_data;
- +
- + if (vt_allocated (vt))
- + wake_up_interruptible (&vt->vtd->paste_wait);
- +}
- +
- +/*
- + * We don't have kmalloc setup yet. We just want to get enough up so that printk
- + * can work.
- + *
- + * Basically, we setup the VT structures for tty1.
- + */
- +unsigned long vt_pre_init (unsigned long kmem)
- +{
- + struct vt *vt = vt_con_data;
- +
- + memset (vt_con_data, 0, sizeof (vt_con_data));
- +
- + vtdata.numcolumns = ORIG_VIDEO_COLS;
- + vtdata.numrows = ORIG_VIDEO_LINES;
- + vtdata.blanked = NULL;
- + vtdata.fgconsole = vt_con_data;
- + vtdata.screen.blankinterval = 10*60*HZ;
- + vtdata.select.vt = NULL;
- + vtdata.select.start = -1;
- + vtdata.select.end = 0;
- + vtdata.select.buffer= NULL;
- +
- + vt->vcd = (struct con_struct *)kmem; kmem += sizeof (struct con_struct);
- + vt->kbd = (struct kbd_struct *)kmem; kmem += sizeof (struct kbd_struct);
- + vt->vtd = (struct vt_struct *)kmem; kmem += sizeof (struct vt_struct);
- + vt->vtd->xmit_buf = (unsigned char *)kmem; kmem += CON_XMIT_SIZE;
- + vt->vtd->xmitting = vt->vtd->xmit_cnt =
- + vt->vtd->xmit_out = vt->vtd->xmit_in = 0;
- + vt->tty = &vt_table[0];
- + vt->num = 1;
- +
- + /*
- + * vcd_init is called inside vcd_pre_init
- + */
- + kbd_struct_init (vt, 0);
- + vt_reset (vt);
- + vt->allocinit = 1;
- +
- + kmem = vcd_pre_init (kmem, vt);
- +
- + return kmem;
- +}
- +
- +/*
- + * This is the post initialisation. We have kmalloc setup so we can use it...
- + */
- +void vt_post_init (void)
- +{
- + int i;
- +
- + memset (&vt_driver, 0, sizeof (struct tty_driver));
- + vt_driver.magic = TTY_DRIVER_MAGIC;
- + vt_driver.name = "tty";
- + vt_driver.name_base = 1;
- + vt_driver.major = TTY_MAJOR;
- + vt_driver.minor_start = 1;
- + vt_driver.num = MAX_NR_CONSOLES;
- + vt_driver.type = TTY_DRIVER_TYPE_CONSOLE;
- + vt_driver.init_termios = tty_std_termios;
- + vt_driver.flags = TTY_DRIVER_REAL_RAW;
- + vt_driver.refcount = &vt_refcount;
- + vt_driver.table = vt_table;
- + vt_driver.termios = vt_termios;
- + vt_driver.termios_locked = vt_termios_locked;
- + vt_driver.open = vt_open;
- + vt_driver.write = vt_write;
- + vt_driver.put_char = vt_put_char;
- + vt_driver.flush_chars = vt_flush_chars;
- + vt_driver.write_room = vt_write_room;
- + vt_driver.chars_in_buffer = vt_chars_in_buffer;
- +#if 0
- + vt_driver.flush_buffer = vt_flush_buffer;
- +#endif
- + vt_driver.ioctl = vt_ioctl;
- + vt_driver.stop = vt_stop;
- + vt_driver.start = vt_start;
- + vt_driver.throttle = vt_throttle;
- + vt_driver.unthrottle = vt_unthrottle;
- +
- + for (i = 1; i < MAX_NR_CONSOLES; i++) {
- + struct vt *vt = vt_con_data + i;
- + vt->tty = &vt_table[i];
- + vt->num = i + 1;
- + vt->allocinit = 0;
- + if (i < MIN_NR_CONSOLES)
- + vt_allocate (vt);
- + }
- +
- + if (tty_register_driver (&vt_driver))
- + panic ("Couldn't register console driver");
- +
- + timer_table[BLANK_TIMER].fn = vt_blankscreen;
- + if (vtdata.screen.blankinterval) {
- + timer_table[BLANK_TIMER].expires = jiffies + vtdata.screen.blankinterval;
- + timer_active |= 1 << BLANK_TIMER;
- + } else
- + timer_table[BLANK_TIMER].expires = 0;
- +
- + kbd_init ();
- + vcs_init ();
- +}
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/char/vt_kern.h linux/arch/arm/drivers/char/vt_kern.h
- --- linux.orig/arch/arm/drivers/char/vt_kern.h Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/char/vt_kern.h Fri Sep 6 22:23:13 1996
- @@ -0,0 +1,201 @@
- +/*
- + * linux/arch/arm/drivers/char/vt.h
- + *
- + * Virtual Terminal bits...
- + */
- +
- +#ifndef _VT_KERN_H
- +#define _VT_KERN_H
- +
- +#include <linux/vt.h>
- +
- +#define NPAR 16
- +#define SEL_BUFFER_SIZE 4096
- +
- +struct vc_state {
- + unsigned char forecol; /* foreground */
- + unsigned char backcol; /* background */
- + unsigned char x; /* x position */
- + unsigned char y; /* y position */
- +#define FLG_BOLD 0x01
- +#define FLG_ITALIC 0x02
- +#define FLG_UNDERLINE 0x04
- +#define FLG_FLASH 0x08
- +#define FLG_INVERSE 0x10
- +#define FLG_CHRSET 0x20
- + unsigned char flags; /* special flags */
- + unsigned char G0_charset; /* G0 character set */
- + unsigned char G1_charset; /* G1 character set */
- + unsigned char __unused; /* unused */
- +};
- +
- +struct con_struct {
- + struct {
- + unsigned int origin; /* address of top-left */
- + unsigned int pos; /* current position into screen */
- + unsigned long *palette_entries; /* Current palette */
- + unsigned int cursoron; /* Cursor on count */
- + } screen;
- +
- + struct {
- + unsigned long *buffer; /* pointer to actual buffer */
- + unsigned int size; /* size of buffer */
- + unsigned int pos; /* current position into buffer */
- + unsigned char kmalloced : 1; /* buffer kmalloced */
- + } buffer;
- +
- + /*
- + * State
- + */
- + struct vc_state curstate; /* current state */
- + struct vc_state savedstate; /* saved state */
- + unsigned long combined_state; /* combined state */
- + unsigned long cached_backcolwrd; /* cached background colour */
- + unsigned long tab_stop[5]; /* tab stops */
- + unsigned short *translate; /* translation table */
- +
- + unsigned char top; /* top of scrollable region */
- + unsigned char bottom; /* bottom of scrollable region */
- + unsigned char def_forecol; /* default foreground */
- + unsigned char def_backcol; /* default background */
- +
- + unsigned char cursor_count; /* on/off cursor count (int) */
- +
- + unsigned char disp_ctrl : 1; /* display control characters */
- + unsigned char toggle_meta : 1; /* toggle high bit */
- + unsigned char decscnm : 1; /* screen mode */
- + unsigned char decom : 1; /* origin mode */
- + unsigned char decawm : 1; /* autowrap mode */
- + unsigned char deccm : 1; /* cursor visible */
- + unsigned char decim : 1; /* insert mode */
- + unsigned char deccolm : 1; /* 80/132 col mode */
- +
- + unsigned char report_mouse : 2; /* mouse reporting? */
- + unsigned char need_wrap : 1; /* need to wrap */
- + unsigned char ques : 1;
- + /*
- + * UTF
- + */
- + unsigned char utf : 1; /* Unicode UTF-8 encoding */
- +
- + unsigned char utf_count; /* UTF character count */
- + unsigned long utf_char; /* UTF character built */
- + unsigned long npar; /* number of params */
- + unsigned long par[NPAR]; /* params */
- + unsigned char state; /* Current escape state */
- +
- +};
- +
- +struct vt_data {
- + unsigned char numcolumns; /* number of columns */
- + unsigned char numrows; /* number of rows */
- + unsigned char __unused[2];
- + struct vt *fgconsole; /* displayed VC */
- + struct vt *blanked; /* blanked VC */
- + struct {
- + unsigned char bitsperpix; /* bits per pixel */
- + unsigned char bytespercharh; /* horiz. bytes a char takes */
- + unsigned char bytespercharv; /* vert. bytes a char takes */
- + unsigned char __unused[1];
- + unsigned long sizerow; /* size of a row of chars */
- + unsigned long totsize; /* total size */
- + unsigned long memstart; /* video mem start */
- + unsigned long memmask; /* video mem mask */
- + unsigned long memend; /* video mem end */
- + unsigned long blankinterval; /* blank interval */
- + } screen;
- + struct {
- + unsigned long totsize; /* total buffer size */
- + unsigned long sizerow; /* size of a row of chars */
- + } buffer;
- + struct {
- + struct vt *vt; /* selected VC */
- + int start; /* start offset */
- + int end; /* end offset */
- + int length; /* buffer length */
- + char *buffer; /* buffer */
- + } select;
- +};
- +
- +extern struct vt_data vtdata;
- +extern struct tty_driver console_driver;
- +
- +struct vt_struct {
- + unsigned char vc_mode; /* hmm... */
- + unsigned char vc_kbdraw;
- + unsigned char vc_kbde0;
- + unsigned char vc_kbdleds;
- + struct vt_mode vt_mode;
- + pid_t vt_pid;
- + struct vt *vt_newvt; /* VT to switch to.. */
- + struct wait_queue *paste_wait;
- + unsigned char * xmit_buf;
- + unsigned int xmit_cnt;
- + unsigned int xmit_out;
- + unsigned int xmit_in;
- + unsigned int xmitting;
- +};
- +
- +struct vt {
- + /*
- + * Per-console data
- + */
- + struct con_struct *vcd;
- + /*
- + * Keyboard stuff
- + */
- + struct kbd_struct *kbd;
- + /*
- + * VT stuff
- + */
- + struct vt_struct *vtd;
- + /*
- + * tty that this VT is connected to
- + */
- + struct tty_struct **tty;
- + /*
- + * tty number of this vt struct
- + */
- + unsigned char num;
- + /*
- + * is this vt allocated and initialised?
- + */
- + unsigned char allocinit;
- + /*
- + * might add scrmem at some time, to have everything
- + * in one place - the disadvantage would be that
- + * vc_cons etc can no longer be static
- + */
- +};
- +
- +extern struct vt vt_con_data[];
- +
- +#define VT_IS_IN_USE(i) (vt_driver.table[i] && vt_driver.table[i]->count)
- +#define VT_BUSY(i) (VT_IS_IN_USE(i) || vt_con_data + i == vtdata.fgconsole || vt_con_data + i == vtdata.select.vt)
- +
- +extern inline int vt_allocated (const struct vt * const vt)
- +{
- + return vt->allocinit != 0;
- +}
- +
- +extern void vt_reset (const struct vt *vt);
- +extern void vt_completechangeconsole (const struct vt *vt);
- +extern void vt_changeconsole (struct vt *newvt);
- +extern void vt_mksound (unsigned int count, unsigned int vol, unsigned int ticks);
- +extern int vt_deallocate (int arg);
- +extern int vt_resize (int cols, int rows);
- +
- +/*
- + * Blanking ...
- + */
- +extern void vt_pokeblankedconsole (void);
- +extern void vt_do_unblankscreen (void);
- +extern void vt_do_blankscreen (int nopowersave);
- +
- +/*
- + * Initialisation ...
- + */
- +extern unsigned long vt_pre_init (unsigned long kmem);
- +extern void vt_post_init (void);
- +
- +#endif /* _VT_KERN_H */
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/net/Config.in linux/arch/arm/drivers/net/Config.in
- --- linux.orig/arch/arm/drivers/net/Config.in Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/net/Config.in Thu Jun 27 20:09:55 1996
- @@ -0,0 +1,21 @@
- +#
- +# Network device configuration
- +#
- +tristate 'Dummy net driver support' CONFIG_DUMMY
- +tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
- +tristate 'PPP (point-to-point) support' CONFIG_PPP
- +if [ ! "$CONFIG_PPP" = "n" ]; then
- + comment 'CCP compressors for PPP are only built as modules.'
- +fi
- +tristate 'SLIP (serial line) support' CONFIG_SLIP
- +if [ "$CONFIG_SLIP" != "n" ]; then
- + bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED
- + bool ' Keepalive and linefill' CONFIG_SLIP_SMART
- + bool ' Six bit SLIP encapsulation' CONFIG_SLIP_MODE_SLIP6
- +fi
- +#tristate 'PLIP (parallel port) support' CONFIG_PLIP
- +#
- +# Ethernet
- +#
- +tristate 'Ether1 (82586) support' CONFIG_ETHER1
- +tristate 'Ether3 (NQ8005) support' CONFIG_ETHER3
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/net/Makefile linux/arch/arm/drivers/net/Makefile
- --- linux.orig/arch/arm/drivers/net/Makefile Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/net/Makefile Sun Aug 25 15:39:47 1996
- @@ -0,0 +1,129 @@
- +# File: drivers/net/Makefile
- +#
- +# Makefile for the Linux network (ethercard) device drivers.
- +#
- +
- +all: links first_rule
- +
- +LK = README.eql README.multicast README.tunnel README1.PLIP README2.PLIP \
- + bsd_comp.c dummy.c eql.c loopback.c net_init.c slhc.c slip.c slip.h
- +
- +L_TARGET := net.a
- +L_OBJS := Space.o auto_irq.o net_init.o loopback.o
- +M_OBJS :=
- +MOD_LIST_NAME := NET_MODULES
- +
- +# Need these to keep track of whether the SLHC module should
- +# really go in the kernel or a module
- +CONFIG_SLHC_BUILTIN :=
- +CONFIG_SLHC_MODULE :=
- +
- +ifeq ($(CONFIG_ETHER1),y)
- +L_OBJS += ether1.o
- +else
- + ifeq ($(CONFIG_ETHER1),m)
- + M_OBJS += ether1.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_ETHER3),y)
- +L_OBJS += ether3.o
- +else
- + ifeq ($(CONFIG_ETHER3),m)
- + M_OBJS += ether3.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_IPIP),y)
- +L_OBJS += tunnel.o
- +else
- + ifeq ($(CONFIG_IPIP),m)
- + M_OBJS += tunnel.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_PLIP),y)
- +L_OBJS += plip.o
- +else
- + ifeq ($(CONFIG_PLIP),m)
- + M_OBJS += plip.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_PPP),y)
- +LX_OBJS += ppp.o
- +CONFIG_SLHC_BUILTIN = y
- +else
- + ifeq ($(CONFIG_PPP),m)
- + MX_OBJS += ppp.o
- + CONFIG_SLHC_MODULE = y
- + endif
- +endif
- +
- +ifneq ($(CONFIG_PPP),n)
- + M_OBJS += bsd_comp.o
- +endif
- +
- +ifeq ($(CONFIG_SLIP),y)
- +L_OBJS += slip.o
- +CONFIG_SLHC_BUILTIN = y
- +else
- + ifeq ($(CONFIG_SLIP),m)
- + M_OBJS += slip.o
- + CONFIG_SLHC_MODULE = y
- + endif
- +endif
- +
- +ifeq ($(CONFIG_DUMMY),y)
- +L_OBJS += dummy.o
- +else
- + ifeq ($(CONFIG_DUMMY),m)
- + L_OBJS += dummy.o
- + endif
- +endif
- +
- +# If anything built-in uses slhc, then build it into the kernel also.
- +# If not, but a module uses it, build as a module.
- +ifdef CONFIG_SLHC_BUILTIN
- +L_OBJS += slhc.o
- +else
- + ifdef CONFIG_SLHC_MODULE
- + M_OBJS += slhc.o
- + endif
- +endif
- +
- +ifeq ($(CONFIG_EQUALIZER),y)
- +L_OBJS += eql.o
- +else
- + ifeq ($(CONFIG_EQUALIZER),m)
- + M_OBJS += eql.o
- + endif
- +endif
- +
- +fastdep: links
- +
- +include $(TOPDIR)/Rules.make
- +
- +clean:
- + rm -f core *.o *.a *.s
- +
- +net_init.o: ../../../../include/linux/autoconf.h
- +
- +.PHONY: links
- +links:
- + -@for f in $(LK); do \
- + if [ ! -e $$f ]; then \
- + echo "ln -s ../../../../drivers/net/$$f .";\
- + ln -s ../../../../drivers/net/$$f .; \
- + fi \
- + done
- +
- +mrproper:
- + -@for f in $(LK); do \
- + if [ -L $$f ]; then \
- + echo $(RM) $$f; \
- + $(RM) $$f; \
- + fi; \
- + done
- + $(RM) ppp.ver
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/net/Space.c linux/arch/arm/drivers/net/Space.c
- --- linux.orig/arch/arm/drivers/net/Space.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/net/Space.c Sun Jul 7 13:12:08 1996
- @@ -0,0 +1,173 @@
- +/*
- + * INET An implementation of the TCP/IP protocol suite for the LINUX
- + * operating system. INET is implemented using the BSD Socket
- + * interface as the means of communication with the user level.
- + *
- + * Holds initial configuration information for devices.
- + *
- + * NOTE: This file is a nice idea, but its current format does not work
- + * well for drivers that support multiple units, like the SLIP
- + * driver. We should actually have only one pointer to a driver
- + * here, with the driver knowing how many units it supports.
- + * Currently, the SLIP driver abuses the "base_addr" integer
- + * field of the 'device' structure to store the unit number...
- + * -FvK
- + *
- + * Version: @(#)Space.c 1.0.7 08/12/93
- + *
- + * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
- + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- + * Donald J. Becker, <becker@super.org>
- + *
- + * FIXME:
- + * Sort the device chain fastest first.
- + *
- + * 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.
- + */
- +#include <linux/config.h>
- +#include <linux/netdevice.h>
- +#include <linux/errno.h>
- +
- +#define NEXT_DEV NULL
- +
- +
- +/* A unified ethernet device probe. This is the easiest way to have every
- + ethernet adaptor have the name "eth[0123...]".
- + */
- +extern int ether1_probe (struct device *dev);
- +extern int ether3_probe (struct device *dev);
- +
- +static int
- +ethif_probe(struct device *dev)
- +{
- + u_long base_addr = dev->base_addr;
- +
- + if ((base_addr == 0xffe0) || (base_addr == 1))
- + return 1; /* ENXIO */
- +
- + if (1
- +#ifdef CONFIG_ETHER3
- + && ether3_probe (dev)
- +#endif
- +#ifdef CONFIG_ETHER1
- + && ether1_probe (dev)
- +#endif
- + && 1 ) {
- + return 1; /* -ENODEV or -EAGAIN would be more accurate. */
- + }
- + return 0;
- +}
- +
- +/* The first device defaults to I/O base '0', which means autoprobe. */
- +#ifndef ETH0_ADDR
- +# define ETH0_ADDR 0
- +#endif
- +#ifndef ETH0_IRQ
- +# define ETH0_IRQ 0
- +#endif
- +/* The first device defaults to I/O base '0', which means autoprobe. */
- +#ifndef ETH1_ADDR
- +# define ETH1_ADDR 0
- +#endif
- +#ifndef ETH1_IRQ
- +# define ETH1_IRQ 0
- +#endif
- +/* "eth0" defaults to autoprobe (== 0), other use a base of 0xffe0 (== -0x20),
- + which means "don't probe". These entries exist to only to provide empty
- + slots which may be enabled at boot-time. */
- +
- +static struct device eth3_dev = {
- +"eth3" , 0x0, 0x0, 0x0, 0x0, 0xffe0 , 0 , 0, 0, 0, NEXT_DEV , ethif_probe };
- +
- +static struct device eth2_dev = {
- +"eth2" , 0x0, 0x0, 0x0, 0x0, 0xffe0 , 0 , 0, 0, 0, ð3_dev , ethif_probe };
- +
- +static struct device eth1_dev = {
- +"eth1" , 0x0, 0x0, 0x0, 0x0, ETH1_ADDR, ETH1_IRQ, 0, 0, 0, ð2_dev , ethif_probe };
- +
- +static struct device eth0_dev = {
- +"eth0" , 0x0, 0x0, 0x0, 0x0, ETH0_ADDR, ETH0_IRQ, 0, 0, 0, ð1_dev , ethif_probe };
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (ð0_dev)
- +
- +#if defined(PLIP) || defined(CONFIG_PLIP)
- +extern int plip_init(struct device *);
- +static struct device plip2_dev = {
- +"plip2" , 0x0, 0x0, 0x0, 0x0, 0x278 , 2 , 0, 0, 0, NEXT_DEV , plip_init };
- +
- +static struct device plip1_dev = {
- +"plip1" , 0x0, 0x0, 0x0, 0x0, 0x378 , 7 , 0, 0, 0, &plip2_dev, plip_init };
- +
- +static struct device plip0_dev = {
- +"plip0" , 0x0, 0x0, 0x0, 0x0, 0x3BC , 5 , 0, 0, 0, &plip1_dev, plip_init };
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (&plip0_dev)
- +#endif /* PLIP */
- +
- +#if defined(SLIP) || defined(CONFIG_SLIP)
- + /* To be exact, this node just hooks the initialization
- + routines to the device structures. */
- +extern int slip_init_ctrl_dev(struct device *);
- +
- +static struct device slip_bootstrap = {
- +"slip_proto" , 0x0, 0x0, 0x0, 0x0, 0 , 0 , 0, 0, 0, NEXT_DEV , slip_init_ctrl_dev };
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (&slip_bootstrap)
- +#endif /* SLIP */
- +
- +#if defined(CONFIG_PPP)
- +extern int ppp_init(struct device *);
- +
- +static struct device ppp_bootstrap = {
- +"ppp_proto" , 0x0, 0x0, 0x0, 0x0, 0 , 0 , 0, 0, 0, NEXT_DEV , ppp_init };
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (&ppp_bootstrap)
- +#endif /* PPP */
- +
- +#ifdef CONFIG_DUMMY
- +extern int dummy_init(struct device *dev);
- +
- +static struct device dummy_dev = {
- +"dummy" , 0x0, 0x0, 0x0, 0x0, 0 , 0 , 0, 0, 0, NEXT_DEV , dummy_init };
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (&dummy_dev)
- +#endif
- +
- +#ifdef CONFIG_EQUALIZER
- +extern int eql_init(struct device *dev);
- +
- +static struct device eql_dev = {
- +"eql" , 0x0, 0x0, 0x0, 0x0, 0 , 0 , 0, 0, 0, NEXT_DEV , eql_init };
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (&eql_dev)
- +#endif
- +
- +#ifdef CONFIG_NET_IPIP
- +#ifdef CONFIG_IP_FORWARD
- +extern int tunnel_init (struct device *dev);
- +
- +static struct device tunnel_dev1 = {
- +"tunl1" , 0x0, 0x0, 0x0, 0x0, 0 , 0 , 0, 0, 0, NEXT_DEV , tunnel_init };
- +
- +static struct device tunnel_dev0 = {
- +"tunl0" , 0x0, 0x0, 0x0, 0x0, 0 , 0 , 0, 0, 0, &tunnel_dev1, tunnel_init };
- +
- +#undef NEXT_DEV
- +#define NEXT_DEV (&tunnel_dev0)
- +#endif
- +#endif
- +
- +extern int loopback_init(struct device *dev);
- +struct device loopback_dev = {
- +"lo" , 0x0, 0x0, 0x0, 0x0, 0 , 0 , 0, 0, 0, NEXT_DEV , loopback_init };
- +
- +struct device *dev_base = &loopback_dev;
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/net/auto_irq.c linux/arch/arm/drivers/net/auto_irq.c
- --- linux.orig/arch/arm/drivers/net/auto_irq.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/net/auto_irq.c Fri Jun 28 21:23:06 1996
- @@ -0,0 +1,122 @@
- +/* auto_irq.c: Auto-configure IRQ lines for linux. */
- +/*
- + Written 1994 by Donald Becker.
- +
- + The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
- + Center of Excellence in Space Data and Information Sciences
- + Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
- +
- + This code is a general-purpose IRQ line detector for devices with
- + jumpered IRQ lines. If you can make the device raise an IRQ (and
- + that IRQ line isn't already being used), these routines will tell
- + you what IRQ line it's using -- perfect for those oh-so-cool boot-time
- + device probes!
- +
- + To use this, first call autoirq_setup(timeout). TIMEOUT is how many
- + 'jiffies' (1/100 sec.) to detect other devices that have active IRQ lines,
- + and can usually be zero at boot. 'autoirq_setup()' returns the bit
- + vector of nominally-available IRQ lines (lines may be physically in-use,
- + but not yet registered to a device).
- + Next, set up your device to trigger an interrupt.
- + Finally call autoirq_report(TIMEOUT) to find out which IRQ line was
- + most recently active. The TIMEOUT should usually be zero, but may
- + be set to the number of jiffies to wait for a slow device to raise an IRQ.
- +
- + The idea of using the setup timeout to filter out bogus IRQs came from
- + the serial driver.
- +*/
- +
- +
- +#ifdef version
- +static const char *version=
- +"auto_irq.c:v1.11 Donald Becker (becker@cesdis.gsfc.nasa.gov)";
- +#endif
- +
- +#include <linux/sched.h>
- +#include <linux/delay.h>
- +#include <asm/bitops.h>
- +#include <asm/io.h>
- +#include <asm/irq.h>
- +#include <linux/netdevice.h>
- +
- +struct device *irq2dev_map[32];
- +
- +unsigned long irqs_busy = 0x2147; /* The set of fixed IRQs (keyboard, timer, etc) */
- +unsigned long irqs_used = 0x0001; /* The set of fixed IRQs sometimes enabled. */
- +unsigned long irqs_reserved; /* An advisory "reserved" table. */
- +unsigned long irqs_shared; /* IRQ lines "shared" among conforming cards.*/
- +
- +static volatile unsigned long irq_bitmap; /* The irqs we actually found. */
- +static unsigned long irq_handled; /* The irq lines we have a handler on. */
- +static volatile int irq_number; /* The latest irq number we actually found. */
- +
- +static void autoirq_probe(int irq, void *dev_id, struct pt_regs * regs)
- +{
- + irq_number = irq;
- + set_bit(irq, (void *)&irq_bitmap); /* irq_bitmap |= 1 << irq; */
- + disable_irq(irq);
- + return;
- +}
- +
- +int autoirq_setup(int waittime)
- +{
- + int i, mask;
- + int timeout = jiffies + waittime;
- + int boguscount = (waittime*loops_per_sec) / 100;
- +
- + irq_handled = 0;
- + for (i = 0; i < 16; i++) {
- + if (test_bit(i, &irqs_busy) == 0
- + && request_irq(i, autoirq_probe, SA_INTERRUPT, "irq probe", NULL) == 0)
- + set_bit(i, (void *)&irq_handled); /* irq_handled |= 1 << i;*/
- + }
- + /* Update our USED lists. */
- + irqs_used |= ~irq_handled;
- + irq_number = 0;
- + irq_bitmap = 0;
- +
- + /* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
- + while (timeout > jiffies && --boguscount > 0)
- + ;
- +
- + for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
- + if (irq_bitmap & irq_handled & mask) {
- + irq_handled &= ~mask;
- +#ifdef notdef
- + printk(" Spurious interrupt on IRQ %d\n", i);
- +#endif
- + free_irq(i, NULL);
- + }
- + }
- + return irq_handled;
- +}
- +
- +int autoirq_report(int waittime)
- +{
- + int i;
- + int timeout = jiffies+waittime;
- + int boguscount = (waittime*loops_per_sec) / 100;
- +
- + /* Hang out at least <waittime> jiffies waiting for the IRQ. */
- +
- + while (timeout > jiffies && --boguscount > 0)
- + if (irq_number)
- + break;
- +
- + /* Retract the irq handlers that we installed. */
- + for (i = 0; i < 16; i++) {
- + if (test_bit(i, (void *)&irq_handled))
- + free_irq(i, NULL);
- + }
- + return irq_number;
- +}
- +
- +/*
- + * Local variables:
- + * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c auto_irq.c"
- + * version-control: t
- + * kept-new-versions: 5
- + * c-indent-level: 4
- + * tab-width: 4
- + * End:
- + */
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/net/ether1.c linux/arch/arm/drivers/net/ether1.c
- --- linux.orig/arch/arm/drivers/net/ether1.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/net/ether1.c Mon Jul 29 20:27:07 1996
- @@ -0,0 +1,1132 @@
- +/*
- + * linux/arch/arm/drivers/net/ether1.c
- + *
- + * Acorn ether1 driver (82586 chip)
- + *
- + * (c) 1996 Russell King
- + */
- +
- +/*
- + * We basically keep two queues in the cards memory - one for transmit
- + * and one for receive. Each has a head and a tail. The head is where
- + * we/the chip adds packets to be transmitted/received, and the tail
- + * is where the transmitter has got to/where the receiver will stop.
- + * Both of these queues are circular, and since the chip is running
- + * all the time, we have to be careful when we modify the pointers etc
- + * so that the buffer memory is valid all the time.
- + */
- +
- +/*
- + * Change log:
- + * 1.00 RMK Released
- + * 1.01 RMK 19/03/96 Transfers the last odd byte onto/off of the card now.
- + */
- +
- +#include <linux/module.h>
- +#include <linux/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/types.h>
- +#include <linux/fcntl.h>
- +#include <linux/interrupt.h>
- +#include <linux/ptrace.h>
- +#include <linux/ioport.h>
- +#include <linux/in.h>
- +#include <linux/malloc.h>
- +#include <linux/string.h>
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +#include <asm/io.h>
- +#include <asm/dma.h>
- +#include <linux/errno.h>
- +
- +#include <linux/netdevice.h>
- +#include <linux/etherdevice.h>
- +#include <linux/skbuff.h>
- +
- +#include <asm/ecard.h>
- +
- +#define __ETHER1_C
- +#include "ether1.h"
- +
- +static unsigned int net_debug = NET_DEBUG;
- +
- +#define FUNC_PROLOGUE \
- + struct ether1_priv *priv = (struct ether1_priv *)dev->priv
- +
- +#define BUFFER_SIZE 0x10000
- +#define TX_AREA_START 0x00100
- +#define TX_AREA_END 0x05000
- +#define RX_AREA_START 0x05000
- +#define RX_AREA_END 0x0fc00
- +
- +#define tx_done(dev) 0
- +/* ------------------------------------------------------------------------- */
- +static char *version = "ether1 ethernet driver (c) 1995 Russell King v1.01\n";
- +
- +#define BUS_16 16
- +#define BUS_8 8
- +
- +static const int ether1_prods[] = { 0x0003 };
- +static const int ether1_manus[] = { 0x0000 };
- +
- +/* ------------------------------------------------------------------------- */
- +
- +#define DISABLEIRQS 1
- +#define NORMALIRQS 0
- +
- +#define ether1_inw(dev, addr, type, offset, svflgs) ether1_inw_p (dev, addr + (int)(&((type *)0)->offset), svflgs)
- +#define ether1_outw(dev, val, addr, type, offset, svflgs) ether1_outw_p (dev, val, addr + (int)(&((type *)0)->offset), svflgs)
- +
- +static inline unsigned short
- +ether1_inw_p (struct device *dev, int addr, int svflgs)
- +{
- + unsigned long flags;
- + unsigned short ret;
- +
- + if (svflgs) {
- + save_flags_cli (flags);
- + }
- + outb (addr >> 12, REG_PAGE);
- + ret = inw (ETHER1_RAM + ((addr & 4095) >> 1));
- + if (svflgs)
- + restore_flags (flags);
- + return ret;
- +}
- +
- +static inline void
- +ether1_outw_p (struct device *dev, unsigned short val, int addr, int svflgs)
- +{
- + unsigned long flags;
- +
- + if (svflgs) {
- + save_flags_cli (flags);
- + }
- + outb (addr >> 12, REG_PAGE);
- + outw (val, ETHER1_RAM + ((addr & 4095) >> 1));
- + if (svflgs)
- + restore_flags (flags);
- +}
- +
- +static inline void *
- +ether1_inswb (int addr, void *data, unsigned int len)
- +{
- + int used;
- +
- + __asm__ __volatile__(
- + " subs %3, %3, #2\n"
- + " bmi 2f\n"
- + "1: ldr %0, [%1], #4\n"
- + " strb %0, [%2], #1\n"
- + " mov %0, %0, lsr #8\n"
- + " strb %0, [%2], #1\n"
- + " subs %3, %3, #2\n"
- + " bmi 2f\n"
- + " ldr %0, [%1], #4\n"
- + " strb %0, [%2], #1\n"
- + " mov %0, %0, lsr #8\n"
- + " strb %0, [%2], #1\n"
- + " subs %3, %3, #2\n"
- + " bmi 2f\n"
- + " ldr %0, [%1], #4\n"
- + " strb %0, [%2], #1\n"
- + " mov %0, %0, lsr #8\n"
- + " strb %0, [%2], #1\n"
- + " subs %3, %3, #2\n"
- + " bmi 2f\n"
- + " ldr %0, [%1], #4\n"
- + " strb %0, [%2], #1\n"
- + " mov %0, %0, lsr #8\n"
- + " strb %0, [%2], #1\n"
- + " subs %3, %3, #2\n"
- + " bpl 1b\n"
- + "2: adds %3, %3, #1\n"
- + " ldreqb %0, [%1]\n"
- + " streqb %0, [%2]\n"
- + : "=&r" (used), "=&r" (addr), "=&r" (data), "=&r" (len)
- + : "1" (addr << 2), "2" (data), "3" (len));
- +
- + return data;
- +}
- +
- +static inline void *
- +ether1_outswb (int addr, void *data, unsigned int len)
- +{
- + int used;
- +
- + __asm__ __volatile__(
- + " subs %3, %3, #2\n"
- + " bmi 2f\n"
- + "1: ldr %0, [%2], #2\n"
- + " mov %0, %0, lsl #16\n"
- + " orr %0, %0, %0, lsr #16\n"
- + " str %0, [%1], #4\n"
- + " subs %3, %3, #2\n"
- + " bmi 2f\n"
- + " ldr %0, [%2], #2\n"
- + " mov %0, %0, lsl #16\n"
- + " orr %0, %0, %0, lsr #16\n"
- + " str %0, [%1], #4\n"
- + " subs %3, %3, #2\n"
- + " bmi 2f\n"
- + " ldr %0, [%2], #2\n"
- + " mov %0, %0, lsl #16\n"
- + " orr %0, %0, %0, lsr #16\n"
- + " str %0, [%1], #4\n"
- + " subs %3, %3, #2\n"
- + " bmi 2f\n"
- + " ldr %0, [%2], #2\n"
- + " mov %0, %0, lsl #16\n"
- + " orr %0, %0, %0, lsr #16\n"
- + " str %0, [%1], #4\n"
- + " subs %3, %3, #2\n"
- + " bpl 1b\n"
- + "2: adds %3, %3, #1\n"
- + " ldreqb %0, [%2]\n"
- + " streqb %0, [%1]\n"
- + : "=&r" (used), "=&r" (addr), "=&r" (data), "=&r" (len)
- + : "1" (addr << 2), "2" (data), "3" (len));
- +
- + return data;
- +}
- +
- +
- +static void
- +ether1_writebuffer (struct device *dev, void *data, unsigned int start, unsigned int length)
- +{
- + unsigned int page, thislen, offset;
- +
- + offset = start & 4095;
- +
- + for (page = start >> 12; length; page++) {
- + outb (page, REG_PAGE);
- + if (offset + length > 4096) {
- + length -= 4096 - offset;
- + thislen = 4096 - offset;
- + } else {
- + thislen = length;
- + length = 0;
- + }
- +
- + data = ether1_outswb (ETHER1_RAM + (offset >> 1), data, thislen);
- + offset = 0;
- + }
- +}
- +
- +static void
- +ether1_readbuffer (struct device *dev, void *data, unsigned int start, unsigned int length)
- +{
- + unsigned int page, thislen, offset;
- +
- + offset = start & 4095;
- +
- + for (page = start >> 12; length; page++) {
- + outb (page, REG_PAGE);
- + if (offset + length > 4096) {
- + length -= 4096 - offset;
- + thislen = 4096 - offset;
- + } else {
- + thislen = length;
- + length = 0;
- + }
- +
- + data = ether1_inswb (ETHER1_RAM + (offset >> 1), data, thislen);
- + offset = 0;
- + }
- +}
- +
- +static int
- +ether1_ramtest (struct device *dev, unsigned char byte)
- +{
- + unsigned char *buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL);
- + int i, ret = BUFFER_SIZE;
- + int max_errors = 15;
- + int bad = -1;
- + int bad_start = 0;
- +
- + if (!buffer)
- + return 1;
- +
- + memset (buffer, byte, BUFFER_SIZE);
- + ether1_writebuffer (dev, buffer, 0, BUFFER_SIZE);
- + memset (buffer, byte ^ 0xff, BUFFER_SIZE);
- + ether1_readbuffer (dev, buffer, 0, BUFFER_SIZE);
- +
- + for (i = 0; i < BUFFER_SIZE; i++) {
- + if (buffer[i] != byte) {
- + if (max_errors >= 0 && bad != buffer[i]) {
- + if (bad != -1)
- + printk ("\n");
- + printk (KERN_CRIT "%s: RAM failed with (%02X instead of %02X) at 0x%04X",
- + dev->name, buffer[i], byte, i);
- + ret = -ENODEV;
- + max_errors --;
- + bad = buffer[i];
- + bad_start = i;
- + }
- + } else {
- + if (bad != -1) {
- + if (bad_start == i - 1)
- + printk ("\n");
- + else
- + printk (" - 0x%04X\n", i - 1);
- + bad = -1;
- + }
- + }
- + }
- +
- + if (bad != -1)
- + printk (" - 0x%04X\n", BUFFER_SIZE);
- + kfree (buffer);
- +
- + return ret;
- +}
- +
- +static int
- +ether1_reset (struct device *dev)
- +{
- + outb (CTRL_RST|CTRL_ACK, REG_CONTROL);
- + return BUS_16;
- +}
- +
- +static int
- +ether1_init_2 (struct device *dev)
- +{
- + int i;
- + dev->mem_start = 0;
- +
- + i = ether1_ramtest (dev, 0x5a);
- +
- + if (i > 0)
- + i = ether1_ramtest (dev, 0x1e);
- +
- + if (i <= 0)
- + return -ENODEV;
- +
- + dev->mem_end = i;
- +
- + return 0;
- +}
- +
- +/*
- + * These are the structures that are loaded into the ether RAM card to
- + * initialise the 82586
- + */
- +
- +/* at 0x0100 */
- +#define NOP_ADDR (TX_AREA_START)
- +#define NOP_SIZE (0x06)
- +static nop_t init_nop = {
- + 0,
- + CMD_NOP,
- + NOP_ADDR
- +};
- +
- +/* at 0x003a */
- +#define TDR_ADDR (0x003a)
- +#define TDR_SIZE (0x08)
- +static tdr_t init_tdr = {
- + 0,
- + CMD_TDR | CMD_INTR,
- + NOP_ADDR,
- + 0
- +};
- +
- +/* at 0x002e */
- +#define MC_ADDR (0x002e)
- +#define MC_SIZE (0x0c)
- +static mc_t init_mc = {
- + 0,
- + CMD_SETMULTICAST,
- + TDR_ADDR,
- + 0,
- + { { 0, } }
- +};
- +
- +/* at 0x0022 */
- +#define SA_ADDR (0x0022)
- +#define SA_SIZE (0x0c)
- +static sa_t init_sa = {
- + 0,
- + CMD_SETADDRESS,
- + MC_ADDR,
- + { 0, }
- +};
- +
- +/* at 0x0010 */
- +#define CFG_ADDR (0x0010)
- +#define CFG_SIZE (0x12)
- +static cfg_t init_cfg = {
- + 0,
- + CMD_CONFIG,
- + SA_ADDR,
- + 4,
- + 8,
- + CFG8_SRDY,
- + CFG9_PREAMB8 | CFG9_ADDRLENBUF | CFG9_ADDRLEN(6),
- + 0,
- +};
- +
- +/* at 0x0000 */
- +#define SCB_ADDR (0x0000)
- +#define SCB_SIZE (0x10)
- +static scb_t init_scb = {
- + 0,
- + 0,
- + CFG_ADDR,
- + RX_AREA_START,
- + 0,
- + 0,
- + 0,
- + 0
- +};
- +
- +/* at 0xffee */
- +#define ISCP_ADDR (0xffee)
- +#define ISCP_SIZE (0x08)
- +static iscp_t init_iscp = {
- + 1,
- + SCB_ADDR,
- + 0x0000,
- + 0x0000
- +};
- +
- +/* at 0xfff6 */
- +#define SCP_ADDR (0xfff6)
- +#define SCP_SIZE (0x0a)
- +static scp_t init_scp = {
- + SCP_SY_16BBUS,
- + { 0, 0 },
- + ISCP_ADDR,
- + 0
- +};
- +
- +#define RFD_SIZE (0x16)
- +static rfd_t init_rfd = {
- + 0,
- + 0,
- + 0,
- + 0,
- + { 0, },
- + { 0, },
- + 0
- +};
- +
- +#define RBD_SIZE (0x0a)
- +static rbd_t init_rbd = {
- + 0,
- + 0,
- + 0,
- + 0,
- + ETH_FRAME_LEN + 8
- +};
- +
- +#define TX_SIZE (0x08)
- +#define TBD_SIZE (0x08)
- +
- +static int
- +ether1_init_for_open (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + int i, status, addr, next, next2;
- +
- + outb (CTRL_RST|CTRL_ACK, REG_CONTROL);
- +
- + for (i = 0; i < 6; i++)
- + init_sa.sa_addr[i] = dev->dev_addr[i];
- +
- + /* load data structures into ether1 RAM */
- + ether1_writebuffer (dev, &init_scp, SCP_ADDR, SCP_SIZE);
- + ether1_writebuffer (dev, &init_iscp, ISCP_ADDR, ISCP_SIZE);
- + ether1_writebuffer (dev, &init_scb, SCB_ADDR, SCB_SIZE);
- + ether1_writebuffer (dev, &init_cfg, CFG_ADDR, CFG_SIZE);
- + ether1_writebuffer (dev, &init_sa, SA_ADDR, SA_SIZE);
- + ether1_writebuffer (dev, &init_mc, MC_ADDR, MC_SIZE);
- + ether1_writebuffer (dev, &init_tdr, TDR_ADDR, TDR_SIZE);
- + ether1_writebuffer (dev, &init_nop, NOP_ADDR, NOP_SIZE);
- +
- + /*
- + * setup circularly linked list of { rfd, rbd, buffer }, with
- + * all rfds circularly linked, rbds circularly linked.
- + * First rfd is linked to scp, first rbd is linked to first
- + * rfd. Last rbd has a suspend command.
- + */
- + addr = RX_AREA_START;
- + do {
- + next = addr + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10;
- + next2 = next + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10;
- +
- + if (next2 >= RX_AREA_END) {
- + next = RX_AREA_START;
- + init_rfd.rfd_command = RFD_CMDEL | RFD_CMDSUSPEND;
- + priv->rx_tail = addr;
- + } else
- + init_rfd.rfd_command = 0;
- + if (addr == RX_AREA_START)
- + init_rfd.rfd_rbdoffset = addr + RFD_SIZE;
- + else
- + init_rfd.rfd_rbdoffset = 0;
- + init_rfd.rfd_link = next;
- + init_rbd.rbd_link = next + RFD_SIZE;
- + init_rbd.rbd_bufl = addr + RFD_SIZE + RBD_SIZE;
- +
- + ether1_writebuffer (dev, &init_rfd, addr, RFD_SIZE);
- + ether1_writebuffer (dev, &init_rbd, addr + RFD_SIZE, RBD_SIZE);
- + addr = next;
- + } while (next2 < RX_AREA_END);
- +
- + priv->tx_link = NOP_ADDR;
- + priv->tx_head = NOP_ADDR + NOP_SIZE;
- + priv->tx_tail = TDR_ADDR;
- + priv->rx_head = RX_AREA_START;
- +
- + /* release reset & give 586 a prod */
- + priv->resetting = 1;
- + priv->initialising = 1;
- + outb (CTRL_RST, REG_CONTROL);
- + outb (0, REG_CONTROL);
- + outb (CTRL_CA, REG_CONTROL);
- +
- + /* 586 should now unset iscp.busy */
- + i = jiffies + HZ/2;
- + while (ether1_inw (dev, ISCP_ADDR, iscp_t, iscp_busy, DISABLEIRQS) == 1) {
- + if (jiffies > i) {
- + printk (KERN_WARNING "%s: can't initialise 82586: iscp is busy\n", dev->name);
- + return 1;
- + }
- + }
- +
- + /* check status of commands that we issued */
- + i += HZ/10;
- + while (((status = ether1_inw (dev, CFG_ADDR, cfg_t, cfg_status, DISABLEIRQS))
- + & STAT_COMPLETE) == 0) {
- + if (jiffies > i)
- + break;
- + }
- +
- + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
- + printk (KERN_WARNING "%s: can't initialise 82586: config status %04X\n", dev->name, status);
- + ether1_reset (dev);
- + return 1;
- + }
- +
- + i += HZ/10;
- + while (((status = ether1_inw (dev, SA_ADDR, sa_t, sa_status, DISABLEIRQS))
- + & STAT_COMPLETE) == 0) {
- + if (jiffies > i)
- + break;
- + }
- +
- + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
- + printk (KERN_WARNING "%s: can't initialise 82586: set address status %04X\n", dev->name, status);
- + ether1_reset (dev);
- + return 1;
- + }
- +
- + i += HZ/10;
- + while (((status = ether1_inw (dev, MC_ADDR, mc_t, mc_status, DISABLEIRQS))
- + & STAT_COMPLETE) == 0) {
- + if (jiffies > i)
- + break;
- + }
- +
- + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
- + printk (KERN_WARNING "%s: can't initialise 82586: set multicast status %04X\n", dev->name, status);
- + ether1_reset (dev);
- + return 1;
- + }
- +
- + i += HZ;
- + while (((status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_status, DISABLEIRQS))
- + & STAT_COMPLETE) == 0) {
- + if (jiffies > i)
- + break;
- + }
- +
- + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) {
- + printk (KERN_WARNING "%s: can't tdr (ignored)\n", dev->name);
- + return 0;
- + }
- +
- + status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_result, DISABLEIRQS);
- + if (status & TDR_XCVRPROB)
- + printk (KERN_WARNING "%s: i/f failed tdr: transceiver problem\n", dev->name);
- + else
- + if (status & (TDR_SHORT|TDR_OPEN)) {
- +#ifdef FANCY
- + printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d.%d us away\n", dev->name,
- + status & TDR_SHORT ? "short" : "open", (status & TDR_TIME) / 10,
- + (status & TDR_TIME) % 10);
- +#else
- + printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d clks away\n", dev->name,
- + status & TDR_SHORT ? "short" : "open", (status & TDR_TIME));
- +#endif
- + }
- +
- + return 0;
- +}
- +
- +static int
- +ether1_probe1 (struct device *dev)
- +{
- + static unsigned int version_printed = 0;
- + struct ether1_priv *priv;
- + int i;
- +
- + if (!dev->priv)
- + dev->priv = kmalloc (sizeof (struct ether1_priv), GFP_KERNEL);
- +
- + if (!dev->priv)
- + return 1;
- +
- + priv = (struct ether1_priv *)dev->priv;
- + memset (priv, 0, sizeof (struct ether1_priv));
- +
- + if ((priv->bus_type = ether1_reset (dev)) == 0) {
- + kfree (dev->priv);
- + return 1;
- + }
- +
- + if (net_debug && version_printed++ == 0)
- + printk (KERN_INFO "%s", version);
- +
- + printk (KERN_INFO "%s: ether1 found [%d, %04lx, %d]", dev->name, priv->bus_type,
- + dev->base_addr, dev->irq);
- +
- + request_region (dev->base_addr, 16, "ether1");
- + request_region (dev->base_addr + 0x800, 4096, "ether1(ram)");
- +
- + for (i = 0; i < 6; i++)
- + printk (i==0?" %02x":i==5?":%02x\n":":%02x", dev->dev_addr[i]);
- +
- + if (ether1_init_2 (dev)) {
- + kfree (dev->priv);
- + return 1;
- + }
- +
- + dev->open = ether1_open;
- + dev->stop = ether1_close;
- + dev->hard_start_xmit = ether1_sendpacket;
- + dev->get_stats = ether1_getstats;
- + dev->set_multicast_list = ether1_setmulticastlist;
- +
- + /* 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;
- +}
- +
- +/* ------------------------------------------------------------------------- */
- +
- +static void
- +ether1_addr (struct device *dev)
- +{
- + int i;
- +
- + for (i = 0; i < 6; i++)
- + dev->dev_addr[i] = inb (IDPROM_ADDRESS + i);
- +}
- +
- +int
- +ether1_probe (struct device *dev)
- +{
- +#ifndef MODULE
- + struct expansion_card *ec;
- +
- + if (!dev)
- + return ENODEV;
- +
- + if ((ec = ecard_find (0, sizeof (ether1_prods) / sizeof (int), ether1_prods, ether1_manus)) == NULL)
- + return ENODEV;
- +
- + dev->base_addr = ((unsigned long)ecard_address (ec->slot_no, ECARD_IOC, ECARD_FAST)) >> 2;
- + dev->irq = ec->irq;
- +
- + ecard_claim (ec);
- +
- +#endif
- + ether1_addr (dev);
- +
- + if (ether1_probe1 (dev) == 0)
- + return 0;
- + return ENODEV;
- +}
- +
- +/* ------------------------------------------------------------------------- */
- +
- +static int
- +ether1_txalloc (struct device *dev, int size)
- +{
- + FUNC_PROLOGUE;
- + int start, tail;
- +
- + size = (size + 1) & ~1;
- + tail = priv->tx_tail;
- +
- + if (priv->tx_head + size > TX_AREA_END) {
- + if (tail > priv->tx_head)
- + return -1;
- + start = TX_AREA_START;
- + if (start + size > tail)
- + return -1;
- + priv->tx_head = start + size;
- + } else {
- + if (priv->tx_head < tail && (priv->tx_head + size) > tail)
- + return -1;
- + start = priv->tx_head;
- + priv->tx_head += size;
- + }
- +
- + return start;
- +}
- +
- +static void
- +ether1_restart (struct device *dev, char *reason)
- +{
- + FUNC_PROLOGUE;
- + priv->stats.tx_errors ++;
- +
- + if (reason)
- + printk (KERN_WARNING "%s: %s - resetting device\n", dev->name, reason);
- + else
- + printk (" - resetting device\n");
- +
- + ether1_reset (dev);
- +
- + dev->start = 0;
- + dev->tbusy = 0;
- +
- + if (ether1_init_for_open (dev))
- + printk (KERN_ERR "%s: unable to restart interface\n", dev->name);
- +
- + dev->start = 1;
- +}
- +
- +static int
- +ether1_open (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- +#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;
- + }
- +
- + dev->tbusy = 0;
- + dev->interrupt = 0;
- + dev->start = 1;
- +
- + return 0;
- +}
- +
- +static int
- +ether1_sendpacket (struct sk_buff *skb, struct device *dev)
- +{
- + FUNC_PROLOGUE;
- +
- + if (dev->tbusy) {
- + /*
- + * If we get here, some higher level has decided that we are broken.
- + * There should really be a "kick me" function call instead.
- + */
- + int tickssofar = jiffies - dev->trans_start;
- +
- + if (tickssofar < 5)
- + return 1;
- +
- + /* Try to restart the adapter. */
- + ether1_restart (dev, "transmit timeout, network cable problem?");
- + dev->trans_start = jiffies;
- + }
- +
- + /*
- + * If some higher layer thinks we've missed a tx-done interrupt
- + * we are passed NULL. Caution: dev_tint() handles the cli()/sti()
- + * itself.
- + */
- + if (skb == NULL) {
- + dev_tint (dev);
- + return 0;
- + }
- +
- + /*
- + * Block a timer-based transmit from overlapping. This could better be
- + * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
- + */
- + if (set_bit (0, (void *)&dev->tbusy) != 0)
- + printk (KERN_WARNING "%s: transmitter access conflict.\n", dev->name);
- + else {
- + int len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
- + int tmp, tst, nopaddr, txaddr, tbdaddr, dataddr;
- + unsigned long flags;
- + tx_t tx;
- + tbd_t tbd;
- + nop_t nop;
- + /*
- + * insert packet followed by a nop
- + */
- + txaddr = ether1_txalloc (dev, TX_SIZE);
- + tbdaddr = ether1_txalloc (dev, TBD_SIZE);
- + dataddr = ether1_txalloc (dev, len);
- + nopaddr = ether1_txalloc (dev, NOP_SIZE);
- +
- + tx.tx_status = 0;
- + tx.tx_command = CMD_TX | CMD_INTR;
- + tx.tx_link = nopaddr;
- + tx.tx_tbdoffset = tbdaddr;
- + tbd.tbd_opts = TBD_EOL | len;
- + tbd.tbd_link = I82586_NULL;
- + tbd.tbd_bufl = dataddr;
- + tbd.tbd_bufh = 0;
- + nop.nop_status = 0;
- + nop.nop_command = CMD_NOP;
- + nop.nop_link = nopaddr;
- +
- + save_flags_cli (flags);
- + ether1_writebuffer (dev, &tx, txaddr, TX_SIZE);
- + ether1_writebuffer (dev, &tbd, tbdaddr, TBD_SIZE);
- + ether1_writebuffer (dev, skb->data, dataddr, len);
- + ether1_writebuffer (dev, &nop, nopaddr, NOP_SIZE);
- + tmp = priv->tx_link;
- + priv->tx_link = nopaddr;
- +
- + /* now reset the previous nop pointer */
- + ether1_outw (dev, txaddr, tmp, nop_t, nop_link, NORMALIRQS);
- +
- + restore_flags (flags);
- +
- + /* handle transmit */
- + dev->trans_start = jiffies;
- +
- + /* check to see if we have room for a full sized ether frame */
- + tmp = priv->tx_head;
- + tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN);
- + priv->tx_head = tmp;
- + if (tst != -1)
- + dev->tbusy = 0;
- + }
- + dev_kfree_skb (skb, FREE_WRITE);
- +
- + return 0;
- +}
- +
- +static void
- +ether1_xmit_done (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + nop_t nop;
- + int caddr, tst;
- +
- + caddr = priv->tx_tail;
- +
- +again:
- + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
- +
- + switch (nop.nop_command & CMD_MASK) {
- + case CMD_TDR:
- + /* special case */
- + if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS)
- + != (unsigned short)I82586_NULL) {
- + ether1_outw (dev, SCB_CMDCUCSTART | SCB_CMDRXSTART, SCB_ADDR, scb_t,
- + scb_command, NORMALIRQS);
- + outb (CTRL_CA, REG_CONTROL);
- + }
- + priv->tx_tail = NOP_ADDR;
- + return;
- +
- + case CMD_NOP:
- + if (nop.nop_link == caddr) {
- + if (priv->initialising == 0)
- + printk (KERN_WARNING "%s: strange command complete with no tx command!\n", dev->name);
- + else
- + priv->initialising = 0;
- + return;
- + }
- + caddr = nop.nop_link;
- + goto again;
- +
- + case CMD_TX:
- + if (nop.nop_status & STAT_COMPLETE)
- + break;
- + ether1_restart (dev, "strange command complete without completed command!");
- + return;
- +
- + default:
- + printk (KERN_WARNING "%s: strange command %d complete! (offset %04X)", dev->name,
- + nop.nop_command & CMD_MASK, caddr);
- + ether1_restart (dev, NULL);
- + return;
- + }
- +
- + while (nop.nop_status & STAT_COMPLETE) {
- + if (nop.nop_status & STAT_OK) {
- + priv->stats.tx_packets ++;
- + priv->stats.collisions += (nop.nop_status & STAT_COLLISIONS);
- + } else {
- + priv->stats.tx_errors ++;
- +
- + if (nop.nop_status & STAT_COLLAFTERTX)
- + priv->stats.collisions ++;
- + if (nop.nop_status & STAT_NOCARRIER)
- + priv->stats.tx_carrier_errors ++;
- + if (nop.nop_status & STAT_TXLOSTCTS)
- + printk (KERN_WARNING "%s: cts lost\n", dev->name);
- + if (nop.nop_status & STAT_TXSLOWDMA)
- + priv->stats.tx_fifo_errors ++;
- + if (nop.nop_status & STAT_COLLEXCESSIVE)
- + priv->stats.collisions += 16;
- + }
- +
- + if (nop.nop_link == caddr) {
- + printk (KERN_ERR "%s: tx buffer chaining error: tx command points to itself\n", dev->name);
- + break;
- + }
- +
- + caddr = nop.nop_link;
- + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
- + if ((nop.nop_command & CMD_MASK) != CMD_NOP) {
- + printk (KERN_ERR "%s: tx buffer chaining error: no nop after tx command\n", dev->name);
- + break;
- + }
- +
- + if (caddr == nop.nop_link)
- + break;
- +
- + caddr = nop.nop_link;
- + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE);
- + if ((nop.nop_command & CMD_MASK) != CMD_TX) {
- + printk (KERN_ERR "%s: tx buffer chaining error: no tx command after nop\n", dev->name);
- + break;
- + }
- + }
- + priv->tx_tail = caddr;
- +
- + caddr = priv->tx_head;
- + tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN);
- + priv->tx_head = caddr;
- + if (tst != -1)
- + dev->tbusy = 0;
- +
- + mark_bh (NET_BH);
- +}
- +
- +static void
- +ether1_recv_done (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + int status;
- + int nexttail, rbdaddr;
- + rbd_t rbd;
- +
- + do {
- + status = ether1_inw (dev, priv->rx_head, rfd_t, rfd_status, NORMALIRQS);
- + if ((status & RFD_COMPLETE) == 0)
- + break;
- +
- + rbdaddr = ether1_inw (dev, priv->rx_head, rfd_t, rfd_rbdoffset, NORMALIRQS);
- + ether1_readbuffer (dev, &rbd, rbdaddr, RBD_SIZE);
- +
- + if ((rbd.rbd_status & (RBD_EOF | RBD_ACNTVALID)) == (RBD_EOF | RBD_ACNTVALID)) {
- + int length = rbd.rbd_status & RBD_ACNT;
- + struct sk_buff *skb;
- +
- + length = (length + 1) & ~1;
- + skb = dev_alloc_skb (length + 2);
- +
- + if (skb) {
- + skb->dev = dev;
- + skb_reserve (skb, 2);
- +
- + ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length);
- +
- + skb->protocol = eth_type_trans (skb, dev);
- + netif_rx (skb);
- + priv->stats.rx_packets ++;
- + } else
- + priv->stats.rx_dropped ++;
- + } else {
- + printk (KERN_WARNING "%s: %s\n", dev->name,
- + (rbd.rbd_status & RBD_EOF) ? "oversized packet" : "acnt not valid");
- + priv->stats.rx_dropped ++;
- + }
- +
- + nexttail = ether1_inw (dev, priv->rx_tail, rfd_t, rfd_link, NORMALIRQS);
- + /* nexttail should be rx_head */
- + if (nexttail != priv->rx_head)
- + printk (KERN_ERR "%s: receiver buffer chaining error (%04X != %04X)\n",
- + dev->name, nexttail, priv->rx_head);
- + ether1_outw (dev, RFD_CMDEL | RFD_CMDSUSPEND, nexttail, rfd_t, rfd_command, NORMALIRQS);
- + ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_command, NORMALIRQS);
- + ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_status, NORMALIRQS);
- + ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_rbdoffset, NORMALIRQS);
- +
- + priv->rx_tail = nexttail;
- + priv->rx_head = ether1_inw (dev, priv->rx_head, rfd_t, rfd_link, NORMALIRQS);
- + } while (1);
- +}
- +
- +static void
- +ether1_interrupt (int irq, void *dev_id, struct pt_regs *regs)
- +{
- + struct device *dev = (struct device *)dev_id;
- + FUNC_PROLOGUE;
- + int status;
- +
- + dev->interrupt = 1;
- +
- + status = ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS);
- + if (status ) {
- + ether1_outw (dev, status & (SCB_STRNR | SCB_STCNA | SCB_STFR | SCB_STCX),
- + SCB_ADDR, scb_t, scb_command, NORMALIRQS);
- + outb (CTRL_CA | CTRL_ACK, REG_CONTROL);
- + if (status & SCB_STCX) {
- + ether1_xmit_done (dev);
- + }
- + if (status & SCB_STCNA) {
- + if (priv->resetting == 0)
- + printk (KERN_WARNING "%s: CU went not ready ???\n", dev->name);
- + if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS)
- + != (unsigned short)I82586_NULL) {
- + ether1_outw (dev, SCB_CMDCUCSTART, SCB_ADDR, scb_t, scb_command, NORMALIRQS);
- + outb (CTRL_CA, REG_CONTROL);
- + }
- + if (priv->resetting == 2)
- + priv->resetting = 0;
- + }
- + if (status & SCB_STFR) {
- + ether1_recv_done (dev);
- + }
- + if (status & SCB_STRNR) {
- + printk (KERN_WARNING "%s: RU went not ready\n", dev->name);
- + printk (KERN_WARNING "RU ptr = %04X\n", ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset,
- + NORMALIRQS));
- + }
- + } else
- + outb (CTRL_ACK, REG_CONTROL);
- +
- + dev->interrupt = 0;
- +}
- +
- +static int
- +ether1_close (struct device *dev)
- +{
- +#ifdef CLAIM_IRQ_AT_OPEN
- + free_irq (dev->irq, dev);
- +#endif
- +
- + ether1_reset (dev);
- +
- + dev->start = 0;
- + dev->tbusy = 0;
- +
- + MOD_DEC_USE_COUNT;
- +
- + return 0;
- +}
- +
- +static struct enet_statistics *
- +ether1_getstats (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + return &priv->stats;
- +}
- +
- +/*
- + * Set or clear the multicast filter for this adaptor.
- + * num_addrs == -1 Promiscuous mode, receive all packets.
- + * num_addrs == 0 Normal mode, clear multicast list.
- + * num_addrs > 0 Multicast mode, receive normal and MC packets, and do
- + * best-effort filtering.
- + */
- +static void
- +ether1_setmulticastlist (struct device *dev)
- +{
- +}
- +
- +/* ------------------------------------------------------------------------- */
- +
- +#ifdef MODULE
- +
- +static char ethernames[MAX_ECARDS][9];
- +static struct device *my_ethers[MAX_ECARDS];
- +static struct expansion_card *ec[MAX_ECARDS];
- +
- +int
- +init_module (void)
- +{
- + int i;
- +
- + for (i = 0; i < MAX_ECARDS; i++) {
- + my_ethers[i] = NULL;
- + ec[i] = NULL;
- + strcpy (ethernames[i], " ");
- + }
- +
- + i = 0;
- +
- + do {
- + if ((ec[i] = ecard_find(0, sizeof(ether1_prods) / sizeof(int), ether1_prods, ether1_manus))
- + == 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 = ((unsigned long)ecard_address (ec[i]->slot_no, ECARD_IOC, ECARD_FAST)) >> 2;
- + 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;
- +}
- +
- +void
- +cleanup_module (void)
- +{
- + int i;
- +
- + for (i = 0; i < MAX_ECARDS; i++) {
- + if (my_ethers[i]) {
- + unregister_netdev (my_ethers[i]);
- + my_ethers[i] = NULL;
- + }
- + if (ec[i]) {
- + ecard_release (ec[i]);
- + ec[i] = NULL;
- + }
- + }
- +}
- +#endif /* MODULE */
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/net/ether1.h linux/arch/arm/drivers/net/ether1.h
- --- linux.orig/arch/arm/drivers/net/ether1.h Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/net/ether1.h Fri Jul 19 20:44:22 1996
- @@ -0,0 +1,281 @@
- +/*
- + * linux/arch/arm/drivers/net/ether1.h
- + *
- + * network driver for Acorn Ether1 cards.
- + *
- + * (c) 1996 Russell King
- + */
- +
- +#ifndef _LINUX_ether1_H
- +#define _LINUX_ether1_H
- +
- +#ifdef __ETHER1_C
- +/* use 0 for production, 1 for verification, >2 for debug */
- +#ifndef NET_DEBUG
- +#define NET_DEBUG 0
- +#endif
- +
- +/* Page register */
- +#define REG_PAGE (dev->base_addr + 0x00)
- +
- +/* Control register */
- +#define REG_CONTROL (dev->base_addr + 0x01)
- +#define CTRL_RST 0x01
- +#define CTRL_LOOPBACK 0x02
- +#define CTRL_CA 0x04
- +#define CTRL_ACK 0x08
- +
- +#define ETHER1_RAM (dev->base_addr + 0x800)
- +
- +/* HW address */
- +#define IDPROM_ADDRESS (dev->base_addr + 0x09)
- +
- +struct ether1_priv {
- + struct enet_statistics stats;
- + int tx_link;
- + int tx_head;
- + volatile int tx_tail;
- + volatile int rx_head;
- + volatile int rx_tail;
- + char bus_type;
- + char resetting;
- + char initialising;
- +};
- +
- +static int ether1_open (struct device *dev);
- +static int ether1_sendpacket (struct sk_buff *skb, struct device *dev);
- +static void ether1_interrupt (int irq, void *dev_id, struct pt_regs *regs);
- +static int ether1_close (struct device *dev);
- +static struct enet_statistics *ether1_getstats (struct device *dev);
- +static void ether1_setmulticastlist (struct device *dev);
- +
- +#define I82586_NULL (-1)
- +
- +typedef struct { /* tdr */
- + unsigned short tdr_status;
- + unsigned short tdr_command;
- + unsigned short tdr_link;
- + unsigned short tdr_result;
- +#define TDR_TIME (0x7ff)
- +#define TDR_SHORT (1 << 12)
- +#define TDR_OPEN (1 << 13)
- +#define TDR_XCVRPROB (1 << 14)
- +#define TDR_LNKOK (1 << 15)
- +} tdr_t;
- +
- +typedef struct { /* transmit */
- + unsigned short tx_status;
- + unsigned short tx_command;
- + unsigned short tx_link;
- + unsigned short tx_tbdoffset;
- +} tx_t;
- +
- +typedef struct { /* tbd */
- + unsigned short tbd_opts;
- +#define TBD_CNT (0x3fff)
- +#define TBD_EOL (1 << 15)
- + unsigned short tbd_link;
- + unsigned short tbd_bufl;
- + unsigned short tbd_bufh;
- +} tbd_t;
- +
- +typedef struct { /* rfd */
- + unsigned short rfd_status;
- +#define RFD_NOEOF (1 << 6)
- +#define RFD_FRAMESHORT (1 << 7)
- +#define RFD_DMAOVRN (1 << 8)
- +#define RFD_NORESOURCES (1 << 9)
- +#define RFD_ALIGNERROR (1 << 10)
- +#define RFD_CRCERROR (1 << 11)
- +#define RFD_OK (1 << 13)
- +#define RFD_FDCONSUMED (1 << 14)
- +#define RFD_COMPLETE (1 << 15)
- + unsigned short rfd_command;
- +#define RFD_CMDSUSPEND (1 << 14)
- +#define RFD_CMDEL (1 << 15)
- + unsigned short rfd_link;
- + unsigned short rfd_rbdoffset;
- + unsigned char rfd_dest[6];
- + unsigned char rfd_src[6];
- + unsigned short rfd_len;
- +} rfd_t;
- +
- +typedef struct { /* rbd */
- + unsigned short rbd_status;
- +#define RBD_ACNT (0x3fff)
- +#define RBD_ACNTVALID (1 << 14)
- +#define RBD_EOF (1 << 15)
- + unsigned short rbd_link;
- + unsigned short rbd_bufl;
- + unsigned short rbd_bufh;
- + unsigned short rbd_len;
- +} rbd_t;
- +
- +typedef struct { /* nop */
- + unsigned short nop_status;
- + unsigned short nop_command;
- + unsigned short nop_link;
- +} nop_t;
- +
- +typedef struct { /* set multicast */
- + unsigned short mc_status;
- + unsigned short mc_command;
- + unsigned short mc_link;
- + unsigned short mc_cnt;
- + unsigned char mc_addrs[1][6];
- +} mc_t;
- +
- +typedef struct { /* set address */
- + unsigned short sa_status;
- + unsigned short sa_command;
- + unsigned short sa_link;
- + unsigned char sa_addr[6];
- +} sa_t;
- +
- +typedef struct { /* config command */
- + unsigned short cfg_status;
- + unsigned short cfg_command;
- + unsigned short cfg_link;
- + unsigned char cfg_bytecnt; /* size foll data: 4 - 12 */
- + unsigned char cfg_fifolim; /* FIFO threshold */
- + unsigned char cfg_byte8;
- +#define CFG8_SRDY (1 << 6)
- +#define CFG8_SAVEBADF (1 << 7)
- + unsigned char cfg_byte9;
- +#define CFG9_ADDRLEN(x) (x)
- +#define CFG9_ADDRLENBUF (1 << 3)
- +#define CFG9_PREAMB2 (0 << 4)
- +#define CFG9_PREAMB4 (1 << 4)
- +#define CFG9_PREAMB8 (2 << 4)
- +#define CFG9_PREAMB16 (3 << 4)
- +#define CFG9_ILOOPBACK (1 << 6)
- +#define CFG9_ELOOPBACK (1 << 7)
- + unsigned char cfg_byte10;
- +#define CFG10_LINPRI(x) (x)
- +#define CFG10_ACR(x) (x << 4)
- +#define CFG10_BOFMET (1 << 7)
- + unsigned char cfg_ifs;
- + unsigned char cfg_slotl;
- + unsigned char cfg_byte13;
- +#define CFG13_SLOTH(x) (x)
- +#define CFG13_RETRY(x) (x << 4)
- + unsigned char cfg_byte14;
- +#define CFG14_PROMISC (1 << 0)
- +#define CFG14_DISBRD (1 << 1)
- +#define CFG14_MANCH (1 << 2)
- +#define CFG14_TNCRS (1 << 3)
- +#define CFG14_NOCRC (1 << 4)
- +#define CFG14_CRC16 (1 << 5)
- +#define CFG14_BTSTF (1 << 6)
- +#define CFG14_FLGPAD (1 << 7)
- + unsigned char cfg_byte15;
- +#define CFG15_CSTF(x) (x)
- +#define CFG15_ICSS (1 << 3)
- +#define CFG15_CDTF(x) (x << 4)
- +#define CFG15_ICDS (1 << 7)
- + unsigned short cfg_minfrmlen;
- +} cfg_t;
- +
- +typedef struct { /* scb */
- + unsigned short scb_status; /* status of 82586 */
- +#define SCB_STRXMASK (7 << 4)
- +#define SCB_STRXIDLE (0 << 4)
- +#define SCB_STRXSUSP (1 << 4)
- +#define SCB_STRXNRES (2 << 4)
- +#define SCB_STRXRDY (4 << 4)
- +#define SCB_STCUMASK (7 << 8)
- +#define SCB_STCUIDLE (0 << 8)
- +#define SCB_STCUSUSP (1 << 8)
- +#define SCB_STCUACTV (2 << 8)
- +#define SCB_STRNR (1 << 12)
- +#define SCB_STCNA (1 << 13)
- +#define SCB_STFR (1 << 14)
- +#define SCB_STCX (1 << 15)
- + unsigned short scb_command; /* Next command */
- +#define SCB_CMDRXSTART (1 << 4)
- +#define SCB_CMDRXRESUME (2 << 4)
- +#define SCB_CMDRXSUSPEND (3 << 4)
- +#define SCB_CMDRXABORT (4 << 4)
- +#define SCB_CMDCUCSTART (1 << 8)
- +#define SCB_CMDCUCRESUME (2 << 8)
- +#define SCB_CMDCUCSUSPEND (3 << 8)
- +#define SCB_CMDCUCABORT (4 << 8)
- +#define SCB_CMDACKRNR (1 << 12)
- +#define SCB_CMDACKCNA (1 << 13)
- +#define SCB_CMDACKFR (1 << 14)
- +#define SCB_CMDACKCX (1 << 15)
- + unsigned short scb_cbl_offset; /* Offset of first command unit */
- + unsigned short scb_rfa_offset; /* Offset of first receive frame area */
- + unsigned short scb_crc_errors; /* Properly aligned frame with CRC error */
- + unsigned short scb_aln_errors; /* Misaligned frames */
- + unsigned short scb_rsc_errors; /* Frames lost due to no space */
- + unsigned short scb_ovn_errors; /* Frames lost due to slow bus */
- +} scb_t;
- +
- +typedef struct { /* iscp */
- + unsigned short iscp_busy; /* set by CPU before CA */
- + unsigned short iscp_offset; /* offset of SCB */
- + unsigned short iscp_basel; /* base of SCB */
- + unsigned short iscp_baseh;
- +} iscp_t;
- +
- + /* this address must be 0xfff6 */
- +typedef struct { /* scp */
- + unsigned short scp_sysbus; /* bus size */
- +#define SCP_SY_16BBUS 0x00
- +#define SCP_SY_8BBUS 0x01
- + unsigned short scp_junk[2]; /* junk */
- + unsigned short scp_iscpl; /* lower 16 bits of iscp */
- + unsigned short scp_iscph; /* upper 16 bits of iscp */
- +} scp_t;
- +
- +/* commands */
- +#define CMD_NOP 0
- +#define CMD_SETADDRESS 1
- +#define CMD_CONFIG 2
- +#define CMD_SETMULTICAST 3
- +#define CMD_TX 4
- +#define CMD_TDR 5
- +#define CMD_DUMP 6
- +#define CMD_DIAGNOSE 7
- +
- +#define CMD_MASK 7
- +
- +#define CMD_INTR (1 << 13)
- +#define CMD_SUSP (1 << 14)
- +#define CMD_EOL (1 << 15)
- +
- +#define STAT_COLLISIONS (15)
- +#define STAT_COLLEXCESSIVE (1 << 5)
- +#define STAT_COLLAFTERTX (1 << 6)
- +#define STAT_TXDEFERRED (1 << 7)
- +#define STAT_TXSLOWDMA (1 << 8)
- +#define STAT_TXLOSTCTS (1 << 9)
- +#define STAT_NOCARRIER (1 << 10)
- +#define STAT_FAIL (1 << 11)
- +#define STAT_ABORTED (1 << 12)
- +#define STAT_OK (1 << 13)
- +#define STAT_BUSY (1 << 14)
- +#define STAT_COMPLETE (1 << 15)
- +#endif
- +#endif
- +
- +/*
- + * Ether1 card definitions:
- + *
- + * FAST accesses:
- + * +0 Page register
- + * 16 pages
- + * +4 Control
- + * '1' = reset
- + * '2' = loopback
- + * '4' = CA
- + * '8' = int ack
- + *
- + * RAM at address + 0x2000
- + * Pod. Prod id = 3
- + * Words after ID block [base + 8 words]
- + * +0 pcb issue (0x0c and 0xf3 invalid)
- + * +1 - +6 eth hw address
- + */
- +
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/net/ether3.c linux/arch/arm/drivers/net/ether3.c
- --- linux.orig/arch/arm/drivers/net/ether3.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/net/ether3.c Mon Jul 29 20:46:34 1996
- @@ -0,0 +1,892 @@
- +/*
- + * linux/drivers/net/ether3.c
- + *
- + * SEEQ nq8005 ethernet driver
- + *
- + * By Russell King, with some suggestions from borris@ant.co.uk
- + *
- + * Changelog:
- + * 29/02/96: RMK Won't pass packets that are from our ethernet address
- + * up to the higher levels - they're silently ignored.
- + * I/F can now be put into multicast mode. Receiver
- + * routine optimised.
- + *
- + * 30/02/96: RMK Now claims interrupt at open when part of the kernel
- + * rather than when a module.
- + *
- + * 02/03/96: RMK Various code cleanups
- + *
- + * TODO:
- + * When we detect a fatal error on the interface, we should restart it.
- + */
- +
- +#include <linux/module.h>
- +#include <linux/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/types.h>
- +#include <linux/fcntl.h>
- +#include <linux/interrupt.h>
- +#include <linux/ptrace.h>
- +#include <linux/ioport.h>
- +#include <linux/in.h>
- +#include <linux/malloc.h>
- +#include <linux/string.h>
- +#include <linux/errno.h>
- +#include <linux/netdevice.h>
- +#include <linux/etherdevice.h>
- +#include <linux/skbuff.h>
- +
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +#include <asm/io.h>
- +#include <asm/dma.h>
- +#include <asm/ecard.h>
- +
- +#include "ether3.h"
- +
- +static unsigned int net_debug = NET_DEBUG;
- +static const int ether3_prods[] = { 0x00A4 };
- +static const int ether3_manus[] = { 0x0011 };
- +
- +static void
- +ether3_setmulticastlist (struct device *dev);
- +
- +static char *version = "ether3 ethernet driver (c) 1995 R.M.King v1.10\n";
- +
- +extern int inswb(int reg, void *buffer, int len);
- +extern int outswb(int reg, void *buffer, int len);
- +
- +#define FUNC_PROLOGUE \
- + struct dev_priv *priv = (struct dev_priv *)dev->priv
- +
- +#define BUS_16 16
- +#define BUS_8 8
- +
- +/*
- + * 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};
- +
- +/* --------------------------------------------------------------------------- */
- +
- +/*
- + * write data to the buffer memory
- + */
- +static void
- +ether3_writebuffer(struct device *dev, void *data, int start, int length)
- +{
- + FUNC_PROLOGUE;
- + int timeout = 1000000;
- +
- + if (start != -1) {
- + outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- + outw (priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
- + while ((inw (REG_STATUS) & STAT_FIFOEMPTY) == 0)
- + if (!timeout--) {
- + printk (KERN_CRIT "%s: writebuf broken\n", dev->name);
- + priv->broken = 1;
- + return;
- + }
- + outw (priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
- + outw (start, REG_DMAADDR);
- + }
- + if(length != 0)
- + outswb (REG_BUFWIN, data, length);
- +}
- +
- +/*
- + * read data from the buffer memory
- + */
- +static void
- +ether3_readbuffer(struct device *dev, void *data, int start, int length)
- +{
- + FUNC_PROLOGUE;
- + int timeout = 1000000;
- +
- + if (start != -1) {
- + outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- + outw (priv->regs.command | CMD_FIFOWRITE, REG_COMMAND);
- + while ((inw( REG_STATUS) & STAT_FIFOEMPTY) == 0)
- + if (!timeout--) {
- + printk (KERN_CRIT "%s: readbuf broken\n", dev->name);
- + priv->broken = 1;
- + return;
- + }
- + outw (start, REG_DMAADDR);
- + outw (priv->regs.command | CMD_FIFOREAD, REG_COMMAND);
- + }
- + if(length != 0)
- + inswb (REG_BUFWIN, data, length);
- +}
- +
- +/*
- + * Increment the use count, and switch LED on if necessary
- + */
- +static inline void
- +ether3_ledincrementuse (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- +
- + if (++ priv->use == 1) {
- + priv->regs.config2 &= ~CFG2_CTRLO;
- + outw (priv->regs.config2 , REG_CONFIG2);
- + }
- +}
- +
- +/*
- + * Decrement the use count and switch LED off if necessary
- + */
- +static inline void
- +ether3_leddecrementuse (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- +
- + if (priv->use && -- priv->use == 0) {
- + priv->regs.config2 |= CFG2_CTRLO;
- + outw (priv->regs.config2, REG_CONFIG2);
- + }
- +}
- +
- +/*
- + * Read the ethernet address string from the on board rom.
- + * This is an ascii string!!!
- + */
- +static void
- +ether3_addr (char *addr, struct expansion_card *ec)
- +{
- + struct chunk_dir cd;
- + char *s;
- +
- + if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) {
- + int i;
- + for (i = 0; i<6; i++) {
- + addr[i] = simple_strtoul(s + 1, &s, 0x10);
- + if (*s != (i==5?')' : ':' ))
- + break;
- + }
- + if (i == 6)
- + return;
- + }
- + memcpy (addr, def_eth_addr, 6);
- +}
- +
- +/* --------------------------------------------------------------------------- */
- +
- +static int
- +ether3_ramtest(struct device *dev, unsigned char byte)
- +{
- + unsigned char *buffer = kmalloc(RX_END, GFP_KERNEL);
- + int i,ret=0;
- + int max_errors = 4;
- + int bad=-1;
- +
- + if (!buffer)
- + return 1;
- +
- + memset (buffer, byte, RX_END);
- + ether3_writebuffer (dev, buffer, 0, TX_END);
- + ether3_writebuffer (dev, buffer+RX_START, RX_START, RX_LEN);
- +
- + memset (buffer, byte ^ 0xff, RX_END);
- + ether3_readbuffer (dev, buffer, 0, TX_END);
- + ether3_readbuffer (dev, buffer+RX_START, RX_START, RX_LEN);
- +
- + for (i=0; i<RX_END; i++) {
- + if (buffer[i] != byte) {
- + if (max_errors>=0 && bad!=buffer[i]) {
- + printk (KERN_ERR "%s: RAM failed with (%02X instead of %02X) at 0x%04X",
- + dev->name, buffer[i], byte, i);
- + ret = 2;
- + max_errors--;
- + bad = buffer[i];
- + }
- + } else {
- + if (bad != -1) {
- + printk(" - 0x%04X\n", i);
- + bad = -1;
- + }
- + }
- + }
- + if (bad != -1)
- + printk (" - 0x10000\n");
- + kfree (buffer);
- +
- + return ret;
- +}
- +
- +/* ------------------------------------------------------------------------------- */
- +
- +static int
- +ether3_init_2(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + int i;
- + unsigned long txhdr = 0;
- +
- + priv->regs.config1 = CFG1_RECVCOMPSTAT0|CFG1_DMABURST8;
- + priv->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC;
- + priv->regs.command = 0;
- + /*
- + * Set up our hardware address
- + */
- + outw (priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
- + for (i = 0; i < 6; i++)
- + outb (dev->dev_addr[i], REG_BUFWIN);
- +
- + if (dev->flags & IFF_PROMISC)
- + priv->regs.config1 |= CFG1_RECVPROMISC;
- + else
- + if (dev->flags & IFF_MULTICAST)
- + priv->regs.config1 |= CFG1_RECVSPECBRMULTI;
- + else
- + priv->regs.config1 |= CFG1_RECVSPECBROAD;
- +
- + /*
- + * There is a problem with the NQ8005 in that it occasionally losses the
- + * last two bytes. To get round this problem, we receive the CRC as well.
- + * That way, if we do loose the last two, then it doesn't matter
- + */
- + outw (priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
- + outw ((TX_END>>8) - 1, REG_BUFWIN);
- + outw (priv->regs.recvptr, REG_RECVPTR);
- + outw (priv->regs.transmitptr, REG_TRANSMITPTR);
- + outw (priv->regs.recvptr >> 8, REG_RECVEND);
- + outw (priv->regs.config2, REG_CONFIG2);
- + outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- +
- + ether3_writebuffer (dev, &txhdr, priv->regs.transmitptr, 4);
- +
- + outw (priv->regs.command, REG_COMMAND);
- +
- + i = ether3_ramtest (dev, 0x5A);
- + if(i)
- + return i;
- + return ether3_ramtest (dev, 0x1E);
- +}
- +
- +static void
- +ether3_init_for_open(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + unsigned long txhdr = 0;
- + int i;
- +
- + memset (&priv->stats, 0, sizeof (struct enet_statistics));
- +
- + priv->regs.command = 0;
- + priv->txbuffered = 0;
- + outw (CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
- + while (inw (REG_STATUS) & (STAT_RXON|STAT_TXON));
- +
- + outw (priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1);
- + for (i = 0; i < 6; i++)
- + outb (dev->dev_addr[i], REG_BUFWIN);
- +
- + priv->regs.transmitptr = 0; /* address that transmitter has processed to */
- + priv->txinsert = 0; /* address of next available packet for tx */
- + priv->txed = 0; /* transmitted length index */
- + priv->txto = 0; /* to transmit length index */
- + priv->tx_ends[0] = 0;
- + priv->use = 0;
- +
- + priv->regs.config2 |= CFG2_CTRLO;
- +
- + outw (priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1);
- + outw ((TX_END>>8) - 1, REG_BUFWIN);
- +
- + priv->regs.recvptr = RX_START;
- + outw (priv->regs.recvptr, REG_RECVPTR);
- + outw (priv->regs.recvptr >> 8, REG_RECVEND);
- +
- + outw (0, REG_TRANSMITPTR);
- + outw (priv->regs.config2, REG_CONFIG2);
- + outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- +
- + ether3_writebuffer (dev, &txhdr, priv->regs.transmitptr, 4);
- +
- + priv->regs.command = CMD_ENINTRX|CMD_ENINTTX;
- + outw (priv->regs.command | CMD_RXON, REG_COMMAND);
- +}
- +
- +/*
- + * This is the real probe routine.
- + */
- +static int
- +ether3_probe1(struct device *dev)
- +{
- + static unsigned version_printed = 0;
- + struct dev_priv *priv;
- + int i;
- +
- + if (!dev->priv)
- + dev->priv = kmalloc (sizeof (struct dev_priv), GFP_KERNEL);
- +
- + if (!dev->priv)
- + return -ENOMEM;
- +
- + priv = (struct dev_priv *) dev->priv;
- + memset (priv, 0, sizeof(struct dev_priv));
- + priv->broken = 0;
- +
- + outb (0x80, REG_CONFIG2 + 1);
- + outb (0, REG_COMMAND);
- +
- + /*
- + * test to see if we have an 8 or 16-bit IO bus
- + */
- + outb (1, REG_CONFIG1);
- + if (inb (REG_CONFIG1 + 1) == 1 && inb (REG_CONFIG1) == 0)
- + priv->bus_type = BUS_8;
- + else
- + if (inw (REG_CONFIG1) == 0x0101)
- + priv->bus_type = BUS_16;
- + else {
- + kfree(dev->priv);
- + dev->priv = NULL;
- + return -ENODEV;
- + }
- +
- + if (net_debug && version_printed++ == 0)
- + printk (KERN_INFO "%s", version);
- +
- + printk(KERN_INFO "%s: ether3 found [%d, %04lx, %d] ", dev->name, priv->bus_type,
- + dev->base_addr, dev->irq);
- +
- + request_region (dev->base_addr, 128, "ether3");
- +
- + if (priv->bus_type == BUS_8) {
- + printk (KERN_CRIT "*** 8-bit not supported (yet) ***\n");
- + kfree (dev->priv);
- + dev->priv = NULL;
- + return -ENODEV;
- + }
- +
- + /* Retrive and print the ethernet address. */
- + for (i = 0; i < 6; i++)
- + printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]);
- +
- + if (ether3_init_2(dev)) {
- + kfree (dev->priv);
- + dev->priv = NULL;
- + return -ENODEV;
- + }
- +
- + dev->open = ether3_open;
- + dev->stop = ether3_close;
- + dev->hard_start_xmit = ether3_sendpacket;
- + dev->get_stats = ether3_getstats;
- + dev->set_multicast_list = ether3_setmulticastlist;
- +
- + /* Fill in the fields of the device structure with ethernet values. */
- + ether_setup(dev);
- +#ifndef CLAIM_IRQ_AT_OPEN
- + if (request_irq(dev->irq, ether3_interrupt, 0, "ether3", dev)) {
- + kfree (dev->priv);
- + dev->priv = NULL;
- + return -EAGAIN;
- + }
- +#endif
- + return 0;
- +}
- +
- +#ifndef MODULE
- +int
- +ether3_probe(struct device *dev)
- +{
- + struct expansion_card *ec;
- +
- + if (!dev)
- + return ENODEV;
- +
- + if ((ec = ecard_find (0, sizeof(ether3_prods), ether3_prods, ether3_manus)) == NULL)
- + return ENODEV;
- +
- + dev->base_addr = ((unsigned long)ecard_address (ec->slot_no, ECARD_MEMC, 0)) >> 2;
- + dev->irq = ec->irq;
- +
- + ecard_claim (ec);
- +
- + ether3_addr (dev->dev_addr, ec);
- + return ether3_probe1(dev);
- +}
- +#endif
- +
- +/*
- + * Open/initialize the board. This is called (in the current kernel)
- + * sometime after booting when the 'ifconfig' program is run.
- + *
- + * This routine should set everything up anew at each open, even
- + * registers that "should" only need to be set once at boot, so that
- + * there is non-reboot way to recover if something goes wrong.
- + */
- +static int
- +ether3_open(struct device *dev)
- +{
- + ether3_init_for_open(dev);
- +
- +#ifdef CLAIM_IRQ_AT_OPEN
- + if (request_irq(dev->irq, ether3_interrupt, 0, "ether3", dev))
- + return -EAGAIN;
- +#endif
- +
- + MOD_INC_USE_COUNT;
- +
- + dev->tbusy = 0;
- + dev->interrupt = 0;
- + dev->start = 1;
- + return 0;
- +}
- +
- +/*
- + * The inverse routine to ether3_open().
- + */
- +static int
- +ether3_close(struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + unsigned long flags;
- +
- + dev->tbusy = 1;
- + dev->start = 0;
- +
- + save_flags_cli (flags);
- +
- + outw (CMD_RXOFF|CMD_TXOFF, REG_COMMAND);
- + priv->regs.command = 0;
- + while (inw (REG_STATUS) & (STAT_RXON|STAT_TXON));
- + outb (0x80, REG_CONFIG2 + 1);
- + outw (0, REG_COMMAND);
- +
- + restore_flags (flags);
- +#ifdef CLAIM_IRQ_AT_OPEN
- + free_irq (dev->irq, dev);
- +#endif
- +
- + MOD_DEC_USE_COUNT;
- + return 0;
- +}
- +
- +/*
- + * Get the current statistics. This may be called with the card open or
- + * closed.
- + */
- +static struct enet_statistics *ether3_getstats (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- + return &priv->stats;
- +}
- +
- +/*
- + * Set or clear promiscuous/multicast mode filter for this adaptor.
- + *
- + * We don't attempt any packet filtering. The card may have a SEEQ 8004
- + * in which does not have the other ethernet address registers present...
- + */
- +static void ether3_setmulticastlist (struct device *dev)
- +{
- + FUNC_PROLOGUE;
- +
- + priv->regs.config1 &= ~CFG1_RECVPROMISC;
- +
- + if (dev->flags & IFF_PROMISC) {
- + /* promiscuous mode */
- + priv->regs.config1 |= CFG1_RECVPROMISC;
- + } else
- + if (dev->flags & IFF_ALLMULTI) {
- + priv->regs.config1 |= CFG1_RECVSPECBRMULTI;
- + } else
- + priv->regs.config1 |= CFG1_RECVSPECBROAD;
- +
- + outw (priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1);
- +}
- +
- +/*
- + * Transmit a packet
- + */
- +static int
- +ether3_sendpacket(struct sk_buff *skb, struct device *dev)
- +{
- + FUNC_PROLOGUE;
- +
- + if (dev->tbusy) {
- + /* If we get here, some higher level has decided we are broken.
- + There should really be a "kick me" function call instead. */
- + int tickssofar = jiffies - dev->trans_start;
- + if (tickssofar < 5)
- + return 1;
- + printk (KERN_WARNING "%s: transmit timed out, network cable problem?\n", dev->name);
- + /* Try to restart the adaptor. */
- + dev->tbusy = 0;
- + priv->use = 0;
- + priv->regs.config2 |= CFG2_CTRLO;
- + outw (priv->regs.config2 , REG_CONFIG2);
- + dev->trans_start = jiffies;
- + }
- +
- + /* If some higher layer thinks we've missed an tx-done interrupt
- + we are passed NULL. Caution: dev_tint() handles the cli()/sti()
- + itself. */
- + if (skb == NULL) {
- + dev_tint(dev);
- + return 0;
- + }
- +
- + /* Block a timer-based transmit from overlapping. This could better be
- + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
- + if (set_bit(0, (void*)&dev->tbusy) != 0)
- + printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name);
- + else {
- + unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
- + unsigned int thisdata, thisstart, nextpacket;
- + unsigned long flags;
- + unsigned long thishdr = TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS
- + |TXHDR_ENSUCCESS;
- + unsigned long nexthdr = 0;
- +
- + if (priv->broken) {
- + dev_kfree_skb(skb, FREE_WRITE);
- + priv->stats.tx_dropped ++;
- + dev->tbusy = 0;
- + return 0;
- + }
- +
- + thisstart = priv->txinsert;
- + thisdata = thisstart + 4;
- + if (thisdata >= TX_END)
- + thisdata -= TX_END;
- +
- + nextpacket = thisdata + length;
- + if (nextpacket >= TX_END)
- + nextpacket -= TX_END;
- +
- + priv->txinsert = nextpacket;
- + priv->tx_ends[priv->txto++] = nextpacket;
- +
- + if (priv->txto >= MAX_TXED)
- + priv->txto = 0;
- +
- + thishdr |= htons (nextpacket);
- +
- + save_flags_cli (flags);
- + ether3_writebuffer(dev, skb->data, thisdata, (length & 1) ? (length + 1) : length);
- + ether3_writebuffer(dev, &nexthdr, -1, 4);
- + ether3_ledincrementuse (dev);
- + ether3_writebuffer(dev, &thishdr, thisstart, 4);
- +
- + if ((inw (REG_STATUS) & STAT_TXON) == 0) {
- + outw (thisstart, REG_TRANSMITPTR);
- + outw (priv->regs.command | CMD_TXON, REG_COMMAND);
- + }
- + restore_flags(flags);
- +
- + dev->trans_start = jiffies;
- + if (priv->txbuffered ++ < MAX_TX_BUFFERED)
- + dev->tbusy = 0;
- + }
- + dev_kfree_skb (skb, FREE_WRITE);
- +
- + return 0;
- +}
- +
- +static void
- +ether3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
- +{
- + struct device *dev = (struct device *)dev_id;
- + struct dev_priv *priv;
- + int boguscount = 20, done;
- +
- +#if NET_DEBUG > 1
- + if(net_debug & DEBUG_INT)
- + printk(KERN_DEBUG "eth3irq: %d ", irq);
- +#endif
- +
- + priv = (struct dev_priv *)dev->priv;
- +
- + dev->interrupt = 1;
- +
- + do {
- + unsigned int status;
- +
- + done = 0;
- + status = inw(REG_STATUS);
- +
- + if (status & STAT_INTRX) { /* Got a packet(s). */
- + outw (CMD_ACKINTRX | priv->regs.command, REG_COMMAND);
- + ether3_rx (dev, priv);
- + done = 1;
- + }
- + if (status & STAT_INTTX) { /* Packets transmitted */
- + outw (CMD_ACKINTTX | priv->regs.command, REG_COMMAND);
- + ether3_tx (dev, priv);
- + done = 1;
- + }
- + if (status & STAT_INTBUFWIN) { /* buffer window interrupt */
- + outw (CMD_ACKINTBUFWIN | priv->regs.command, REG_COMMAND);
- + }
- + } while (-- boguscount && !done) ;
- +
- + dev->interrupt = 0;
- +
- +#if NET_DEBUG > 1
- + if(net_debug & DEBUG_INT)
- + printk("done\n");
- +#endif
- +}
- +
- +/*
- + * If we have a good packet(s), get it/them out of the buffers.
- + */
- +static void
- +ether3_rx(struct device *dev, struct dev_priv *priv)
- +{
- + unsigned int current_recv_ptr;
- + unsigned int maxcnt = 10;
- +
- + ether3_ledincrementuse (dev);
- +
- + current_recv_ptr = priv->regs.recvptr;
- +
- + do {
- + struct t {
- + unsigned long rxhdr;
- + unsigned char dst_addr[6];
- + unsigned char src_addr[6];
- + } p;
- + unsigned int prev_recv_ptr;
- +
- + /*
- + * read the first 16 bytes from the buffer.
- + * This contains the status bytes etc and ethernet addresses,
- + * and we also check the source ethernet address to see if
- + * it origionated from us.
- + */
- + prev_recv_ptr = current_recv_ptr;
- + ether3_readbuffer (dev, &p, current_recv_ptr, 16);
- +
- + if ((p.rxhdr & RX_NEXT) == 0 || (p.rxhdr & RXSTAT_DONE) == 0)
- + break;
- + current_recv_ptr = ntohs (p.rxhdr & RX_NEXT);
- +
- + /*
- + * ignore our own packets...
- + */
- + if (memcmp (dev->dev_addr, &p.src_addr, 6) == 0) {
- + outw (current_recv_ptr >> 8, REG_RECVEND);
- + if (p.rxhdr & RXHDR_CHAINCONTINUE)
- + continue;
- + else
- + break;
- + }
- +
- + if (!(p.rxhdr & (RXSTAT_OVERSIZE|RXSTAT_CRCERROR|RXSTAT_DRIBBLEERROR|RXSTAT_SHORTPACKET))) {
- + unsigned char *buf;
- + unsigned int length;
- + struct sk_buff *skb;
- +
- + if (current_recv_ptr > prev_recv_ptr)
- + length = current_recv_ptr - prev_recv_ptr - 4;
- + else
- + length = (current_recv_ptr - RX_START) + (RX_END - prev_recv_ptr) - 4;
- +
- + skb = dev_alloc_skb(length + 2);
- + if (skb == NULL) {
- + printk (KERN_WARNING "%s: memory squeeze, dropping packet.\n", dev->name);
- + priv->stats.rx_dropped ++;
- + break;
- + }
- +
- + skb->dev = dev;
- + skb_reserve (skb, 2);
- + buf = skb_put (skb, length);
- + /*
- + * buf isn't word aligned, and it is probably faster to
- + * copy the address this way
- + */
- + *(unsigned short *)&buf[0] = *(unsigned short *)&p.dst_addr[0];
- + *(unsigned short *)&buf[2] = *(unsigned short *)&p.dst_addr[2];
- + *(unsigned short *)&buf[4] = *(unsigned short *)&p.dst_addr[4];
- + *(unsigned short *)&buf[6] = *(unsigned short *)&p.src_addr[0];
- + *(unsigned short *)&buf[8] = *(unsigned short *)&p.src_addr[2];
- + *(unsigned short *)&buf[10] = *(unsigned short *)&p.src_addr[4];
- +
- + ether3_readbuffer (dev, buf + 12, -1, length - 12);
- +
- + outw (current_recv_ptr >> 8, REG_RECVEND);
- +
- + skb->protocol = eth_type_trans(skb, dev);
- + netif_rx (skb);
- + priv->stats.rx_packets++;
- + } else {
- + priv->stats.rx_errors++;
- + if(p.rxhdr & RXSTAT_OVERSIZE) priv->stats.rx_length_errors ++;
- + if(p.rxhdr & RXSTAT_CRCERROR) priv->stats.rx_crc_errors ++;
- + if(p.rxhdr & RXSTAT_DRIBBLEERROR) priv->stats.rx_fifo_errors ++;
- + if(p.rxhdr & RXSTAT_SHORTPACKET) priv->stats.rx_length_errors ++;
- + outw (current_recv_ptr >> 8, REG_RECVEND);
- + }
- + if (!(p.rxhdr & RXHDR_CHAINCONTINUE))
- + break;
- + }
- + while (-- maxcnt);
- +
- + priv->regs.recvptr = current_recv_ptr;
- + if (!(inw (REG_STATUS) & STAT_RXON)) {
- + outw (priv->regs.recvptr, REG_RECVPTR);
- + outw (priv->regs.command | CMD_RXON, REG_COMMAND);
- + }
- +
- + /* If any worth-while packets have been received, dev_rint()
- + has done a mark_bh(NET_BH) for us and will work on them
- + when we get to the bottom-half routine. */
- + ether3_leddecrementuse (dev);
- +}
- +
- +/*
- + * Update stats for the transmitted packet
- + */
- +static void
- +ether3_tx(struct device *dev, struct dev_priv *priv)
- +{
- + unsigned long txhdr;
- + unsigned char *buffer = (unsigned char *)&txhdr;
- + unsigned int txptr;
- +
- + txptr = priv->regs.transmitptr;
- +
- + do {
- + /*
- + * Read the packet header
- + */
- + if (txptr + 4 > TX_END) {
- + int len = TX_END - txptr;
- + if (len)
- + ether3_readbuffer (dev, buffer, txptr, len);
- + ether3_readbuffer (dev, buffer + len, 0, 4 - len);
- + } else
- + ether3_readbuffer (dev, buffer, txptr, 4);
- +
- + /*
- + * Check to see if this packet has been transmitted
- + */
- + if (!(txhdr & TXSTAT_DONE) || !(txhdr & TXHDR_TRANSMIT)) {
- + priv->regs.transmitptr = txptr;
- + return;
- + }
- +
- + /*
- + * Update collision count (undocumented in datasheet)
- + */
- + if (txhdr & TXSTAT_COLLISION)
- + priv->stats.collisions += (txhdr >> (3+24)) & 15;
- +
- + /*
- + * Update errors
- + */
- + if (txhdr & (TXSTAT_BABBLED | TXSTAT_16COLLISIONS)) {
- + priv->stats.tx_errors ++;
- + if (txhdr & TXSTAT_16COLLISIONS) priv->stats.collisions += 16;
- + if (txhdr & TXSTAT_BABBLED) priv->stats.tx_fifo_errors ++;
- + } else
- + priv->stats.tx_packets++;
- +
- + /*
- + * Get next packet address
- + */
- + txptr = priv->tx_ends[priv->txed++]; /* next packet address */
- +
- + if (priv->txed >= MAX_TXED)
- + priv->txed = 0;
- +
- + if (txptr != ntohs (txhdr & TX_NEXT)) {
- + printk (KERN_WARNING "%s: TX: bad next pointer - %04X instead of %04X\n",
- + dev->name, ntohs (txhdr & TX_NEXT), txptr);
- + priv->regs.transmitptr = txptr;
- + return;
- + }
- +
- + if (priv->regs.transmitptr >= TX_END)
- + priv->regs.transmitptr -= TX_END;
- +
- + if (priv->txbuffered)
- + priv->txbuffered -= 1;
- +
- + dev->tbusy = 0;
- +
- + ether3_leddecrementuse (dev);
- + mark_bh (NET_BH); /* Inform upper layers. */
- + } while (1);
- +}
- +
- +#ifdef MODULE
- +
- +char ethernames[MAX_ECARDS][9];
- +
- +static struct device *my_ethers[MAX_ECARDS];
- +static struct expansion_card *ec[MAX_ECARDS];
- +
- +int
- +init_module(void)
- +{
- + int i;
- +
- + for(i = 0; i < MAX_ECARDS; i++) {
- + my_ethers[i] = NULL;
- + ec[i] = NULL;
- + strcpy (ethernames[i], " ");
- + }
- +
- + i = 0;
- +
- + do {
- + if ((ec[i] = ecard_find(0, sizeof(ether3_prods), ether3_prods, ether3_manus)) == 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= ((unsigned long)ecard_address (ec[i]->slot_no, ECARD_MEMC, 0))>>2;
- + 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++;
- + }
- + while(i < MAX_ECARDS);
- +
- + return i != 0 ? 0 : -ENODEV;
- +}
- +
- +void
- +cleanup_module(void)
- +{
- + int i;
- + for (i = 0; i < MAX_ECARDS; i++) {
- + if (my_ethers[i]) {
- + unregister_netdev(my_ethers[i]);
- + my_ethers[i] = NULL;
- + }
- + if (ec[i]) {
- + ecard_release(ec[i]);
- + ec[i] = NULL;
- + }
- + }
- +}
- +#endif /* MODULE */
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/net/ether3.h linux/arch/arm/drivers/net/ether3.h
- --- linux.orig/arch/arm/drivers/net/ether3.h Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/net/ether3.h Fri Jul 19 20:44:42 1996
- @@ -0,0 +1,174 @@
- +/*
- + * linux/drivers/net/ether3.h
- + *
- + * network driver for Acorn Ether3 cards
- + */
- +
- +#ifndef _LINUX_ether3_H
- +#define _LINUX_ether3_H
- +
- +/* use 0 for production, 1 for verification, >2 for debug. debug flags: */
- +#define DEBUG_TX 2
- +#define DEBUG_RX 4
- +#define DEBUG_INT 8
- +#define DEBUG_IC 16
- +#ifndef NET_DEBUG
- +#define NET_DEBUG 0
- +#endif
- +
- +/* Command register definitions & bits */
- +#define REG_COMMAND (dev->base_addr + 0x00)
- +#define CMD_ENINTDMA 0x0001
- +#define CMD_ENINTRX 0x0002
- +#define CMD_ENINTTX 0x0004
- +#define CMD_ENINTBUFWIN 0x0008
- +#define CMD_ACKINTDMA 0x0010
- +#define CMD_ACKINTRX 0x0020
- +#define CMD_ACKINTTX 0x0040
- +#define CMD_ACKINTBUFWIN 0x0080
- +#define CMD_DMAON 0x0100
- +#define CMD_RXON 0x0200
- +#define CMD_TXON 0x0400
- +#define CMD_DMAOFF 0x0800
- +#define CMD_RXOFF 0x1000
- +#define CMD_TXOFF 0x2000
- +#define CMD_FIFOREAD 0x4000
- +#define CMD_FIFOWRITE 0x8000
- +
- +/* status register */
- +#define REG_STATUS (dev->base_addr + 0x00)
- +#define STAT_ENINTSTAT 0x0001
- +#define STAT_ENINTRX 0x0002
- +#define STAT_ENINTTX 0x0004
- +#define STAT_ENINTBUFWIN 0x0008
- +#define STAT_INTDMA 0x0010
- +#define STAT_INTRX 0x0020
- +#define STAT_INTTX 0x0040
- +#define STAT_INTBUFWIN 0x0080
- +#define STAT_DMAON 0x0100
- +#define STAT_RXON 0x0200
- +#define STAT_TXON 0x0400
- +#define STAT_FIFOFULL 0x2000
- +#define STAT_FIFOEMPTY 0x4000
- +#define STAT_FIFODIR 0x8000
- +
- +/* configuration register 1 */
- +#define REG_CONFIG1 (dev->base_addr + 0x10)
- +#define CFG1_BUFSELSTAT0 0x0000
- +#define CFG1_BUFSELSTAT1 0x0001
- +#define CFG1_BUFSELSTAT2 0x0002
- +#define CFG1_BUFSELSTAT3 0x0003
- +#define CFG1_BUFSELSTAT4 0x0004
- +#define CFG1_BUFSELSTAT5 0x0005
- +#define CFG1_ADDRPROM 0x0006
- +#define CFG1_TRANSEND 0x0007
- +#define CFG1_LOCBUFMEM 0x0008
- +#define CFG1_INTVECTOR 0x0009
- +#define CFG1_DMABURSTCONT 0x0000
- +#define CFG1_DMABURST800NS 0x0010
- +#define CFG1_DMABURST1600NS 0x0020
- +#define CFG1_DMABURST3200NS 0x0030
- +#define CFG1_DMABURST1 0x0000
- +#define CFG1_DMABURST4 0x0040
- +#define CFG1_DMABURST8 0x0080
- +#define CFG1_DMABURST16 0x00C0
- +#define CFG1_RECVCOMPSTAT0 0x0100
- +#define CFG1_RECVCOMPSTAT1 0x0200
- +#define CFG1_RECVCOMPSTAT2 0x0400
- +#define CFG1_RECVCOMPSTAT3 0x0800
- +#define CFG1_RECVCOMPSTAT4 0x1000
- +#define CFG1_RECVCOMPSTAT5 0x2000
- +#define CFG1_RECVSPECONLY 0x0000
- +#define CFG1_RECVSPECBROAD 0x4000
- +#define CFG1_RECVSPECBRMULTI 0x8000
- +#define CFG1_RECVPROMISC 0xC000
- +
- +/* configuration register 2 */
- +#define REG_CONFIG2 (dev->base_addr + 0x20)
- +#define CFG2_BYTESWAP 0x0001
- +#define CFG2_ERRENCRC 0x0008
- +#define CFG2_ERRENDRIBBLE 0x0010
- +#define CFG2_ERRSHORTFRAME 0x0020
- +#define CFG2_SLOTSELECT 0x0040
- +#define CFG2_PREAMSELECT 0x0080
- +#define CFG2_ADDRLENGTH 0x0100
- +#define CFG2_RECVCRC 0x0200
- +#define CFG2_XMITNOCRC 0x0400
- +#define CFG2_LOOPBACK 0x0800
- +#define CFG2_CTRLO 0x1000
- +#define CFG2_RESET 0x8000
- +
- +#define REG_RECVEND (dev->base_addr + 0x30)
- +
- +#define REG_BUFWIN (dev->base_addr + 0x40)
- +
- +#define REG_RECVPTR (dev->base_addr + 0x50)
- +
- +#define REG_TRANSMITPTR (dev->base_addr + 0x60)
- +
- +#define REG_DMAADDR (dev->base_addr + 0x70)
- +
- +/*
- + * Cards transmit/receive headers
- + */
- +#define TX_NEXT (0xffff)
- +#define TXHDR_ENBABBLEINT (1 << 16)
- +#define TXHDR_ENCOLLISIONINT (1 << 17)
- +#define TXHDR_EN16COLLISION (1 << 18)
- +#define TXHDR_ENSUCCESS (1 << 19)
- +#define TXHDR_DATAFOLLOWS (1 << 21)
- +#define TXHDR_CHAINCONTINUE (1 << 22)
- +#define TXHDR_TRANSMIT (1 << 23)
- +#define TXSTAT_BABBLED (1 << 24)
- +#define TXSTAT_COLLISION (1 << 25)
- +#define TXSTAT_16COLLISIONS (1 << 26)
- +#define TXSTAT_DONE (1 << 31)
- +
- +#define RX_NEXT (0xffff)
- +#define RXHDR_CHAINCONTINUE (1 << 22)
- +#define RXHDR_RECEIVE (1 << 23)
- +#define RXSTAT_OVERSIZE (1 << 24)
- +#define RXSTAT_CRCERROR (1 << 25)
- +#define RXSTAT_DRIBBLEERROR (1 << 26)
- +#define RXSTAT_SHORTPACKET (1 << 27)
- +#define RXSTAT_DONE (1 << 31)
- +
- +
- +#define TX_END 0x6000
- +#define RX_START 0x6000
- +#define RX_LEN 0xA000
- +#define RX_END 0x10000
- +#define MAX_TXED 360
- +#define MAX_TX_BUFFERED 10
- +
- +struct dev_priv {
- + int bus_type; /* type of bus (not currently used) */
- + struct {
- + unsigned int command;
- + unsigned int config1;
- + unsigned int config2;
- + unsigned int transmitptr;
- + unsigned int recvptr;
- + } regs;
- + struct enet_statistics stats;
- + unsigned int txinsert; /* address to insert next packet */
- + unsigned int txed; /* transmitted index in tx_ends */
- + unsigned int txto; /* to transmit index in tx_ends */
- + unsigned int txbuffered; /* number of packets currently buffered */
- + unsigned int tx_ends[MAX_TXED]; /* addresses of transmit packets */
- + unsigned int use; /* 0 = card not in use, >0 packet transferring */
- + int broken; /* 0 = ok, 1 = something went wrong */
- +};
- +
- +extern int ether3_probe (struct device *dev);
- +static int ether3_probe1 (struct device *dev);
- +static int ether3_open (struct device *dev);
- +static int ether3_sendpacket (struct sk_buff *skb, struct device *dev);
- +static void ether3_interrupt (int irq, void *dev_id, struct pt_regs *regs);
- +static void ether3_rx (struct device *dev, struct dev_priv *priv);
- +static void ether3_tx (struct device *dev, struct dev_priv *priv);
- +static int ether3_close (struct device *dev);
- +static struct enet_statistics *ether3_getstats (struct device *dev);
- +static void ether3_setmulticastlist (struct device *dev);
- +
- +#endif
- diff -u -X rej --recursive --new-file linux.orig/arch/arm/drivers/net/ppp.c linux/arch/arm/drivers/net/ppp.c
- --- linux.orig/arch/arm/drivers/net/ppp.c Thu Jan 1 01:00:00 1970
- +++ linux/arch/arm/drivers/net/ppp.c Thu Jul 25 23:07:09 1996
- @@ -0,0 +1,3516 @@
- +/* PPP for Linux
- + *
- + * Michael Callahan <callahan@maths.ox.ac.uk>
- + * Al Longyear <longyear@netcom.com>
- + *
- + * Dynamic PPP devices by Jim Freeman <jfree@caldera.com>.
- + * ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid <ewerlid@syscon.uu.se>
- + *
- + * ==FILEVERSION 960528==
- + *
- + * NOTE TO MAINTAINERS:
- + * If you modify this file at all, please set the number above to the
- + * date of the modification as YYMMDD (year month day).
- + * ppp.c is shipped with a PPP distribution as well as with the kernel;
- + * if everyone increases the FILEVERSION number above, then scripts
- + * can do the right thing when deciding whether to install a new ppp.c
- + * file. Don't change the format of that line otherwise, so the
- + * installation script can recognize it.
- + */
- +
- +/*
- + Sources:
- +
- + slip.c
- +
- + RFC1331: The Point-to-Point Protocol (PPP) for the Transmission of
- + Multi-protocol Datagrams over Point-to-Point Links
- +
- + RFC1332: IPCP
- +
- + ppp-2.0
- +
- + Flags for this module (any combination is acceptable for testing.):
- +
- + OPTIMIZE_FLAG_TIME - Number of jiffies to force sending of leading flag
- + character. This is normally set to ((HZ * 3) / 2).
- + This is 1.5 seconds. If zero then the leading
- + flag is always sent.
- +
- + CHECK_CHARACTERS - Enable the checking on all received characters for
- + 8 data bits, no parity. This adds a small amount of
- + processing for each received character.
- +*/
- +
- +#define OPTIMIZE_FLAG_TIME ((HZ * 3)/2)
- +
- +#define CHECK_CHARACTERS 1
- +#define PPP_COMPRESS 1
- +
- +#ifndef PPP_MAX_DEV
- +#define PPP_MAX_DEV 256
- +#endif
- +
- +/* $Id: ppp.c,v 1.5 1995/06/12 11:36:53 paulus Exp $
- + * Added dynamic allocation of channels to eliminate
- + * compiled-in limits on the number of channels.
- + *
- + * Dynamic channel allocation code Copyright 1995 Caldera, Inc.,
- + * released under the GNU General Public License Version 2.
- + */
- +
- +#include <linux/autoconf.h>
- +#include <linux/module.h>
- +#include <linux/kernel.h>
- +#include <linux/sched.h>
- +#include <linux/types.h>
- +#include <linux/fcntl.h>
- +#include <linux/interrupt.h>
- +#include <linux/ptrace.h>
- +#include <linux/ioport.h>
- +#include <linux/in.h>
- +#include <linux/malloc.h>
- +#include <linux/tty.h>
- +#include <linux/errno.h>
- +#include <linux/sched.h> /* to get the struct task_struct */
- +#include <linux/string.h> /* used in new tty drivers */
- +#include <linux/signal.h> /* used in new tty drivers */
- +#include <asm/system.h>
- +#include <asm/bitops.h>
- +#include <asm/segment.h>
- +#include <linux/if.h>
- +#include <linux/if_ether.h>
- +#include <linux/netdevice.h>
- +#include <linux/skbuff.h>
- +#include <linux/inet.h>
- +#include <linux/ioctl.h>
- +
- +typedef struct sk_buff sk_buff;
- +#define skb_data(skb) ((__u8 *) (skb)->data)
- +
- +#include <linux/ip.h>
- +#include <linux/tcp.h>
- +#include <linux/if_arp.h>
- +#include <net/slhc_vj.h>
- +
- +#define fcstab ppp_crc16_table /* Name of the table in the kernel */
- +#include <linux/ppp_defs.h>
- +
- +#include <linux/socket.h>
- +#include <linux/if_ppp.h>
- +#include <linux/if_pppvar.h>
- +
- +#undef PACKETPTR
- +#define PACKETPTR 1
- +#include <linux/ppp-comp.h>
- +#undef PACKETPTR
- +
- +#define bsd_decompress (*ppp->sc_rcomp->decompress)
- +#define bsd_compress (*ppp->sc_xcomp->compress)
- +
- +#ifndef PPP_IPX
- +#define PPP_IPX 0x2b /* IPX protocol over PPP */
- +#endif
- +
- +#ifndef PPP_LQR
- +#define PPP_LQR 0xc025 /* Link Quality Reporting Protocol */
- +#endif
- +
- +static int ppp_register_compressor (struct compressor *cp);
- +static void ppp_unregister_compressor (struct compressor *cp);
- +
- +/*
- + * Local functions
- + */
- +
- +static struct compressor *find_compressor (int type);
- +static void ppp_init_ctrl_blk (register struct ppp *);
- +static void ppp_kick_tty (struct ppp *, struct ppp_buffer *bfr);
- +static int ppp_doframe (struct ppp *);
- +static struct ppp *ppp_alloc (void);
- +static struct ppp *ppp_find (int pid_value);
- +static void ppp_print_buffer (const __u8 *, const __u8 *, int);
- +extern inline void ppp_stuff_char (struct ppp *ppp,
- + register struct ppp_buffer *buf,
- + register __u8 chr);
- +extern inline int lock_buffer (register struct ppp_buffer *buf);
- +
- +static int rcv_proto_ip (struct ppp *, __u16, __u8 *, int);
- +static int rcv_proto_ipx (struct ppp *, __u16, __u8 *, int);
- +static int rcv_proto_vjc_comp (struct ppp *, __u16, __u8 *, int);
- +static int rcv_proto_vjc_uncomp (struct ppp *, __u16, __u8 *, int);
- +static int rcv_proto_unknown (struct ppp *, __u16, __u8 *, int);
- +static int rcv_proto_lqr (struct ppp *, __u16, __u8 *, int);
- +static void ppp_doframe_lower (struct ppp *, __u8 *, int);
- +static int ppp_doframe (struct ppp *);
- +
- +extern int ppp_bsd_compressor_init(void);
- +static void ppp_proto_ccp (struct ppp *ppp, __u8 *dp, int len, int rcvd);
- +static int rcv_proto_ccp (struct ppp *, __u16, __u8 *, int);
- +
- +#define ins_char(pbuf,c) (buf_base(pbuf) [(pbuf)->count++] = (__u8)(c))
- +
- +#ifndef OPTIMIZE_FLAG_TIME
- +#define OPTIMIZE_FLAG_TIME 0
- +#endif
- +
- +#ifndef PPP_MAX_DEV
- +#define PPP_MAX_DEV 256
- +#endif
- +
- +/*
- + * Parameters which may be changed via insmod.
- + */
- +
- +static int flag_time = OPTIMIZE_FLAG_TIME;
- +static int max_dev = PPP_MAX_DEV;
- +
- +/*
- + * The "main" procedure to the ppp device
- + */
- +
- +int ppp_init (struct device *);
- +
- +/*
- + * Network device driver callback routines
- + */
- +
- +static int ppp_dev_open (struct device *);
- +static int ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd);
- +static int ppp_dev_close (struct device *);
- +static int ppp_dev_xmit (sk_buff *, struct device *);
- +static struct enet_statistics *ppp_dev_stats (struct device *);
- +static int ppp_dev_header (sk_buff *, struct device *, __u16,
- + void *, void *, unsigned int);
- +static int ppp_dev_rebuild (void *eth, struct device *dev,
- + unsigned long raddr, struct sk_buff *skb);
- +/*
- + * TTY callbacks
- + */
- +
- +static int ppp_tty_read (struct tty_struct *, struct file *, __u8 *,
- + unsigned int);
- +static int ppp_tty_write (struct tty_struct *, struct file *, const __u8 *,
- + unsigned int);
- +static int ppp_tty_ioctl (struct tty_struct *, struct file *, unsigned int,
- + unsigned long);
- +static int ppp_tty_select (struct tty_struct *tty, struct inode *inode,
- + struct file *filp, int sel_type, select_table * wait);
- +static int ppp_tty_open (struct tty_struct *);
- +static void ppp_tty_close (struct tty_struct *);
- +static int ppp_tty_room (struct tty_struct *tty);
- +static void ppp_tty_receive (struct tty_struct *tty, const __u8 * cp,
- + char *fp, int count);
- +static void ppp_tty_wakeup (struct tty_struct *tty);
- +
- +#define CHECK_PPP(a) if (!ppp->inuse) { printk (ppp_warning, __LINE__); return a;}
- +#define CHECK_PPP_VOID() if (!ppp->inuse) { printk (ppp_warning, __LINE__); return;}
- +
- +#define in_xmap(ppp,c) (ppp->xmit_async_map[(c) >> 5] & (1 << ((c) & 0x1f)))
- +#define in_rmap(ppp,c) ((((unsigned int) (__u8) (c)) < 0x20) && \
- + ppp->recv_async_map & (1 << (c)))
- +
- +#define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f)))
- +
- +#define tty2ppp(tty) ((struct ppp *) (tty->disc_data))
- +#define dev2ppp(dev) ((struct ppp *) (dev->priv))
- +#define ppp2tty(ppp) ((struct tty_struct *) ppp->tty)
- +#define ppp2dev(ppp) ((struct device *) ppp->dev)
- +
- +struct ppp_hdr {
- + __u8 address;
- + __u8 control;
- + __u8 protocol[2];
- +};
- +
- +#define PPP_HARD_HDR_LEN (sizeof (struct ppp_hdr))
- +
- +typedef struct ppp_ctrl {
- + struct ppp_ctrl *next; /* Next structure in the list */
- + char name [8]; /* Name of the device */
- + struct ppp ppp; /* PPP control table */
- + struct device dev; /* Device information table */
- +} ppp_ctrl_t;
- +
- +static ppp_ctrl_t *ppp_list = NULL;
- +
- +#define ctl2ppp(ctl) (struct ppp *) &ctl->ppp
- +#define ctl2dev(ctl) (struct device *) &ctl->dev
- +#undef PPP_NRUNIT
- +
- +/* Buffer types */
- +#define BUFFER_TYPE_DEV_RD 0 /* ppp read buffer */
- +#define BUFFER_TYPE_TTY_WR 1 /* tty write buffer */
- +#define BUFFER_TYPE_DEV_WR 2 /* ppp write buffer */
- +#define BUFFER_TYPE_TTY_RD 3 /* tty read buffer */
- +#define BUFFER_TYPE_VJ 4 /* vj compression buffer */
- +
- +/* Define this string only once for all macro invocations */
- +static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n";
- +
- +static char szVersion[] = PPP_VERSION;
- +
- +/*
- + * Information for the protocol decoder
- + */
- +
- +typedef int (*pfn_proto) (struct ppp *, __u16, __u8 *, int);
- +
- +typedef struct ppp_proto_struct {
- + int proto;
- + pfn_proto func;
- +} ppp_proto_type;
- +
- +static
- +ppp_proto_type proto_list[] = {
- + { PPP_IP, rcv_proto_ip },
- + { PPP_IPX, rcv_proto_ipx },
- + { PPP_VJC_COMP, rcv_proto_vjc_comp },
- + { PPP_VJC_UNCOMP, rcv_proto_vjc_uncomp },
- + { PPP_LQR, rcv_proto_lqr },
- + { PPP_CCP, rcv_proto_ccp },
- + { 0, rcv_proto_unknown } /* !!! MUST BE LAST !!! */
- +};
- +
- +__u16 ppp_crc16_table[256] =
- +{
- + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
- + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
- + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
- + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
- + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
- + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
- + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
- + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
- + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
- + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
- + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
- + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
- + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
- + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
- + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
- + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
- + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
- + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
- + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
- + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
- + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
- + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
- + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
- + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
- + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
- + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
- + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
- + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
- + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
- + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
- + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
- + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
- +};
- +
- +#ifdef CHECK_CHARACTERS
- +static __u32 paritytab[8] =
- +{
- + 0x96696996, 0x69969669, 0x69969669, 0x96696996,
- + 0x69969669, 0x96696996, 0x96696996, 0x69969669
- +};
- +#endif
- +
- +/* local function to store a value into the LQR frame */
- +extern inline __u8 * store_long (register __u8 *p, register int value) {
- + *p++ = (__u8) (value >> 24);
- + *p++ = (__u8) (value >> 16);
- + *p++ = (__u8) (value >> 8);
- + *p++ = (__u8) value;
- + return p;
- +}
- +
- +/*************************************************************
- + * INITIALIZATION
- + *************************************************************/
- +
- +/* This procedure is called once and once only to define who we are to
- + * the operating system and the various procedures that it may use in
- + * accessing the ppp protocol.
- + */
- +
- +static int
- +ppp_first_time (void)
- +{
- + static struct tty_ldisc ppp_ldisc;
- + int status;
- +
- + printk (KERN_INFO
- + "PPP: version %s (dynamic channel allocation)"
- + "\n", szVersion);
- +
- +#ifndef MODULE /* slhc module logic has its own copyright announcement */
- + printk (KERN_INFO
- + "TCP compression code copyright 1989 Regents of the "
- + "University of California\n");
- +#endif
- +
- + printk (KERN_INFO
- + "PPP Dynamic channel allocation code copyright 1995 "
- + "Caldera, Inc.\n");
- +/*
- + * Register the tty discipline
- + */
- + (void) memset (&ppp_ldisc, 0, sizeof (ppp_ldisc));
- + ppp_ldisc.magic = TTY_LDISC_MAGIC;
- + ppp_ldisc.open = ppp_tty_open;
- + ppp_ldisc.close = ppp_tty_close;
- + ppp_ldisc.read = ppp_tty_read;
- + ppp_ldisc.write = ppp_tty_write;
- + ppp_ldisc.ioctl = ppp_tty_ioctl;
- + ppp_ldisc.select = ppp_tty_select;
- + ppp_ldisc.receive_room = ppp_tty_room;
- + ppp_ldisc.receive_buf = ppp_tty_receive;
- + ppp_ldisc.write_wakeup = ppp_tty_wakeup;
- +
- + status = tty_register_ldisc (N_PPP, &ppp_ldisc);
- + if (status == 0)
- + printk (KERN_INFO "PPP line discipline registered.\n");
- + else
- + printk (KERN_ERR "error registering line discipline: %d\n",
- + status);
- + return status;
- +}
- +
- +/*************************************************************
- + * INITIALIZATION
- + *************************************************************/
- +
- +/* called when the device is actually created */
- +
- +static int
- +ppp_init_dev (struct device *dev)
- +{
- + int indx;
- +
- + dev->hard_header = ppp_dev_header;
- + dev->rebuild_header = ppp_dev_rebuild;
- + dev->hard_header_len = PPP_HARD_HDR_LEN;
- +
- + /* device INFO */
- + dev->mtu = PPP_MTU;
- + dev->hard_start_xmit = ppp_dev_xmit;
- + dev->open = ppp_dev_open;
- + dev->stop = ppp_dev_close;
- + dev->get_stats = ppp_dev_stats;
- + dev->do_ioctl = ppp_dev_ioctl;
- + dev->addr_len = 0;
- + dev->tx_queue_len = 10;
- + dev->type = ARPHRD_PPP;
- +
- + for (indx = 0; indx < DEV_NUMBUFFS; indx++)
- + skb_queue_head_init (&dev->buffs[indx]);
- +
- + /* New-style flags */
- + dev->flags = IFF_POINTOPOINT;
- + dev->family = AF_INET;
- + dev->pa_addr = 0;
- + dev->pa_brdaddr = 0;
- + dev->pa_mask = 0;
- + dev->pa_alen = 4; /* sizeof (__u32) */
- +
- + return 0;
- +}
- +
- +/*
- + * Local procedure to initialize the ppp structure
- + */
- +
- +static void
- +ppp_init_ctrl_blk (register struct ppp *ppp)
- +{
- + ppp->magic = PPP_MAGIC;
- + ppp->toss = 0xE0;
- + ppp->escape = 0;
- +
- + ppp->flags = 0;
- + ppp->mtu = PPP_MTU;
- + ppp->mru = PPP_MRU;
- +
- + memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map));
- + ppp->xmit_async_map[0] = 0xffffffff;
- + ppp->xmit_async_map[3] = 0x60000000;
- + ppp->recv_async_map = 0x00000000;
- +
- + ppp->rbuf = NULL;
- + ppp->wbuf = NULL;
- + ppp->ubuf = NULL;
- + ppp->cbuf = NULL;
- + ppp->slcomp = NULL;
- + ppp->read_wait = NULL;
- + ppp->write_wait = NULL;
- + ppp->last_xmit = jiffies - flag_time;
- +
- + /* clear statistics */
- + memset (&ppp->stats, '\0', sizeof (struct pppstat));
- +
- + /* Reset the demand dial information */
- + ppp->ddinfo.xmit_idle= /* time since last NP packet sent */
- + ppp->ddinfo.recv_idle=jiffies; /* time since last NP packet received */
- +
- + /* PPP compression data */
- + ppp->sc_xc_state =
- + ppp->sc_rc_state = NULL;
- +}
- +
- +static struct symbol_table ppp_syms = {
- +#include <linux/symtab_begin.h>
- + X(ppp_register_compressor),
- + X(ppp_unregister_compressor),
- + X(ppp_crc16_table),
- +#include <linux/symtab_end.h>
- +};
- +
- +/* called at boot/load time for each ppp device defined in the kernel */
- +
- +#ifndef MODULE
- +int
- +ppp_init (struct device *dev)
- +{
- + static int first_time = 1;
- + int answer = 0;
- +
- + if (first_time) {
- + first_time = 0;
- + answer = ppp_first_time();
- + if (answer == 0)
- + (void) register_symtab (&ppp_syms);
- + }
- + if (answer == 0)
- + answer = -ENODEV;
- + return answer;
- +}
- +#endif
- +
- +/*
- + * Routine to allocate a buffer for later use by the driver.
- + */
- +
- +static struct ppp_buffer *
- +ppp_alloc_buf (int size, int type)
- +{
- + struct ppp_buffer *buf;
- +
- + buf = (struct ppp_buffer *) kmalloc (size + sizeof (struct ppp_buffer),
- + GFP_ATOMIC);
- +
- + if (buf != NULL) {
- + buf->size = size - 1; /* Mask for the buffer size */
- + buf->type = type;
- + buf->locked = 0;
- + buf->count = 0;
- + buf->head = 0;
- + buf->tail = 0;
- + buf->fcs = PPP_INITFCS;
- +
- + }
- + return (buf);
- +}
- +
- +/*
- + * Routine to release the allocated buffer.
- + */
- +
- +static void
- +ppp_free_buf (struct ppp_buffer *ptr)
- +{
- + if (ptr != NULL)
- + kfree (ptr);
- +}
- +
- +/*
- + * Lock the indicated transmit buffer
- + */
- +
- +extern inline int
- +lock_buffer (register struct ppp_buffer *buf)
- +{
- + register int state;
- + int flags;
- +/*
- + * Save the current state and if free then set it to the "busy" state
- + */
- + save_flags (flags);
- + cli ();
- + state = buf->locked;
- + if (state == 0)
- + buf->locked = 2;
- +
- + restore_flags (flags);
- + return (state);
- +}
- +
- +/*
- + * MTU has been changed by the IP layer. Unfortunately we are not told
- + * about this, but we spot it ourselves and fix things up. We could be
- + * in an upcall from the tty driver, or in an ip packet queue.
- + */
- +
- +static int
- +ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru)
- +{
- + struct device *dev;
- +
- + struct ppp_buffer *new_rbuf;
- + struct ppp_buffer *new_wbuf;
- + struct ppp_buffer *new_cbuf;
- + struct ppp_buffer *new_tbuf;
- +
- + struct ppp_buffer *old_rbuf;
- + struct ppp_buffer *old_wbuf;
- + struct ppp_buffer *old_cbuf;
- + struct ppp_buffer *old_tbuf;
- +
- + int mtu, mru;
- +/*
- + * Allocate the buffer from the kernel for the data
- + */
- + dev = ppp2dev (ppp);
- + mru = new_mru;
- + /* allow for possible escaping of every character */
- + mtu = (new_mtu * 2) + 20;
- +
- + /* RFC 1331, section 7.2 says the minimum value is 1500 bytes */
- + if (mru < PPP_MRU)
- + mru = PPP_MRU;
- +
- + mru += 10;
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO "ppp: channel %s mtu = %d, mru = %d\n",
- + dev->name, new_mtu, new_mru);
- +
- + new_wbuf = ppp_alloc_buf (mtu+PPP_HARD_HDR_LEN, BUFFER_TYPE_DEV_WR);
- + new_tbuf = ppp_alloc_buf ((PPP_MTU * 2) + 24, BUFFER_TYPE_TTY_WR);
- + new_rbuf = ppp_alloc_buf (mru + 84, BUFFER_TYPE_DEV_RD);
- + new_cbuf = ppp_alloc_buf (mru+PPP_HARD_HDR_LEN, BUFFER_TYPE_VJ);
- +/*
- + * If the buffers failed to allocate then complain and release the partial
- + * allocations.
- + */
- + if (new_wbuf == NULL || new_tbuf == NULL ||
- + new_rbuf == NULL || new_cbuf == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp: failed to allocate new buffers\n");
- +
- + ppp_free_buf (new_wbuf);
- + ppp_free_buf (new_tbuf);
- + ppp_free_buf (new_rbuf);
- + ppp_free_buf (new_cbuf);
- + return 0;
- + }
- +/*
- + * Update the pointers to the new buffer structures.
- + */
- + cli ();
- + old_wbuf = ppp->wbuf;
- + old_rbuf = ppp->rbuf;
- + old_cbuf = ppp->cbuf;
- + old_tbuf = ppp->tbuf;
- +
- + ppp->wbuf = new_wbuf;
- + ppp->rbuf = new_rbuf;
- + ppp->cbuf = new_cbuf;
- + ppp->tbuf = new_tbuf;
- +
- + ppp->rbuf->size -= 80; /* reserve space for vj header expansion */
- +
- + dev->mem_start = (unsigned long) buf_base (new_wbuf);
- + dev->mem_end = (unsigned long) (dev->mem_start + mtu);
- + dev->rmem_start = (unsigned long) buf_base (new_rbuf);
- + dev->rmem_end = (unsigned long) (dev->rmem_start + mru);
- +/*
- + * Update the parameters for the new buffer sizes
- + */
- + ppp->toss = 0xE0; /* To ignore characters until new FLAG */
- + ppp->escape = 0; /* No pending escape character */
- +
- + dev->mtu =
- + ppp->mtu = new_mtu;
- + ppp->mru = new_mru;
- +
- + ppp->s1buf = NULL;
- + ppp->s2buf = NULL;
- + ppp->xbuf = NULL;
- +
- + ppp->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
- + ppp->flags &= ~SC_XMIT_BUSY;
- +
- + sti ();
- +/*
- + * Release old buffer pointers
- + */
- + ppp_free_buf (old_rbuf);
- + ppp_free_buf (old_wbuf);
- + ppp_free_buf (old_cbuf);
- + ppp_free_buf (old_tbuf);
- + return 1;
- +}
- +
- +/*
- + * CCP is down; free (de)compressor state if necessary.
- + */
- +
- +static void
- +ppp_ccp_closed (struct ppp *ppp)
- +{
- + if (ppp->sc_xc_state) {
- + (*ppp->sc_xcomp->comp_free) (ppp->sc_xc_state);
- + ppp->sc_xc_state = NULL;
- + }
- +
- + if (ppp->sc_rc_state) {
- + (*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state);
- + ppp->sc_rc_state = NULL;
- + }
- +}
- +
- +/*
- + * Called to release all of the information in the current PPP structure.
- + *
- + * It is called when the ppp device goes down or if it is unable to go
- + * up.
- + */
- +
- +static void
- +ppp_release (struct ppp *ppp)
- +{
- + struct tty_struct *tty;
- + struct device *dev;
- +
- + tty = ppp2tty (ppp);
- + dev = ppp2dev (ppp);
- +
- + ppp_ccp_closed (ppp);
- +
- + /* Ensure that the pppd process is not hanging on select() */
- + wake_up_interruptible (&ppp->read_wait);
- + wake_up_interruptible (&ppp->write_wait);
- +
- + if (tty != NULL && tty->disc_data == ppp)
- + tty->disc_data = NULL; /* Break the tty->ppp link */
- +
- + if (dev && dev->flags & IFF_UP) {
- + dev_close (dev); /* close the device properly */
- + dev->flags = 0; /* prevent recursion */
- + }
- +
- + ppp_free_buf (ppp->rbuf);
- + ppp_free_buf (ppp->wbuf);
- + ppp_free_buf (ppp->cbuf);
- + ppp_free_buf (ppp->ubuf);
- + ppp_free_buf (ppp->tbuf);
- +
- + ppp->rbuf =
- + ppp->wbuf =
- + ppp->cbuf =
- + ppp->tbuf =
- + ppp->xbuf =
- + ppp->s1buf =
- + ppp->s2buf =
- + ppp->ubuf = NULL;
- +
- + if (ppp->slcomp) {
- + slhc_free (ppp->slcomp);
- + ppp->slcomp = NULL;
- + }
- +
- + ppp->inuse = 0;
- + ppp->tty = NULL;
- +}
- +
- +/*
- + * Device callback.
- + *
- + * Called when the PPP device goes down in response to an ifconfig request.
- + */
- +
- +static void
- +ppp_tty_close_local (struct tty_struct *tty, int sc_xfer)
- +{
- + struct ppp *ppp = tty2ppp (tty);
- +
- + if (ppp != NULL) {
- + if (ppp->magic != PPP_MAGIC) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp: trying to close unopened tty!\n");
- + } else {
- + CHECK_PPP_VOID();
- + ppp->sc_xfer = sc_xfer;
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO "ppp: channel %s closing.\n",
- + ppp2dev(ppp) -> name);
- + ppp_release (ppp);
- + MOD_DEC_USE_COUNT;
- + }
- + }
- +}
- +
- +static void
- +ppp_tty_close (struct tty_struct *tty)
- +{
- + ppp_tty_close_local (tty, 0);
- +}
- +
- +/*
- + * TTY callback.
- + *
- + * Called when the tty discipline is switched to PPP.
- + */
- +
- +static int
- +ppp_tty_open (struct tty_struct *tty)
- +{
- + struct ppp *ppp = tty2ppp (tty);
- + int indx;
- +/*
- + * There should not be an existing table for this slot.
- + */
- + if (ppp) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_tty_open: gack! tty already associated to %s!\n",
- + ppp->magic == PPP_MAGIC ? ppp2dev(ppp)->name
- + : "unknown");
- + return -EEXIST;
- + }
- +/*
- + * Allocate the structure from the system
- + */
- + ppp = ppp_find(current->pid);
- + if (ppp == NULL) {
- + ppp = ppp_find(0);
- + if (ppp == NULL)
- + ppp = ppp_alloc();
- + }
- +
- + if (ppp == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_tty_open: couldn't allocate ppp channel\n");
- + return -ENFILE;
- + }
- +/*
- + * Initialize the control block
- + */
- + ppp_init_ctrl_blk (ppp);
- + ppp->tty = tty;
- + tty->disc_data = ppp;
- +/*
- + * Flush any pending characters in the driver and discipline.
- + */
- + if (tty->ldisc.flush_buffer)
- + tty->ldisc.flush_buffer (tty);
- +
- + if (tty->driver.flush_buffer)
- + tty->driver.flush_buffer (tty);
- +/*
- + * Allocate space for the default VJ header compression slots
- + */
- + ppp->slcomp = slhc_init (16, 16);
- + if (ppp->slcomp == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_tty_open: no space for compression buffers!\n");
- + ppp_release (ppp);
- + return -ENOMEM;
- + }
- +/*
- + * Allocate space for the MTU and MRU buffers
- + */
- + if (ppp_changedmtu (ppp, ppp2dev(ppp)->mtu, ppp->mru) == 0) {
- + ppp_release (ppp);
- + return -ENOMEM;
- + }
- +/*
- + * Allocate space for a user level buffer
- + */
- + ppp->ubuf = ppp_alloc_buf (RBUFSIZE, BUFFER_TYPE_TTY_RD);
- + if (ppp->ubuf == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_tty_open: no space for user receive buffer\n");
- + ppp_release (ppp);
- + return -ENOMEM;
- + }
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO "ppp: channel %s open\n",
- + ppp2dev(ppp)->name);
- +
- + for (indx = 0; indx < NUM_NP; ++indx)
- + ppp->sc_npmode[indx] = NPMODE_PASS;
- +
- + MOD_INC_USE_COUNT;
- + return (ppp->line);
- +}
- +
- +/*
- + * Local function to send the next portion of the buffer.
- + *
- + * Called by the tty driver's tty_wakeup function should it be entered
- + * because the partial buffer was transmitted.
- + *
- + * Called by kick_tty to send the initial portion of the buffer.
- + *
- + * Completion processing of the buffer transmission is handled here.
- + */
- +
- +static void
- +ppp_tty_wakeup_code (struct ppp *ppp, struct tty_struct *tty,
- + struct ppp_buffer *xbuf)
- +{
- + register int count, actual;
- +/*
- + * Prevent re-entrancy by ensuring that this routine is called only once.
- + */
- + cli ();
- + if (ppp->flags & SC_XMIT_BUSY) {
- + sti ();
- + return;
- + }
- + ppp->flags |= SC_XMIT_BUSY;
- + sti ();
- +/*
- + * Send the next block of data to the modem
- + */
- + count = xbuf->count - xbuf->tail;
- + actual = tty->driver.write (tty, 0,
- + buf_base (xbuf) + xbuf->tail, count);
- +/*
- + * Terminate transmission of any block which may have an error.
- + * This could occur should the carrier drop.
- + */
- + if (actual < 0) {
- + ppp->stats.ppp_oerrors++;
- + actual = count;
- + } else
- + ppp->bytes_sent += actual;
- +/*
- + * If the buffer has been transmitted then clear the indicators.
- + */
- + xbuf->tail += actual;
- + if (actual == count) {
- + xbuf = NULL;
- + ppp->flags &= ~SC_XMIT_BUSY;
- +/*
- + * Complete the transmission on the current buffer.
- + */
- + xbuf = ppp->xbuf;
- + if (xbuf != NULL) {
- + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
- + xbuf->locked = 0;
- + ppp->xbuf = NULL;
- +/*
- + * If the completed buffer came from the device write, then complete the
- + * transmission block.
- + */
- + if (ppp2dev (ppp) -> flags & IFF_UP) {
- + if (xbuf->type == BUFFER_TYPE_DEV_WR)
- + ppp2dev (ppp)->tbusy = 0;
- + mark_bh (NET_BH);
- + }
- +/*
- + * Wake up the transmission queue for all completion events.
- + */
- + wake_up_interruptible (&ppp->write_wait);
- +/*
- + * Look at the priorities. Choose a daemon write over the device driver.
- + */
- + cli();
- + xbuf = ppp->s1buf;
- + ppp->s1buf = NULL;
- + if (xbuf == NULL) {
- + xbuf = ppp->s2buf;
- + ppp->s2buf = NULL;
- + }
- + sti();
- +/*
- + * If there is a pending buffer then transmit it now.
- + */
- + if (xbuf != NULL) {
- + ppp->flags &= ~SC_XMIT_BUSY;
- + ppp_kick_tty (ppp, xbuf);
- + return;
- + }
- + }
- + }
- +/*
- + * Clear the re-entry flag
- + */
- + ppp->flags &= ~SC_XMIT_BUSY;
- +}
- +
- +/*
- + * This function is called by the tty driver when the transmit buffer has
- + * additional space. It is used by the ppp code to continue to transmit
- + * the current buffer should the buffer have been partially sent.
- + *
- + * In addition, it is used to send the first part of the buffer since the
- + * logic and the inter-locking would be identical.
- + */
- +
- +static void
- +ppp_tty_wakeup (struct tty_struct *tty)
- +{
- + struct ppp_buffer *xbuf;
- + struct ppp *ppp = tty2ppp (tty);
- +
- + if (!ppp)
- + return;
- +
- + if (ppp->magic != PPP_MAGIC)
- + return;
- +/*
- + * Ensure that there is a transmission pending. Clear the re-entry flag if
- + * there is no pending buffer. Otherwise, send the buffer.
- + */
- + xbuf = ppp->xbuf;
- + if (xbuf == NULL)
- + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
- + else
- + ppp_tty_wakeup_code (ppp, tty, xbuf);
- +}
- +
- +/*
- + * This function is called to transmit a buffer to the remote. The buffer
- + * is placed on the pending queue if there is presently a buffer being
- + * sent or it is transmitted with the aid of ppp_tty_wakeup.
- + */
- +
- +static void
- +ppp_kick_tty (struct ppp *ppp, struct ppp_buffer *xbuf)
- +{
- + register int flags;
- +/*
- + * Hold interrupts.
- + */
- + save_flags (flags);
- + cli ();
- +/*
- + * Control the flags which are best performed with the interrupts masked.
- + */
- + xbuf->locked = 1;
- + xbuf->tail = 0;
- +/*
- + * If the transmitter is busy then place the buffer on the appropriate
- + * priority queue.
- + */
- + if (ppp->xbuf != NULL) {
- + if (xbuf->type == BUFFER_TYPE_TTY_WR)
- + ppp->s1buf = xbuf;
- + else
- + ppp->s2buf = xbuf;
- + restore_flags (flags);
- + return;
- + }
- +/*
- + * If the transmitter is not busy then this is the highest priority frame
- + */
- + ppp->flags &= ~SC_XMIT_BUSY;
- + ppp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
- + ppp->xbuf = xbuf;
- + restore_flags (flags);
- +/*
- + * Do the "tty wakeup_code" to actually send this buffer.
- + */
- + ppp_tty_wakeup_code (ppp, ppp2tty (ppp), xbuf);
- +}
- +
- +/*************************************************************
- + * TTY INPUT
- + * The following functions handle input that arrives from
- + * the TTY. It recognizes PPP frames and either hands them
- + * to the network layer or queues them for delivery to a
- + * user process reading this TTY.
- + *************************************************************/
- +
- +/*
- + * Callback function from tty driver. Return the amount of space left
- + * in the receiver's buffer to decide if remote transmitter is to be
- + * throttled.
- + */
- +
- +static int
- +ppp_tty_room (struct tty_struct *tty)
- +{
- + return 65536; /* We can handle an infinite amount of data. :-) */
- +}
- +
- +/*
- + * Callback function when data is available at the tty driver.
- + */
- +
- +static void
- +ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
- + char *flags, int count)
- +{
- + register struct ppp *ppp = tty2ppp (tty);
- + register struct ppp_buffer *buf = NULL;
- + __u8 chr;
- +/*
- + * Fetch the pointer to the buffer. Be careful about race conditions.
- + */
- + if (ppp != NULL)
- + buf = ppp->rbuf;
- +
- + if (buf == NULL)
- + return;
- +/*
- + * Verify the table pointer and ensure that the line is
- + * still in PPP discipline.
- + */
- + if (ppp->magic != PPP_MAGIC) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "PPP: handler called but couldn't find "
- + "PPP struct.\n");
- + return;
- + }
- + CHECK_PPP_VOID ();
- +/*
- + * Print the buffer if desired
- + */
- + if (ppp->flags & SC_LOG_RAWIN)
- + ppp_print_buffer ("receive buffer", data, count);
- +/*
- + * Collect the character and error condition for the character. Set the toss
- + * flag for the first character error.
- + */
- + while (count-- > 0) {
- + ppp->bytes_rcvd++;
- + chr = *data++;
- + if (flags) {
- + if (*flags && ppp->toss == 0)
- + ppp->toss = *flags;
- + ++flags;
- + }
- +/*
- + * Set the flags for 8 data bits and no parity.
- + *
- + * Actually, it sets the flags for d7 being 0/1 and parity being even/odd
- + * so that the normal processing would have all flags set at the end of the
- + * session. A missing flag bit would denote an error condition.
- + */
- +
- +#ifdef CHECK_CHARACTERS
- + if (chr & 0x80)
- + ppp->flags |= SC_RCV_B7_1;
- + else
- + ppp->flags |= SC_RCV_B7_0;
- +
- + if (paritytab[chr >> 5] & (1 << (chr & 0x1F)))
- + ppp->flags |= SC_RCV_ODDP;
- + else
- + ppp->flags |= SC_RCV_EVNP;
- +#endif
- +/*
- + * Branch on the character. Process the escape character. The sequence ESC ESC
- + * is defined to be ESC.
- + */
- + switch (chr) {
- + case PPP_ESCAPE: /* PPP_ESCAPE: invert bit in next character */
- + ppp->escape = PPP_TRANS;
- + break;
- +/*
- + * FLAG. This is the end of the block. If the block terminated by ESC FLAG,
- + * then the block is to be ignored. In addition, characters before the very
- + * first FLAG are also tossed by this procedure.
- + */
- + case PPP_FLAG: /* PPP_FLAG: end of frame */
- + ppp->stats.ppp_ibytes += ppp->rbuf->count;
- + if (ppp->escape)
- + ppp->toss |= 0x80;
- +/*
- + * Process frames which are not to be ignored. If the processing failed,
- + * then clean up the VJ tables.
- + */
- + if ((ppp->toss & 0x80) != 0 ||
- + ppp_doframe (ppp) == 0) {
- + slhc_toss (ppp->slcomp);
- + }
- +/*
- + * Reset all indicators for the new frame to follow.
- + */
- + buf->count = 0;
- + buf->fcs = PPP_INITFCS;
- + ppp->escape = 0;
- + ppp->toss = 0;
- + break;
- +/*
- + * All other characters in the data come here. If the character is in the
- + * receive mask then ignore the character.
- + */
- + default:
- + if (in_rmap (ppp, chr))
- + break;
- +/*
- + * Adjust the character and if the frame is to be discarded then simply
- + * ignore the character until the ending FLAG is received.
- + */
- + chr ^= ppp->escape;
- + ppp->escape = 0;
- +
- + if (ppp->toss != 0)
- + break;
- +/*
- + * If the count sent is within reason then store the character, bump the
- + * count, and update the FCS for the character.
- + */
- + if (buf->count < buf->size) {
- + buf_base (buf)[buf->count++] = chr;
- + buf->fcs = PPP_FCS (buf->fcs, chr);
- + break;
- + }
- +/*
- + * The peer sent too much data. Set the flags to discard the current frame
- + * and wait for the re-synchronization FLAG to be sent.
- + */
- + ppp->stats.ppp_ierrors++;
- + ppp->toss |= 0xC0;
- + break;
- + }
- + }
- +}
- +
- +/*
- + * Put the input frame into the networking system for the indicated protocol
- + */
- +
- +static int
- +ppp_rcv_rx (struct ppp *ppp, __u16 proto, __u8 * data, int count)
- +{
- + sk_buff *skb = dev_alloc_skb (count);
- +/*
- + * Generate a skb buffer for the new frame.
- + */
- + if (skb == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_do_ip: packet dropped on %s (no memory)!\n",
- + ppp2dev (ppp)->name);
- + return 0;
- + }
- +/*
- + * Move the received data from the input buffer to the skb buffer.
- + */
- + skb->dev = ppp2dev (ppp); /* We are the device */
- + skb->protocol = proto;
- + skb->mac.raw = skb_data(skb);
- + memcpy (skb_put(skb,count), data, count); /* move data */
- +/*
- + * Tag the frame and kick it to the proper receive routine
- + */
- + skb->free = 1;
- + ppp->ddinfo.recv_idle = jiffies;
- + netif_rx (skb);
- + return 1;
- +}
- +
- +/*
- + * Process the receipt of an IP frame
- + */
- +
- +static int
- +rcv_proto_ip (struct ppp *ppp, __u16 proto, __u8 * data, int count)
- +{
- + if ((ppp2dev (ppp)->flags & IFF_UP) && (count > 0))
- + if (ppp->sc_npmode[NP_IP] == NPMODE_PASS)
- + return ppp_rcv_rx (ppp, htons (ETH_P_IP), data, count);
- + return 0;
- +}
- +
- +/*
- + * Process the receipt of an IPX frame
- + */
- +
- +static int
- +rcv_proto_ipx (struct ppp *ppp, __u16 proto, __u8 * data, int count)
- +{
- + if (((ppp2dev (ppp)->flags & IFF_UP) != 0) && (count > 0))
- + return ppp_rcv_rx (ppp, htons (ETH_P_IPX), data, count);
- + return 0;
- +}
- +
- +/*
- + * Process the receipt of an VJ Compressed frame
- + */
- +
- +static int
- +rcv_proto_vjc_comp (struct ppp *ppp, __u16 proto,
- + __u8 *data, int count)
- +{
- + if ((ppp->flags & SC_REJ_COMP_TCP) == 0) {
- + int new_count = slhc_uncompress (ppp->slcomp, data, count);
- + if (new_count >= 0) {
- + return rcv_proto_ip (ppp, PPP_IP, data, new_count);
- + }
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_NOTICE
- + "ppp: error in VJ decompression\n");
- + }
- + return 0;
- +}
- +
- +/*
- + * Process the receipt of an VJ Un-compressed frame
- + */
- +
- +static int
- +rcv_proto_vjc_uncomp (struct ppp *ppp, __u16 proto,
- + __u8 *data, int count)
- +{
- + if ((ppp->flags & SC_REJ_COMP_TCP) == 0) {
- + if (slhc_remember (ppp->slcomp, data, count) > 0) {
- + return rcv_proto_ip (ppp, PPP_IP, data, count);
- + }
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_NOTICE
- + "ppp: error in VJ memorizing\n");
- + }
- + return 0;
- +}
- +
- +/*
- + * Receive all unclassified protocols.
- + */
- +
- +static int
- +rcv_proto_unknown (struct ppp *ppp, __u16 proto,
- + __u8 *data, int len)
- +{
- + int totlen;
- + register int current_idx;
- +
- +#define PUTC(c) \
- +{ \
- + buf_base (ppp->ubuf) [current_idx++] = (__u8) (c); \
- + current_idx &= ppp->ubuf->size; \
- + if (current_idx == ppp->ubuf->tail) \
- + goto failure; \
- +}
- +
- +/*
- + * The total length includes the protocol data.
- + * Lock the user information buffer.
- + */
- + if (set_bit (0, &ppp->ubuf->locked)) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_us_queue: can't get lock\n");
- + } else {
- + current_idx = ppp->ubuf->head;
- +/*
- + * Insert the buffer length (not counted), the protocol, and the data
- + */
- + totlen = len + 2;
- + PUTC (totlen >> 8);
- + PUTC (totlen);
- +
- + PUTC (proto >> 8);
- + PUTC (proto);
- +
- + totlen -= 2;
- + while (totlen-- > 0) {
- + PUTC (*data++);
- + }
- +#undef PUTC
- +/*
- + * The frame is complete. Update the head pointer and wakeup the pppd
- + * process.
- + */
- + ppp->ubuf->head = current_idx;
- +
- + clear_bit (0, &ppp->ubuf->locked);
- + wake_up_interruptible (&ppp->read_wait);
- + if (ppp->tty->fasync != NULL)
- + kill_fasync (ppp->tty->fasync, SIGIO);
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp: successfully queued %d bytes, flags = %x\n",
- + len + 2, ppp->flags);
- +
- + return 1;
- +/*
- + * The buffer is full. Unlock the header
- + */
- +failure:
- + clear_bit (0, &ppp->ubuf->locked);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_us_queue: ran out of buffer space.\n");
- + }
- +/*
- + * Discard the frame. There are no takers for this protocol.
- + */
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp: dropping packet on the floor.\n");
- + slhc_toss (ppp->slcomp);
- + return 0;
- +}
- +
- +/*
- + * Handle a CCP packet.
- + *
- + * The CCP packet is passed along to the pppd process just like any
- + * other PPP frame. The difference is that some processing needs to be
- + * immediate or the compressors will become confused on the peer.
- + */
- +
- +static void ppp_proto_ccp (struct ppp *ppp, __u8 *dp, int len, int rcvd)
- +{
- + int slen = CCP_LENGTH(dp);
- + __u8 *opt = dp + CCP_HDRLEN;
- + int opt_len = slen - CCP_HDRLEN;
- +
- + if (slen > len)
- + return;
- +
- + switch (CCP_CODE(dp)) {
- + case CCP_CONFREQ:
- + case CCP_TERMREQ:
- + case CCP_TERMACK:
- +/*
- + * CCP must be going down - disable compression
- + */
- + if (ppp->flags & SC_CCP_UP) {
- + ppp->flags &= ~(SC_CCP_UP |
- + SC_COMP_RUN |
- + SC_DECOMP_RUN);
- + }
- + break;
- +
- + case CCP_CONFACK:
- + if ((ppp->flags & SC_CCP_OPEN) == 0)
- + break;
- + if (ppp->flags & SC_CCP_UP)
- + break;
- + if (slen < (CCP_HDRLEN + CCP_OPT_MINLEN))
- + break;
- + if (slen < (CCP_OPT_LENGTH (opt) + CCP_HDRLEN))
- + break;
- +/*
- + * we're agreeing to send compressed packets.
- + */
- + if (!rcvd) {
- + if (ppp->sc_xc_state == NULL)
- + break;
- +
- + if ((*ppp->sc_xcomp->comp_init)
- + (ppp->sc_xc_state,
- + opt,
- + opt_len,
- + ppp2dev (ppp)->base_addr,
- + 0,
- + ppp->flags))
- + ppp->flags |= SC_COMP_RUN;
- + break;
- + }
- +/*
- + * peer is agreeing to send compressed packets.
- + */
- + if (ppp->sc_rc_state == NULL)
- + break;
- +
- + if ((*ppp->sc_rcomp->decomp_init)
- + (ppp->sc_rc_state,
- + opt,
- + opt_len,
- + ppp2dev (ppp)->base_addr,
- + 0,
- + ppp->mru,
- + ppp->flags)) {
- + ppp->flags |= SC_DECOMP_RUN;
- + ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
- + }
- + break;
- +/*
- + * The protocol sequence is complete at this end
- + */
- + case CCP_RESETACK:
- + if ((ppp->flags & SC_CCP_UP) == 0)
- + break;
- +
- + if (!rcvd) {
- + if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN))
- + (*ppp->sc_xcomp->comp_reset)(ppp->sc_xc_state);
- + } else {
- + if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) {
- + (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state);
- + ppp->flags &= ~SC_DC_ERROR;
- + }
- + }
- + break;
- + }
- +}
- +
- +static int
- +rcv_proto_ccp (struct ppp *ppp, __u16 proto, __u8 *dp, int len)
- +{
- + ppp_proto_ccp (ppp, dp, len, 1);
- + return rcv_proto_unknown (ppp, proto, dp, len);
- +}
- +
- +/*
- + * Handle a LQR packet.
- + */
- +
- +static int
- +rcv_proto_lqr (struct ppp *ppp, __u16 proto, __u8 * data, int len)
- +{
- + return rcv_proto_unknown (ppp, proto, data, len);
- +}
- +
- +/* on entry, a received frame is in ppp->rbuf.bufr
- + check it and dispose as appropriate */
- +
- +static void ppp_doframe_lower (struct ppp *ppp, __u8 *data, int count)
- +{
- + __u16 proto = PPP_PROTOCOL (data);
- + ppp_proto_type *proto_ptr;
- +/*
- + * Ignore empty frames
- + */
- + if (count <= 4)
- + return;
- +/*
- + * Count the frame and print it
- + */
- + ++ppp->stats.ppp_ipackets;
- + if (ppp->flags & SC_LOG_INPKT)
- + ppp_print_buffer ("receive frame", data, count);
- +/*
- + * Find the procedure to handle this protocol. The last one is marked
- + * as a protocol 0 which is the 'catch-all' to feed it to the pppd daemon.
- + */
- + proto_ptr = proto_list;
- + while (proto_ptr->proto != 0 && proto_ptr->proto != proto)
- + ++proto_ptr;
- +/*
- + * Update the appropriate statistic counter.
- + */
- + if ((*proto_ptr->func) (ppp, proto,
- + &data[PPP_HARD_HDR_LEN],
- + count - PPP_HARD_HDR_LEN))
- + ppp->stats.ppp_ioctects += count;
- + else
- + ++ppp->stats.ppp_discards;
- +}
- +
- +/* on entry, a received frame is in ppp->rbuf.bufr
- + check it and dispose as appropriate */
- +
- +static int
- +ppp_doframe (struct ppp *ppp)
- +{
- + __u8 *data = buf_base (ppp->rbuf);
- + int count = ppp->rbuf->count;
- + int addr, ctrl, proto;
- + int new_count;
- + __u8 *new_data;
- +#ifndef NO_RMK_TEMP_FIX
- + int malloced_data;
- +#endif
- +/*
- + * If there is a pending error from the receiver then log it and discard
- + * the damaged frame.
- + */
- + if (ppp->toss) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp_toss: tossing frame, reason = %d\n",
- + ppp->toss);
- + ppp->stats.ppp_ierrors++;
- + return 0;
- + }
- +/*
- + * An empty frame is ignored. This occurs if the FLAG sequence precedes and
- + * follows each frame.
- + */
- + if (count == 0)
- + return 1;
- +/*
- + * Generate an error if the frame is too small.
- + */
- + if (count < PPP_HARD_HDR_LEN) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp: got runt ppp frame, %d chars\n", count);
- + slhc_toss (ppp->slcomp);
- + ppp->stats.ppp_ierrors++;
- + return 1;
- + }
- +/*
- + * Verify the CRC of the frame and discard the CRC characters from the
- + * end of the buffer.
- + */
- + if (ppp->rbuf->fcs != PPP_GOODFCS) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp: frame with bad fcs, excess = %x\n",
- + ppp->rbuf->fcs ^ PPP_GOODFCS);
- + ppp->stats.ppp_ierrors++;
- + return 0;
- + }
- + count -= 2; /* ignore the fcs characters */
- +/*
- + * Ignore the leading ADDRESS and CONTROL fields in the frame.
- + */
- + addr = PPP_ALLSTATIONS;
- + ctrl = PPP_UI;
- +
- + if ((data[0] == PPP_ALLSTATIONS) && (data[1] == PPP_UI)) {
- + data += 2;
- + count -= 2;
- + }
- +/*
- + * Obtain the protocol from the frame
- + */
- + proto = (__u16) *data++;
- + if ((proto & 1) == 0) {
- + proto = (proto << 8) | (__u16) *data++;
- + --count;
- + }
- +/*
- + * Rewrite the header with the full information. This may encroach upon
- + * the 'filler' area in the buffer header. This is the purpose for the
- + * filler.
- + */
- + *(--data) = proto;
- + *(--data) = proto >> 8;
- + *(--data) = ctrl;
- + *(--data) = addr;
- + count += 3;
- +#ifndef NO_RMK_TEMP_FIX
- + /*
- + * rmk: should make sure that the packet received is on a word boundary.
- + * this is a temporary fix!!!
- + */
- + if ((int)data & 3) {
- + new_data = kmalloc (ppp->mru + 4, GFP_ATOMIC); /* would like to have 'count' here */
- + memcpy (new_data, data, count);
- + data = new_data;
- + malloced_data = 1;
- + } else
- + malloced_data = 0;
- +#endif
- +/*
- + * Process the active decompressor.
- + */
- + if ((ppp->sc_rc_state != (void *) 0) &&
- + (ppp->flags & SC_DECOMP_RUN) &&
- + ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) {
- + if (proto == PPP_COMP) {
- +/*
- + * If the frame is compressed then decompress it.
- + */
- + new_data = kmalloc (ppp->mru + 4, GFP_ATOMIC);
- + if (new_data == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_doframe: no memory\n");
- + slhc_toss (ppp->slcomp);
- + (*ppp->sc_rcomp->incomp) (ppp->sc_rc_state,
- + data,
- + count);
- +#ifndef NO_RMK_TEMP_FIX
- + if (malloced_data) kfree (data);
- +#endif
- + return 1;
- + }
- +/*
- + * Decompress the frame
- + */
- + new_count = bsd_decompress (ppp->sc_rc_state,
- + data,
- + count,
- + new_data,
- + ppp->mru + 4);
- + switch (new_count) {
- + default:
- + ppp_doframe_lower (ppp, new_data, new_count);
- + kfree (new_data);
- +#ifndef NO_RMK_TEMP_FIX
- + if (malloced_data) kfree (data);
- +#endif
- + return 1;
- +
- + case DECOMP_OK:
- + break;
- +
- + case DECOMP_ERROR:
- + ppp->flags |= SC_DC_ERROR;
- + break;
- +
- + case DECOMP_FATALERROR:
- + ppp->flags |= SC_DC_FERROR;
- + break;
- + }
- +/*
- + * Log the error condition and discard the frame.
- + */
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_proto_comp: "
- + "decompress err %d\n", new_count);
- + kfree (new_data);
- + slhc_toss (ppp->slcomp);
- +#ifndef NO_RMK_TEMP_FIX
- + if (malloced_data) kfree (data);
- +#endif
- + return 1;
- + }
- +/*
- + * The frame is not special. Pass it through the compressor without
- + * actually compressing the data
- + */
- + (*ppp->sc_rcomp->incomp) (ppp->sc_rc_state,
- + data,
- + count);
- + }
- +/*
- + * Process the uncompressed frame.
- + */
- + ppp_doframe_lower (ppp, data, count);
- +#ifndef NO_RMK_TEMP_FIX
- + if (malloced_data) kfree (data);
- +#endif
- + return 1;
- +}
- +
- +/*************************************************************
- + * LINE DISCIPLINE SUPPORT
- + * The following functions form support user programs
- + * which read and write data on a TTY with the PPP line
- + * discipline. Reading is done from a circular queue,
- + * filled by the lower TTY levels.
- + *************************************************************/
- +
- +/* read a PPP frame from the us_rbuff circular buffer,
- + waiting if necessary
- +*/
- +
- +static int
- +ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf,
- + unsigned int nr)
- +{
- + struct ppp *ppp = tty2ppp (tty);
- + __u8 c;
- + int len, indx;
- +
- +#define GETC(c) \
- +{ \
- + c = buf_base (ppp->ubuf) [ppp->ubuf->tail++]; \
- + ppp->ubuf->tail &= ppp->ubuf->size; \
- +}
- +
- +/*
- + * Validate the pointers
- + */
- + if (!ppp)
- + return -EIO;
- +
- + if (ppp->magic != PPP_MAGIC)
- + return -EIO;
- +
- + CHECK_PPP (-ENXIO);
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_tty_read: called buf=%p nr=%u\n",
- + buf, nr);
- +/*
- + * Acquire the read lock.
- + */
- + for (;;) {
- + ppp = tty2ppp (tty);
- + if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse)
- + return 0;
- +
- + if (set_bit (0, &ppp->ubuf->locked) != 0) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_tty_read: sleeping(ubuf)\n");
- +
- + current->timeout = 0;
- + current->state = TASK_INTERRUPTIBLE;
- + schedule ();
- +
- + if (current->signal & ~current->blocked)
- + return -EINTR;
- + continue;
- + }
- +/*
- + * Before we attempt to write the frame to the user, ensure that the
- + * user has access to the pages for the total buffer length.
- + */
- + indx = verify_area (VERIFY_WRITE, buf, nr);
- + if (indx != 0)
- + return (indx);
- +/*
- + * Fetch the length of the buffer from the first two bytes.
- + */
- + if (ppp->ubuf->head == ppp->ubuf->tail)
- + len = 0;
- + else {
- + GETC (c);
- + len = c << 8;
- + GETC (c);
- + len += c;
- + }
- +/*
- + * If there is no length then wait for the data to arrive.
- + */
- + if (len == 0) {
- + /* no data */
- + clear_bit (0, &ppp->ubuf->locked);
- + if (file->f_flags & O_NONBLOCK) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_tty_read: no data "
- + "(EAGAIN)\n");
- + return -EAGAIN;
- + }
- + current->timeout = 0;
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_tty_read: sleeping(read_wait)\n");
- +
- + interruptible_sleep_on (&ppp->read_wait);
- + if (current->signal & ~current->blocked)
- + return -EINTR;
- + continue;
- + }
- +/*
- + * Reset the time of the last read operation.
- + */
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG "ppp_tty_read: len = %d\n", len);
- +/*
- + * Ensure that the frame will fit within the caller's buffer. If not, then
- + * discard the frame from the input buffer.
- + */
- + if (len + 2 > nr) {
- + /* Can't copy it, update us_rbuff_head */
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp: read of %u bytes too small for %d "
- + "frame\n", nr, len + 2);
- + ppp->ubuf->tail += len;
- + ppp->ubuf->tail &= ppp->ubuf->size;
- + clear_bit (0, &ppp->ubuf->locked);
- + ppp->stats.ppp_ierrors++;
- + return -EOVERFLOW;
- + }
- +/*
- + * Before we attempt to write the frame to the user, ensure that the
- + * page tables are proper.
- + */
- + indx = verify_area (VERIFY_WRITE, buf, len + 2);
- + if (indx != 0) {
- + ppp->ubuf->tail += len;
- + ppp->ubuf->tail &= ppp->ubuf->size;
- + clear_bit (0, &ppp->ubuf->locked);
- + return (indx);
- + }
- +/*
- + * Fake the insertion of the ADDRESS and CONTROL information because these
- + * were not saved in the buffer.
- + */
- + put_user (PPP_ALLSTATIONS, buf++);
- + put_user (PPP_UI, buf++);
- +
- + indx = len;
- +/*
- + * Copy the received data from the buffer to the caller's area.
- + */
- + while (indx-- > 0) {
- + GETC (c);
- + put_user (c, buf);
- + ++buf;
- + }
- +
- + clear_bit (0, &ppp->ubuf->locked);
- + len += 2; /* Account for ADDRESS and CONTROL bytes */
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_tty_read: passing %d bytes up\n", len);
- + return len;
- + }
- +#undef GETC
- +}
- +
- +/* stuff a character into the transmit buffer, using PPP's way of escaping
- + special characters.
- + also, update fcs to take account of new character */
- +
- +extern inline void
- +ppp_stuff_char (struct ppp *ppp, register struct ppp_buffer *buf,
- + register __u8 chr)
- +{
- +/*
- + * The buffer should not be full.
- + */
- + if (ppp->flags & SC_DEBUG) {
- + if ((buf->count < 0) || (buf->count > 3000))
- + printk (KERN_DEBUG "ppp_stuff_char: %x %d\n",
- + (unsigned int) buf->count,
- + (unsigned int) chr);
- + }
- +/*
- + * Update the FCS and if the character needs to be escaped, do it.
- + */
- + buf->fcs = PPP_FCS (buf->fcs, chr);
- + if (in_xmap (ppp, chr)) {
- + chr ^= PPP_TRANS;
- + ins_char (buf, PPP_ESCAPE);
- + }
- +/*
- + * Add the character to the buffer.
- + */
- + ins_char (buf, chr);
- +}
- +
- +/*
- + * Procedure to encode the data with the proper escaping and send the
- + * data to the remote system.
- + */
- +
- +static void
- +ppp_dev_xmit_lower (struct ppp *ppp, struct ppp_buffer *buf,
- + __u8 *data, int count, int non_ip)
- +{
- + __u16 write_fcs;
- + int address, control;
- + int proto;
- +/*
- + * Insert the leading FLAG character
- + */
- + buf->count = 0;
- +
- + if (non_ip || flag_time == 0)
- + ins_char (buf, PPP_FLAG);
- + else {
- + if (jiffies - ppp->last_xmit > flag_time)
- + ins_char (buf, PPP_FLAG);
- + }
- + ppp->last_xmit = jiffies;
- + buf->fcs = PPP_INITFCS;
- +/*
- + * Emit the address/control information if needed
- + */
- + address = PPP_ADDRESS (data);
- + control = PPP_CONTROL (data);
- + proto = PPP_PROTOCOL (data);
- +
- + if (address != PPP_ALLSTATIONS ||
- + control != PPP_UI ||
- + (ppp->flags & SC_COMP_AC) == 0) {
- + ppp_stuff_char (ppp, buf, address);
- + ppp_stuff_char (ppp, buf, control);
- + }
- +/*
- + * Emit the protocol (compressed if possible)
- + */
- + if ((ppp->flags & SC_COMP_PROT) == 0 || (proto & 0xFF00))
- + ppp_stuff_char (ppp, buf, proto >> 8);
- +
- + ppp_stuff_char (ppp, buf, proto);
- +/*
- + * Insert the data
- + */
- + data += 4;
- + count -= 4;
- +
- + while (count-- > 0)
- + ppp_stuff_char (ppp, buf, *data++);
- +/*
- + * Add the trailing CRC and the final flag character
- + */
- + write_fcs = buf->fcs ^ 0xFFFF;
- + ppp_stuff_char (ppp, buf, write_fcs);
- + ppp_stuff_char (ppp, buf, write_fcs >> 8);
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG "ppp_dev_xmit_lower: fcs is %hx\n",
- + write_fcs);
- +/*
- + * Add the trailing flag character
- + */
- + ins_char (buf, PPP_FLAG);
- +/*
- + * Print the buffer
- + */
- + if (ppp->flags & SC_LOG_FLUSH)
- + ppp_print_buffer ("ppp flush", buf_base (buf),
- + buf->count);
- + else {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_dev_xmit: writing %d chars\n",
- + buf->count);
- + }
- +/*
- + * Send the block to the tty driver.
- + */
- + ppp->stats.ppp_obytes += buf->count;
- + ppp_kick_tty (ppp, buf);
- +}
- +
- +/*
- + * Send an frame to the remote with the proper bsd compression.
- + *
- + * Return 0 if frame was queued for transmission.
- + * 1 if frame must be re-queued for later driver support.
- + */
- +
- +static int
- +ppp_dev_xmit_frame (struct ppp *ppp, struct ppp_buffer *buf,
- + __u8 *data, int count)
- +{
- + int proto;
- + int address, control;
- + __u8 *new_data;
- + int new_count;
- +/*
- + * Print the buffer
- + */
- + if (ppp->flags & SC_LOG_OUTPKT)
- + ppp_print_buffer ("write frame", data, count);
- +/*
- + * Determine if the frame may be compressed. Attempt to compress the
- + * frame if possible.
- + */
- + proto = PPP_PROTOCOL (data);
- + address = PPP_ADDRESS (data);
- + control = PPP_CONTROL (data);
- +
- + if (((ppp->flags & SC_COMP_RUN) != 0) &&
- + (ppp->sc_xc_state != (void *) 0) &&
- + (address == PPP_ALLSTATIONS) &&
- + (control == PPP_UI) &&
- + (proto != PPP_LCP) &&
- + (proto != PPP_CCP)) {
- + new_data = kmalloc (count, GFP_ATOMIC);
- + if (new_data == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_dev_xmit_frame: no memory\n");
- + return 1;
- + }
- +
- + new_count = bsd_compress (ppp->sc_xc_state,
- + data,
- + new_data,
- + count,
- + count);
- +
- + if (new_count > 0) {
- + ++ppp->stats.ppp_opackets;
- + ppp->stats.ppp_ooctects += new_count;
- +
- + ppp_dev_xmit_lower (ppp, buf, new_data,
- + new_count, 0);
- + kfree (new_data);
- + return 0;
- + }
- +/*
- + * The frame could not be compressed.
- + */
- + kfree (new_data);
- + }
- +/*
- + * The frame may not be compressed. Update the statistics before the
- + * count field is destroyed. The frame will be transmitted.
- + */
- + ++ppp->stats.ppp_opackets;
- + ppp->stats.ppp_ooctects += count;
- +/*
- + * Go to the escape encoding
- + */
- + ppp_dev_xmit_lower (ppp, buf, data, count, !!(proto & 0xFF00));
- + return 0;
- +}
- +
- +/*
- + * Revise the tty frame for specific protocols.
- + */
- +
- +static int
- +send_revise_frame (register struct ppp *ppp, __u8 *data, int len)
- +{
- + __u8 *p;
- +
- + switch (PPP_PROTOCOL (data)) {
- +/*
- + * Update the LQR frame with the current MIB information. This saves having
- + * the daemon read old MIB data from the driver.
- + */
- + case PPP_LQR:
- + len = 48; /* total size of this frame */
- + p = (__u8 *) &data [40]; /* Point to last two items. */
- + p = store_long (p, ppp->stats.ppp_opackets + 1);
- + p = store_long (p, ppp->stats.ppp_ooctects + len);
- + break;
- +/*
- + * Outbound compression frames
- + */
- + case PPP_CCP:
- + ppp_proto_ccp (ppp,
- + data + PPP_HARD_HDR_LEN,
- + len - PPP_HARD_HDR_LEN,
- + 0);
- + break;
- +
- + default:
- + break;
- + }
- +
- + return len;
- +}
- +
- +/*
- + * write a frame with NR chars from BUF to TTY
- + * we have to put the FCS field on ourselves
- + */
- +
- +static int
- +ppp_tty_write (struct tty_struct *tty, struct file *file, const __u8 * data,
- + unsigned int count)
- +{
- + struct ppp *ppp = tty2ppp (tty);
- + __u8 *new_data;
- + int status;
- +/*
- + * Verify the pointers.
- + */
- + if (!ppp)
- + return -EIO;
- +
- + if (ppp->magic != PPP_MAGIC)
- + return -EIO;
- +
- + CHECK_PPP (-ENXIO);
- +/*
- + * Ensure that the caller does not wish to send too much.
- + */
- + if (count > PPP_MTU) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_WARNING
- + "ppp_tty_write: truncating user packet "
- + "from %u to mtu %d\n", count, PPP_MTU);
- + count = PPP_MTU;
- + }
- +/*
- + * Allocate a buffer for the data and fetch it from the user space.
- + */
- + new_data = kmalloc (count, GFP_KERNEL);
- + if (new_data == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_tty_write: no memory\n");
- + return 0;
- + }
- +/*
- + * lock this PPP unit so we will be the only writer;
- + * sleep if necessary
- + */
- + while (lock_buffer (ppp->tbuf) != 0) {
- + current->timeout = 0;
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG "ppp_tty_write: sleeping\n");
- + interruptible_sleep_on (&ppp->write_wait);
- +
- + ppp = tty2ppp (tty);
- + if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse) {
- + kfree (new_data);
- + return 0;
- + }
- +
- + if (current->signal & ~current->blocked) {
- + kfree (new_data);
- + return -EINTR;
- + }
- + }
- +/*
- + * Ensure that the caller's buffer is valid.
- + */
- + status = verify_area (VERIFY_READ, data, count);
- + if (status != 0) {
- + kfree (new_data);
- + ppp->tbuf->locked = 0;
- + return status;
- + }
- +
- + memcpy_fromfs (new_data, data, count);
- +/*
- + * Change the LQR frame
- + */
- + count = send_revise_frame (ppp, new_data, count);
- +/*
- + * Send the data
- + */
- + ppp_dev_xmit_frame (ppp, ppp->tbuf, new_data, count);
- + kfree (new_data);
- + return (int) count;
- +}
- +
- +/*
- + * Process the BSD compression IOCTL event for the tty device.
- + */
- +
- +static int
- +ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
- +{
- + struct compressor *cp;
- + struct ppp_option_data data;
- + int error;
- + int nb;
- + __u8 *ptr;
- + __u8 ccp_option[CCP_MAX_OPTION_LENGTH];
- +/*
- + * Fetch the compression parameters
- + */
- + error = verify_area (VERIFY_READ, odp, sizeof (data));
- + if (error == 0) {
- + memcpy_fromfs (&data, odp, sizeof (data));
- + nb = data.length;
- + ptr = data.ptr;
- + if ((__u32) nb >= (__u32)CCP_MAX_OPTION_LENGTH)
- + nb = CCP_MAX_OPTION_LENGTH;
- +
- + error = verify_area (VERIFY_READ, ptr, nb);
- + }
- +
- + if (error != 0)
- + return error;
- +
- + memcpy_fromfs (ccp_option, ptr, nb);
- +
- + if (ccp_option[1] < 2) /* preliminary check on the length byte */
- + return (-EINVAL);
- +
- + cp = find_compressor ((int) (unsigned int) (__u8) ccp_option[0]);
- + if (cp != (struct compressor *) 0) {
- + /*
- + * Found a handler for the protocol - try to allocate
- + * a compressor or decompressor.
- + */
- + error = 0;
- + if (data.transmit) {
- + if (ppp->sc_xc_state != NULL)
- + (*ppp->sc_xcomp->comp_free)(ppp->sc_xc_state);
- +
- + ppp->sc_xcomp = cp;
- + ppp->sc_xc_state = cp->comp_alloc(ccp_option, nb);
- +
- + if (ppp->sc_xc_state == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk("ppp%ld: comp_alloc failed\n",
- + ppp2dev (ppp)->base_addr);
- + error = -ENOBUFS;
- + }
- + ppp->flags &= ~SC_COMP_RUN;
- + } else {
- + if (ppp->sc_rc_state != NULL)
- + (*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state);
- + ppp->sc_rcomp = cp;
- + ppp->sc_rc_state = cp->decomp_alloc(ccp_option, nb);
- + if (ppp->sc_rc_state == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk("ppp%ld: decomp_alloc failed\n",
- + ppp2dev (ppp)->base_addr);
- + error = ENOBUFS;
- + }
- + ppp->flags &= ~SC_DECOMP_RUN;
- + }
- + return (error);
- + }
- +
- + if (ppp->flags & SC_DEBUG)
- + printk(KERN_DEBUG "ppp%ld: no compressor for [%x %x %x], %x\n",
- + ppp2dev (ppp)->base_addr, ccp_option[0], ccp_option[1],
- + ccp_option[2], nb);
- + return (-EINVAL); /* no handler found */
- +}
- +
- +/*
- + * Process the IOCTL event for the tty device.
- + */
- +
- +static int
- +ppp_tty_ioctl (struct tty_struct *tty, struct file * file,
- + unsigned int param2, unsigned long param3)
- +{
- + struct ppp *ppp = tty2ppp (tty);
- + register int temp_i = 0;
- + int error = 0;
- +/*
- + * Verify the status of the PPP device.
- + */
- + if (!ppp)
- + return -EBADF;
- +
- + if (ppp->magic != PPP_MAGIC)
- + return -EBADF;
- +
- + CHECK_PPP (-ENXIO);
- +/*
- + * The user must have an euid of root to do these requests.
- + */
- + if (!suser ())
- + return -EPERM;
- +/*
- + * Set the MRU value
- + */
- + switch (param2) {
- + case PPPIOCSMRU:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + temp_i = get_user ((int *) param3);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set mru to %x\n", temp_i);
- +
- + if (ppp->mru != temp_i)
- + ppp_changedmtu (ppp, ppp2dev (ppp)->mtu, temp_i);
- + }
- + break;
- +/*
- + * Fetch the flags
- + */
- + case PPPIOCGFLAGS:
- + error = verify_area (VERIFY_WRITE, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + temp_i = (ppp->flags & SC_MASK);
- +#ifndef CHECK_CHARACTERS /* Don't generate errors if we don't check chars. */
- + temp_i |= SC_RCV_B7_1 | SC_RCV_B7_0 |
- + SC_RCV_ODDP | SC_RCV_EVNP;
- +#endif
- + put_user (temp_i, (int *) param3);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_DEBUG
- + "ppp_tty_ioctl: get flags: addr %lx flags "
- + "%x\n", param3, temp_i);
- + }
- + break;
- +/*
- + * Set the flags for the various options
- + */
- + case PPPIOCSFLAGS:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + temp_i = get_user ((int *) param3) & SC_MASK;
- + temp_i |= (ppp->flags & ~SC_MASK);
- +
- + if ((ppp->flags & SC_CCP_OPEN) &&
- + (temp_i & SC_CCP_OPEN) == 0)
- + ppp_ccp_closed (ppp);
- +
- + if ((ppp->flags | temp_i) & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set flags to %x\n", temp_i);
- + ppp->flags = temp_i;
- + }
- + break;
- +/*
- + * Set the compression mode
- + */
- + case PPPIOCSCOMPRESS:
- + error = ppp_set_compression (ppp,
- + (struct ppp_option_data *) param3);
- + break;
- +/*
- + * Retrieve the transmit async map
- + */
- + case PPPIOCGASYNCMAP:
- + error = verify_area (VERIFY_WRITE, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + put_user (ppp->xmit_async_map[0], (int *) param3);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: get asyncmap: addr "
- + "%lx asyncmap %x\n",
- + param3,
- + ppp->xmit_async_map[0]);
- + }
- + break;
- +/*
- + * Set the transmit async map
- + */
- + case PPPIOCSASYNCMAP:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + ppp->xmit_async_map[0] = get_user ((int *) param3);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set xmit asyncmap %x\n",
- + ppp->xmit_async_map[0]);
- + }
- + break;
- +/*
- + * Set the receive async map
- + */
- + case PPPIOCSRASYNCMAP:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + ppp->recv_async_map = get_user ((int *) param3);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set rcv asyncmap %x\n",
- + ppp->recv_async_map);
- + }
- + break;
- +/*
- + * Obtain the unit number for this device.
- + */
- + case PPPIOCGUNIT:
- + error = verify_area (VERIFY_WRITE, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + put_user (ppp2dev (ppp)->base_addr, (int *) param3);
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: get unit: %ld",
- + ppp2dev (ppp)->base_addr);
- + }
- + break;
- +/*
- + * Set the debug level
- + */
- + case PPPIOCSDEBUG:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + temp_i = (get_user ((int *) param3) & 0x1F) << 16;
- + temp_i |= (ppp->flags & ~0x1F0000);
- +
- + if ((ppp->flags | temp_i) & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set flags to %x\n", temp_i);
- + ppp->flags = temp_i;
- + }
- + break;
- +/*
- + * Get the debug level
- + */
- + case PPPIOCGDEBUG:
- + error = verify_area (VERIFY_WRITE, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + temp_i = (ppp->flags >> 16) & 0x1F;
- + put_user (temp_i, (int *) param3);
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: get debug level %d\n",
- + temp_i);
- + }
- + break;
- +/*
- + * Get the times since the last send/receive frame operation
- + */
- + case PPPIOCGIDLE:
- + error = verify_area (VERIFY_WRITE, (void *) param3,
- + sizeof (struct ppp_idle));
- + if (error == 0) {
- + struct ppp_idle cur_ddinfo;
- + __u32 cur_jiffies = jiffies;
- +
- + /* change absolute times to relative times. */
- + cur_ddinfo.xmit_idle = (cur_jiffies - ppp->ddinfo.xmit_idle) / HZ;
- + cur_ddinfo.recv_idle = (cur_jiffies - ppp->ddinfo.recv_idle) / HZ;
- + memcpy_tofs ((void *) param3, &cur_ddinfo,
- + sizeof (cur_ddinfo));
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: read demand dial info\n");
- + }
- + break;
- +/*
- + * Retrieve the extended async map
- + */
- + case PPPIOCGXASYNCMAP:
- + error = verify_area (VERIFY_WRITE,
- + (void *) param3,
- + sizeof (ppp->xmit_async_map));
- + if (error == 0) {
- + memcpy_tofs ((void *) param3,
- + ppp->xmit_async_map,
- + sizeof (ppp->xmit_async_map));
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: get xasyncmap: addr %lx\n",
- + param3);
- + }
- + break;
- +/*
- + * Set the async extended map
- + */
- + case PPPIOCSXASYNCMAP:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (ppp->xmit_async_map));
- + if (error == 0) {
- + __u32 temp_tbl[8];
- +
- + memcpy_fromfs (temp_tbl, (void *) param3,
- + sizeof (ppp->xmit_async_map));
- + temp_tbl[1] = 0x00000000;
- + temp_tbl[2] &= ~0x40000000;
- + temp_tbl[3] |= 0x60000000;
- +
- + if ((temp_tbl[2] & temp_tbl[3]) != 0 ||
- + (temp_tbl[4] & temp_tbl[5]) != 0 ||
- + (temp_tbl[6] & temp_tbl[7]) != 0)
- + error = -EINVAL;
- + else {
- + memcpy (ppp->xmit_async_map, temp_tbl,
- + sizeof (ppp->xmit_async_map));
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set xasyncmap\n");
- + }
- + }
- + break;
- +/*
- + * Set the maximum VJ header compression slot number.
- + */
- + case PPPIOCSMAXCID:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (temp_i));
- + if (error == 0) {
- + temp_i = get_user ((int *) param3) + 1;
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp_tty_ioctl: set maxcid to %d\n",
- + temp_i);
- + if (ppp->slcomp != NULL)
- + slhc_free (ppp->slcomp);
- + ppp->slcomp = slhc_init (16, temp_i);
- +
- + if (ppp->slcomp == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp: no space for compression buffers!\n");
- + ppp_release (ppp);
- + error = -ENOMEM;
- + }
- + }
- + break;
- +
- + case PPPIOCXFERUNIT:
- + ppp_tty_close_local (tty, current->pid);
- + break;
- +
- + case PPPIOCGNPMODE:
- + case PPPIOCSNPMODE:
- + error = verify_area (VERIFY_READ, (void *) param3,
- + sizeof (struct npioctl));
- + if (error == 0) {
- + struct npioctl npi;
- + memcpy_fromfs (&npi,
- + (void *) param3,
- + sizeof (npi));
- +
- + switch (npi.protocol) {
- + case PPP_IP:
- + npi.protocol = NP_IP;
- + break;
- + default:
- + error = -EINVAL;
- + }
- +
- + if (error != 0)
- + break;
- +
- + if (param2 == PPPIOCGNPMODE) {
- + npi.mode = ppp->sc_npmode[npi.protocol];
- + error = verify_area (VERIFY_WRITE,
- + (void *) param3,
- + sizeof (npi));
- + if (error != 0)
- + break;
- +
- + memcpy_tofs ((void *) param3,
- + &npi,
- + sizeof (npi));
- + break;
- + }
- +
- + if (npi.mode != ppp->sc_npmode[npi.protocol]) {
- + ppp->sc_npmode[npi.protocol] = npi.mode;
- + if (npi.mode != NPMODE_QUEUE) {
- + /* ppp_requeue(ppp); maybe needed */
- + ppp_tty_wakeup (ppp2tty(ppp));
- + }
- + }
- + }
- + break;
- +/*
- + * Allow users to read, but not set, the serial port parameters
- + */
- + case TCGETS:
- + case TCGETA:
- + error = n_tty_ioctl (tty, file, param2, param3);
- + break;
- +
- + case FIONREAD:
- + error = verify_area (VERIFY_WRITE,
- + (void *) param3,
- + sizeof (int));
- + if (error == 0) {
- + int count = ppp->ubuf->tail - ppp->ubuf->head;
- + if (count < 0)
- + count += (ppp->ubuf->size + 1);
- +
- + put_user (count, (int *) param3);
- + }
- + break;
- +/*
- + * All other ioctl() events will come here.
- + */
- + default:
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp_tty_ioctl: invalid ioctl: %x, addr %lx\n",
- + param2,
- + param3);
- +
- + error = -ENOIOCTLCMD;
- + break;
- + }
- + return error;
- +}
- +
- +/*
- + * TTY callback.
- + *
- + * Process the select() statement for the PPP device.
- + */
- +
- +static int
- +ppp_tty_select (struct tty_struct *tty, struct inode *inode,
- + struct file *filp, int sel_type, select_table * wait)
- +{
- + struct ppp *ppp = tty2ppp (tty);
- + int result = 1;
- +/*
- + * Verify the status of the PPP device.
- + */
- + if (!ppp)
- + return -EBADF;
- +
- + if (ppp->magic != PPP_MAGIC)
- + return -EBADF;
- +
- + CHECK_PPP (0);
- +/*
- + * Branch on the type of select mode. A read request must lock the user
- + * buffer area.
- + */
- + switch (sel_type) {
- + case SEL_IN:
- + if (set_bit (0, &ppp->ubuf->locked) == 0) {
- + /* Test for the presence of data in the queue */
- + if (ppp->ubuf->head != ppp->ubuf->tail) {
- + clear_bit (0, &ppp->ubuf->locked);
- + break;
- + }
- + clear_bit (0, &ppp->ubuf->locked);
- + } /* fall through */
- + /*
- + * Exceptions or read errors.
- + */
- + case SEL_EX:
- + /* Is this a pty link and the remote disconnected? */
- + if (tty->flags & (1 << TTY_OTHER_CLOSED))
- + break;
- +
- + /* Is this a local link and the modem disconnected? */
- + if (tty_hung_up_p (filp))
- + break;
- +
- + select_wait (&ppp->read_wait, wait);
- + result = 0;
- + break;
- +/*
- + * Write mode. A write is allowed if there is no current transmission.
- + */
- + case SEL_OUT:
- + if (ppp->tbuf->locked != 0) {
- + select_wait (&ppp->write_wait, wait);
- + result = 0;
- + }
- + break;
- + }
- + return result;
- +}
- +
- +/*************************************************************
- + * NETWORK OUTPUT
- + * This routine accepts requests from the network layer
- + * and attempts to deliver the packets.
- + * It also includes various routines we are compelled to
- + * have to make the network layer work (arp, etc...).
- + *************************************************************/
- +
- +/*
- + * Callback from the network layer when the device goes up.
- + */
- +
- +static int
- +ppp_dev_open (struct device *dev)
- +{
- + struct ppp *ppp = dev2ppp (dev);
- +
- + /* reset POINTOPOINT every time, since dev_close zaps it! */
- + dev->flags |= IFF_POINTOPOINT;
- +
- + if (ppp2tty (ppp) == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp: %s not connected to a TTY! can't go open!\n",
- + dev->name);
- + return -ENXIO;
- + }
- +
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp: channel %s going up for IP packets!\n",
- + dev->name);
- +
- + CHECK_PPP (-ENXIO);
- + return 0;
- +}
- +
- +/*
- + * Callback from the network layer when the ppp device goes down.
- + */
- +
- +static int
- +ppp_dev_close (struct device *dev)
- +{
- + struct ppp *ppp = dev2ppp (dev);
- +
- + if (ppp2tty (ppp) == NULL) {
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_ERR
- + "ppp: %s not connected to a TTY! can't go down!\n",
- + dev->name);
- + return -ENXIO;
- + }
- +/*
- + * We don't do anything about the device going down. It is not important
- + * for us.
- + */
- + if (ppp->flags & SC_DEBUG)
- + printk (KERN_INFO
- + "ppp: channel %s going down for IP packets!\n",
- + dev->name);
- + CHECK_PPP (-ENXIO);
- + return 0;
- +}
- +
- +/*
- + * IOCTL operation to read the version of the driver.
- + */
- +
- +static int
- +ppp_dev_ioctl_version (struct ppp *ppp, struct ifreq *ifr)
- +{
- + int error;
- + int len;
- + char *result;
- +/*
- + * Must have write access to the buffer.
- + */
- + result = (char *) ifr->ifr_ifru.ifru_data;
- + len = strlen (szVersion) + 1;
- + error = verify_area (VERIFY_WRITE, result, len);
- +/*
- + * Move the version data
- + */
- + if (error == 0)
- + memcpy_tofs (result, szVersion, len);
- +
- + return error;
- +}
- +
- +/*
- + * IOCTL to read the statistics for the pppstats program.
- + */
- +
- +static int
- +ppp_dev_ioctl_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev)
- +{
- + struct ppp_stats *result, temp;
- + int error;
- +/*
- + * Must have write access to the buffer.
- + */
- + result = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
- + error = verify_area (VERIFY_WRITE,
- + result,
- + sizeof (temp));
- +/*
- + * Supply the information for the caller. First move the version data
- + * then move the ppp stats; and finally the vj stats.
- + */
- + memset (&temp, 0, sizeof(temp));
- + if (error == 0 && dev->flags & IFF_UP) {
- + memcpy (&temp.p, &ppp->stats, sizeof (struct pppstat));
- + if (ppp->slcomp != NULL) {
- + temp.vj.vjs_packets = ppp->slcomp->sls_o_compressed+
- + ppp->slcomp->sls_o_uncompressed;
- + temp.vj.vjs_compressed = ppp->slcomp->sls_o_compressed;
- + temp.vj.vjs_searches = ppp->slcomp->sls_o_searches;
- + temp.vj.vjs_misses = ppp->slcomp->sls_o_misses;
- + temp.vj.vjs_errorin = ppp->slcomp->sls_i_error;
- + temp.vj.vjs_tossed = ppp->slcomp->sls_i_tossed;
- + temp.vj.vjs_uncompressedin = ppp->slcomp->sls_i_uncompressed;
- + temp.vj.vjs_compressedin = ppp->slcomp->sls_i_compressed;
- + }
- + }
- +
- + if (error == 0)
- + memcpy_tofs (result, &temp, sizeof (temp));
- + return error;
- +}
- +
- +/*
- + * IOCTL to read the compression statistics for the pppstats program.
- + */
- +
- +static int
- +ppp_dev_ioctl_comp_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev)
- +{
- + struct ppp_comp_stats *result, temp;
- + int error;
- +/*
- + * Must have write access to the buffer.
- + */
- + result = (struct ppp_comp_stats *) ifr->ifr_ifru.ifru_data;
- + error = verify_area (VERIFY_WRITE,
- + result,
- + sizeof (temp));
- +/*
- + * Supply the information for the caller.
- + */
- + memset (&temp, 0, sizeof(temp));
- + if (error == 0 && dev->flags & IFF_UP) {
- + if (ppp->sc_xc_state != NULL)
- + (*ppp->sc_xcomp->comp_stat) (ppp->sc_xc_state,
- + &temp.c);
- +
- + if (ppp->sc_rc_state != NULL)
- + (*ppp->sc_rcomp->decomp_stat) (ppp->sc_rc_state,
- + &temp.d);
- + }
- +/*
- + * Move the data to the caller's buffer
- + */
- + if (error == 0)
- + memcpy_tofs (result, &temp, sizeof (temp));
- + return error;
- +}
- +
- +/*
- + * Callback from the network layer to process the sockioctl functions.
- + */
- +
- +static int
- +ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd)
- +{
- + struct ppp *ppp = dev2ppp (dev);
- + int error;
- +/*
- + * Process the requests
- + */
- + switch (cmd) {
- + case SIOCGPPPSTATS:
- + error = ppp_dev_ioctl_stats (ppp, ifr, dev);
- + break;
- +
- + case SIOCGPPPCSTATS:
- + error = ppp_dev_ioctl_comp_stats (ppp, ifr, dev);
- + break;
- +
- + case SIOCGPPPVER:
- + error = ppp_dev_ioctl_version (ppp, ifr);
- + break;
- +
- + default:
- + error = -EINVAL;
- + break;
- + }
- + return error;
- +}
- +
- +/*
- + * Send an IP frame to the remote with vj header compression.
- + *
- + * Return 0 if frame was queued for transmission.
- + * 1 if frame must be re-queued for later driver support.
- + */
- +
- +static int
- +ppp_dev_xmit_ip (