diff -u --recursive --new-file v2.1.108/linux/CREDITS linux/CREDITS
--- v2.1.108/linux/CREDITS	Tue Jun 23 10:01:18 1998
+++ linux/CREDITS	Thu Jul 16 12:35:26 1998
@@ -1,9 +1,9 @@
 	This is at least a partial credits-file of people that have
-	contributed to the linux project.  It is sorted by name, and
-	formatted in a format that allows for easy grepping and
-	beautification by scripts.  The fields are: name (N), email (E),
-	web-address (W), PGP key ID and fingerprint (P), description (D)
-	and snail-mail address (S). 
+	contributed to the Linux project.  It is sorted by name and
+	formatted to allow easy grepping and beautification by
+	scripts.  The fields are: name (N), email (E), web-address
+	(W), PGP key ID and fingerprint (P), description (D), and
+	snail-mail address (S).
 	Thanks,
 
 			Linus
@@ -46,8 +46,8 @@
 P: 1024/FC4CFFED 78 3C 6A 19 FA 5D 92 5A  FB AC 7B A5 A5 E1 FF 8E
 D: Maintainer of ide-cd and Uniform CD-ROM driver, 
 D: ATAPI CD-Changer support, Major 2.1.x CD-ROM update.
-S: 4538 South Carnegie Tech St.
-S: West Valley City, UT 84120
+S: 4538 South Carnegie Tech Street
+S: West Valley City, Utah 84120
 S: USA
 
 N: H. Peter Anvin
@@ -56,7 +56,7 @@
 P: 2047/2A960705 BA 03 D3 2C 14 A8 A8 BD  1E DF FE 69 EE 35 BD 74
 D: Author of the SYSLINUX boot loader, maintainer of the linux.* news
 D: hierarchy and the Linux Device List; various kernel hacks
-S: 4390 Albany Dr. #46
+S: 4390 Albany Drive #46
 S: San Jose, California 95129
 S: USA
 
@@ -85,7 +85,8 @@
 D: Adaptec 274x driver
 S: Department of Computer Science
 S: University of Calgary
-S: Calgary, Alberta, Canada
+S: Calgary, Alberta
+S: Canada
 
 N: Ralf Baechle
 E: ralf@gnu.ai.mit.edu
@@ -124,6 +125,16 @@
 S: Provo, Utah 84606
 S: USA
 
+M: Krzysztof G. Baranowski
+E: kgb@manjak.knm.org.pl
+P: 1024/FA6F16D1 96 D1 1A CF 5F CA 69 EC  F9 4F 36 1F 6D 60 7B DA
+D: Maintainer of the System V file system.
+D: SystemV fs update for 2.1.x dcache.
+D: Various bugfixes.
+S: ul. Koscielna 12a
+S: 62-300 Wrzesnia
+S: Poland
+
 N: Peter Bauer
 E: 100136.3530@compuserve.com
 D: Driver for depca-ethernet-board
@@ -143,7 +154,7 @@
 N: Donald Becker
 E: becker@cesdis.gsfc.nasa.gov
 D: General low-level networking hacker
-D: Most of the ethercard drivers
+D: Most of the Ethernet drivers
 D: Original author of the NFS server
 S: USRA Center of Excellence in Space Data and Information Sciences
 S: Code 930.5, Goddard Space Flight Center
@@ -185,7 +196,8 @@
 D: Some Linux/ARM stuff
 D: Co-architect of the parallel port sharing system
 S: Nexus Electronics Ltd
-S: 10 St Barnabas Road, Cambridge, UK. CB1 2BY
+S: 10 St Barnabas Road, Cambridge CB1 2BY
+S: United Kingdom
 
 N: Thomas Bogendoerfer
 E: tsbogend@alpha.franken.de
@@ -217,8 +229,9 @@
 W: http://coda.cs.cmu.edu/~braam
 D: Coda Filesystem
 S: Dept of Computer Science
-S: 5000 Forbes Ave
-S: Pittsburgh PA 15213
+S: 5000 Forbes Avenue
+S: Pittsburgh, Pennsylvania 15213
+S: USA
 
 N: Andries Brouwer
 E: aeb@cwi.nl
@@ -273,7 +286,8 @@
 D: IGMP(Internet Group Management Protocol) version 2
 S: 3F, 65 Tajen street
 S: Tamsui town, Taipei county,
-S: Taiwan 251, Republic of China
+S: Taiwan 251
+S: Republic of China
 
 N: Raymond Chen
 E: raymondc@microsoft.com
@@ -412,7 +426,8 @@
 N: Tom Dyas
 E: tdyas@eden.rutgers.edu
 D: minor hacks and some sparc port stuff
-S: New Jersey, USA
+S: New Jersey
+S: USA
 
 N: Drew Eckhardt
 E: drew@PoohSticks.ORG
@@ -425,7 +440,7 @@
 
 N: Heiko Eissfeldt
 E: heiko@colossus.escape.de heiko@unifix.de
-D: verify_area stuff, generic scsi fixes
+D: verify_area stuff, generic SCSI fixes
 D: SCSI Programming HOWTO
 D: POSIX.1 compliance testing
 S: Unifix Software GmbH
@@ -458,7 +473,7 @@
 P: 1024/6E657BB5 AF 22 90 33 78 76 04 8B  AF F9 97 1E B5 E2 65 30
 D: Audio Excel DSP 16 init driver author
 D: libmodem author
-D: Yet Another Micro Monitor port and current mantainer
+D: Yet Another Micro Monitor port and current maintainer
 D: First ELF-HOWTO author
 D: random kernel hacker
 S: Via Paolo VI n.29
@@ -482,7 +497,7 @@
 
 N: Jürgen Fischer
 E: fischer@et-inf.fho-emden.de (=?iso-8859-1?q?J=FCrgen?= Fischer)
-D: Author of Adaptec AHA-152x scsi driver
+D: Author of Adaptec AHA-152x SCSI driver
 S: Schulstraße 18
 S: 26506 Norden
 S: Germany
@@ -493,7 +508,7 @@
 D: General mm minor tidyups
 S: 67 Surrey St.
 S: Darlinghurst, Sydney
-S: NSW 2010
+S: New South Wales 2010
 S: Australia
 
 N: Ralf Flaxa
@@ -526,7 +541,7 @@
 D: Dynamic PPP devices
 D: Sundry modularizations (PPP, IPX, ...) and fixes
 S: Caldera, Inc.
-S: 240 West Center St.
+S: 240 West Center Street
 S: Orem, Utah 84059-1920
 S: USA
 
@@ -571,7 +586,7 @@
 D: /proc/mtrr support to manipulate MTRRs on Pentium Pro's
 S: CSIRO Australia Telescope National Facility
 S: P.O. Box 76, Epping
-S: N.S.W., 2121
+S: New South Wales, 2121
 S: Australia
 
 N: Dmitry S. Gorodchanin
@@ -586,7 +601,7 @@
 W: http://rsphy1.anu.edu.au/~gpg109
 D: Real Time Clock driver author.
 D: 8390 net driver hacker (ne2000, wd8013, smc-ultra, 3c503, etc.) 
-D: Ethernet-HowTo and BootPrompt-HowTo author.
+D: Ethernet-HOWTO and BootPrompt-HOWTO author.
 D: Added many new CONFIG options (modules, ramdisk, generic-serial, etc.)
 D: Implemented 1st "official" kernel thread (moved user bdflush to kflushd)
 D: Various other random hacks, patches and utilities.
@@ -601,7 +616,7 @@
 N: Michael A. Griffith
 E: grif@cs.ucr.edu
 W: http://www.cs.ucr.edu/~grif
-D: Loopback speedup, qlogic scsi hacking, VT_LOCKSWITCH
+D: Loopback speedup, qlogic SCSI hacking, VT_LOCKSWITCH
 S: Department of Computer Science
 S: University of California, Riverside
 S: Riverside, California 92521-0304
@@ -613,7 +628,7 @@
 D: original author of ppa driver for parallel port ZIP drive
 D: original architect of the parallel-port sharing scheme 
 D: PARIDE subsystem: drivers for parallel port IDE & ATAPI devices
-S: 44 St. Joseph St., Suite 506
+S: 44 St. Joseph Street, Suite 506
 S: Toronto, Ontario, M4Y 2W4
 S: Canada
 
@@ -694,7 +709,7 @@
 
 N: Michael Hipp
 E: mhipp@student.uni-tuebingen.de
-D: drivers for the racal ni5210 & ni6510 ethernet-boards
+D: drivers for the racal ni5210 & ni6510 Ethernet-boards
 S: Talstr. 1
 S: D - 72072 Tuebingen
 S: Germany
@@ -716,7 +731,7 @@
 E: khollis@bitgate.com
 W: http://www.nurk.org/
 D: Berkshire PC Watchdog Driver
-S: PO Box 15
+S: Post Office Box 15
 S: Grants Pass, Oregon 97526
 S: USA
 
@@ -739,7 +754,7 @@
 D: Kernel development
 D: Minor kernel modifications to support Wabi and Wine
 S: Caldera, Inc.
-S: 240 West Center St.
+S: 240 West Center Street
 S: Orem, Utah 84059-1920
 S: USA
 
@@ -854,7 +869,7 @@
 
 N: Russell King
 E: rmk@arm.uk.linux.org
-D: Linux/arm integrater, maintainer & hacker
+D: Linux/arm integrator, maintainer & hacker
 S: Burgh Heath, Tadworth, Surrey.
 S: England
 
@@ -870,8 +885,8 @@
 W: http://www.kluft.com/~ikluft/
 D: NET-1 beta testing & minor patches, original Smail binary packages for
 D: Slackware and Debian, vote-taker for 2nd comp.os.linux reorganization
-S: PO Box 611311
-S: San Jose, CA 95161-1311
+S: Post Office Box 611311
+S: San Jose, California 95161-1311
 S: USA
 
 N: Alain L. Knaff
@@ -991,7 +1006,7 @@
 N: Hans Lermen
 E: lermen@elserv.ffm.fgan.de
 D: Author of the LOADLIN Linux loader, hacking on boot stuff
-D: Co-ordinator of DOSEMU releases
+D: Coordinator of DOSEMU releases
 S: Am Muehlenweg 38
 S: D53424 Remagen
 S: Germany
@@ -1000,12 +1015,13 @@
 E: achim@vortex.de
 D: GDT SCSI Disk Array Controller driver
 S: ICP vortex Computersysteme GmbH
-S: Flein, Germany
+S: Flein
+S: Germany
 
 N: Phil Lewis
 E: beans@bucket.ualr.edu
 D: Promised to send money if I would put his name in the source tree.
-S: PO Box 371
+S: Post Office Box 371
 S: North Little Rock, Arkansas 72115
 S: USA
 
@@ -1041,7 +1057,7 @@
 N: Warner Losh
 E: imp@village.org
 D: Linux/MIPS Deskstation support, Provided OI/OB for Linux
-S: 8786 Niwot Rd
+S: 8786 Niwot Road
 S: Niwot, Colorado 80503
 S: USA
 
@@ -1099,7 +1115,7 @@
 N: James B. MacLean
 E: macleajb@ednet.ns.ca
 W: http://www.ednet.ns.ca/~macleajb/dosemu.html
-D: Former Co-ordinator of DOSEMU releases
+D: Former Coordinator of DOSEMU releases
 D: Program in DOSEMU
 S: PO BOX 220, HFX. CENTRAL
 S: Halifax, Nova Scotia
@@ -1138,7 +1154,7 @@
 W: http://www.invlogic.com/~mmclagan
 D: DLCI/FRAD drivers for Sangoma SDLAs
 S: Innovative Logic Corp
-S: P.O. Box 1068
+S: Post Office Box 1068
 S: Laurel, Maryland 20732
 S: USA
 
@@ -1202,7 +1218,7 @@
 D: Linux Emacs elf/qmagic support + other libc/gcc things
 D: Yee bore de yee bore! ;-)
 S: 111 Alta Tierra Court
-S: Los Gatos, CA 95032
+S: Los Gatos, California 95032
 S: USA
 
 N: Rick Miller
@@ -1242,7 +1258,7 @@
 E: David.Mosberger@acm.org
 D: Linux/Alpha
 S: 35706 Runckel Lane
-S: Fremont, CA 94536
+S: Fremont, California 94536
 S: USA
 
 N: Ian A. Murdock
@@ -1285,8 +1301,8 @@
 P: 1024/83942741 FF 68 EE 27 A0 5A AA C3  F5 DC 05 62 BD 5B 20 2F
 D: Author of cs89x0, maintainer of kernel changelog through 1.3.3
 D: Wrote many packet drivers, from which some Ethernet drivers are derived.
-S: 521 Pleasant Valley Rd.
-S: Potsdam, NY 13676
+S: 521 Pleasant Valley Road
+S: Potsdam, New York 13676
 S: USA
 
 N: Michael Neuffer
@@ -1402,7 +1418,7 @@
 W: http://www.pathname.com/~quinlan/
 D: FSSTND coordinator; FHS editor
 D: random Linux documentation, patches, and hacks
-S: 4390 Albany Dr. #41A
+S: 4390 Albany Drive #41A
 S: San Jose, California 95129
 S: USA
 
@@ -1501,7 +1517,7 @@
 N: Thomas Sailer
 E: sailer@ife.ee.ethz.ch
 E: HB9JNX@HB9W.CHE.EU (packet radio)
-D: hfmodem, Baycom and Soundcard radio modem driver
+D: hfmodem, Baycom and sound card radio modem driver
 S: Weinbergstrasse 76
 S: 8408 Winterthur
 S: Switzerland
@@ -1541,12 +1557,13 @@
 D: CD-List, Books-List, Ex-FAQ
 D: Linux-Support, -Mailbox, -Stammtisch
 D: several improvements to system programs
-S: Oldenburg, Germany
+S: Oldenburg
+S: Germany
 
 N: Darren Senn
 E: sinster@darkwater.com
 D: Whatever I notice needs doing (so far: itimers, /proc)
-S: POB 64132
+S: Post Office Box 64132
 S: Sunnyvale, California 94088-4132
 S: USA
 
@@ -1589,11 +1606,11 @@
 N: Craig Small
 E: csmall@triode.apana.org.au
 E: vk2xlz@gonzo.vk2xlz.ampr.org (packet radio)
+D: Gracilis PackeTwin device driver
+D: RSPF daemon 
 S: 10 Stockalls Place
 S: Minto, NSW, 2566
 S: Australia
-D: Gracilis PackeTwin device driver
-D: RSPF daemon 
 
 N: Chris Smith
 E: csmith@convex.com
@@ -1681,14 +1698,14 @@
 D: (bogomips, scope, eject, statserial)
 S: 1 Laurie Court
 S: Kanata, Ontario
-S: CANADA K2L 1S2
+S: Canada K2L 1S2
 
 N: Andrew Tridgell
 E: Andrew.Tridgell@anu.edu.au
 D: dosemu, networking, samba
 S: 3 Ballow Crescent
-S: MacGregor A.C.T
-S: 2615 Australia
+S: MacGregor A.C.T 2615
+S: Australia
 
 N: Winfried Trümper
 E: winni@xpilot.org
@@ -1720,7 +1737,8 @@
 D: IGMP(Internet Group Management Protocol) version 2
 S: 2F 14 ALY 31 LN 166 SEC 1 SHIH-PEI RD
 S: Taipei 
-S: Taiwan 112, Republic of China
+S: Taiwan 112
+S: Republic of China
 S: 24335 Delta Drive
 S: Diamond Bar, California 91765
 S: USA
@@ -1799,7 +1817,7 @@
 S: USA
 
 N: Dirk Verworner
-D: Co-author of german book ``Linux-Kernel-Programmierung''
+D: Co-author of German book ``Linux-Kernel-Programmierung''
 D: Co-founder of Berlin Linux User Group
 
 N: Patrick Volkerding
@@ -1903,7 +1921,7 @@
 N: Jonathan Woithe
 E: jwoithe@physics.adelaide.edu.au
 W: http://www.physics.adelaide.edu.au/~jwoithe
-D: ALS-007 soundcard extensions to Sound Blaster driver
+D: ALS-007 sound card extensions to Sound Blaster driver
 S: 4/36 Trevelyan St
 S: Wayville SA 5034
 S: Australia
diff -u --recursive --new-file v2.1.108/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.108/linux/Documentation/Configure.help	Wed Jul  1 19:38:51 1998
+++ linux/Documentation/Configure.help	Wed Jul 15 15:14:35 1998
@@ -3971,26 +3971,17 @@
   module, say M here and read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt.
 
-Radio support
-CONFIG_MISC_RADIO
-  If you have a radio card (which enables your computer to receive
-  regular radio broadcasts), then you will want to say Y here and
-  make a character device file (usually /dev/radio) with major number
-  10 and minor 152 using mknod ("man mknod").  And then, don't forget
-  to pick up some useful tools to use said device (you _might_ find
-  something at ftp://ftp.lmh.ox.ac.uk/users/weejock/linux/, but I haven't
-  written anything too useful yet...)
-
 AIMSlab RadioTrack (aka RadioReveal) support
 CONFIG_RADIO_RTRACK
   Choose Y here if you have one of these FM radio cards, and then fill
   in the port address below.
-  
-  In order to control your radio card under the X window system, you
-  may use the program X-Tuner, available on the WWW at
-  http://gatekeeper.dec.com/pub/X11/R6-contrib/applications/; to
-  browse the WWW, you need to have access to a machine on the Internet
-  that has a program like lynx or netscape.
+
+  In order to control your radio card, you will need to use programs
+  that are compatible with the Video for Linux API.  Information on 
+  this API and pointers to "v4l" programs may be found on the WWW at
+  http://roadrunner.swansea.uk.linux.org/v4l.shtml; to browse the WWW,
+  you need to have access to a machine on the Internet that has a 
+  program like lynx or netscape.
 
   If you want to compile this driver as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
@@ -4633,18 +4624,6 @@
   it as a module, say M here and read Documentation/modules.txt as
   well as Documentation/networking/net-modules.txt.
 
-PCI NE2000 support
-CONFIG_NE2K_PCI
-  If you have a network (Ethernet) card of this type, say Y and read
-  the Ethernet-HOWTO, available via FTP (user: anonymous) in
-  ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO.
-
-  This driver is also available as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want).
-  The module will be called ne2k-pci.o. If you want to compile it as a
-  module, say M here and read Documentation/modules.txt as well as
-  Documentation/networking/net-modules.txt.
-
 Racal-Interlan (Micom) NI cards
 CONFIG_NET_VENDOR_RACAL
   If you have a network (Ethernet) card belonging to this class, such
@@ -5259,6 +5238,20 @@
   module, say M here and read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt.
 
+PCI NE2000 Support
+CONFIG_NE2K_PCI
+  This driver is for NE2000 compatible PCI cards.  It will not work
+  with ISA NE2000 cards.  If you have a network (Ethernet) card of
+  this type, say Y and read the Ethernet-HOWTO, available via FTP
+  (user: anonymous) in ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called ne2k-pci.o. If you want to compile it as a
+  module, say M here and read Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt.
+
+
 TI ThunderLAN support (EXPERIMENTAL)
 CONFIG_TLAN
   If you have a TLAN based network card which is supported by this
@@ -5386,12 +5379,12 @@
 
 SysKonnect adapter support
 CONFIG_SKTR
-  This is support for all SysKonnect Token Ring cards. Specificly
+  This is support for all SysKonnect Token Ring cards, specifically
   SysKonnect TR4/16(+) ISA (SK-4190), SysKonnect TR4/16(+) PCI
   (SK-4590), SysKonnect TR4/16 PCI (SK-4591) adapters.
 
   If you have such an adapter and would like to use it, say Y or M
-  and read the Token-Ring mini-HOWTO, available via ftp (user:anonymous)
+  and read the Token-Ring mini-HOWTO, available via FTP (user:  anonymous)
   from ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO.
 
   Also read the sktr.txt in linux/Documentation/networking or check
@@ -8096,13 +8089,15 @@
 CONFIG_MSNDCLAS_INIT_FILE
   The MultiSound cards have two firmware files which are required for
   operation, and are not currently included.  These files can be
-  obtained from Turtle Beach.
+  obtained from Turtle Beach.  See Documentation/sound/MultiSound for
+  information on how to obtain this.
 
 Full pathname of MSNDPERM.BIN firmware file
 CONFIG_MSNDCLAS_PERM_FILE
   The MultiSound cards have two firmware files which are required for
   operation, and are not currently included.  These files can be
-  obtained from Turtle Beach.
+  obtained from Turtle Beach.  See Documentation/sound/MultiSound for
+  information on how to obtain this.
 
 Support for Turtle Beach MultiSound Pinnacle, Fiji
 CONFIG_SOUND_MSNDPIN
@@ -8114,13 +8109,15 @@
 CONFIG_MSNDPIN_INIT_FILE
   The MultiSound cards have two firmware files which are required for
   operation, and are not currently included.  These files can be
-  obtained from Turtle Beach.
+  obtained from Turtle Beach.  See Documentation/sound/MultiSound for
+  information on how to obtain this.
 
 Full pathname of PNDSPERM.BIN firmware file
 CONFIG_MSNDPIN_PERM_FILE
   The MultiSound cards have two firmware files which are required for
   operation, and are not currently included.  These files can be
-  obtained from Turtle Beach.
+  obtained from Turtle Beach.  See Documentation/sound/MultiSound for
+  information on how to obtain this.
 
 /dev/dsp and /dev/audio support
 CONFIG_SOUND_AUDIO
@@ -8201,6 +8198,36 @@
   Answer Y if you want your audio card to emulate Sound Blaster Pro.
   You should then say Y to "SoundBlaster (SB, SBPro, SB16, clones)
   support" and N to "Audio Excel DSP 16 (MSS emulation)".
+
+Ensoniq ES1370 based PCI soundcards
+CONFIG_SOUND_ES1370
+  Say Y or M if you have a PCI soundcard utilizing the Ensoniq
+  ES1370 chipset, such as Ensoniq's AudioPCI (non-97). To find
+  out if your soundcard uses an ES1370 without removing your
+  computer's cover, use lspci -n and look for the PCI ID 
+  1274:5000. Since Ensoniq was bought by Creative Labs,
+  SoundBlaster 64/PCI models are either ES1370 or ES1371 based.
+  This driver differs slightly from OSS/Free, so PLEASE READ
+  Documentation/sound/es1370.
+
+Ensoniq ES1371 based PCI soundcards
+CONFIG_SOUND_ES1371
+  Say Y or M if you have a PCI soundcard utilizing the Ensoniq
+  ES1371 chipset, such as Ensoniq's AudioPCI97. To find out if
+  your soundcard uses an ES1371 without removing your computer's
+  cover, use lspci -n and look for the PCI ID 1274:1371. Since
+  Ensoniq was bought by Creative Labs, SoundBlaster 64/PCI
+  models are either ES1370 or ES1371 based. This driver differs
+  slightly from OSS/Free, so PLEASE READ Documentation/sound/es1371.
+
+S3 SonicVibes based PCI soundcards
+CONFIG_SOUND_SONICVIBES
+  Say Y or M if you have a PCI soundcard utilizing the S3
+  SonicVibes chipset. To find out if your soundcard uses a
+  SonicVibes chip without removing your computer's cover, use
+  lspci -n and look for the PCI ID 5333:CA00. This driver 
+  differs slightly from OSS/Free, so PLEASE READ
+  Documentation/sound/sonicvibes.
 
 Kernel profiling support
 CONFIG_PROFILE
diff -u --recursive --new-file v2.1.108/linux/Documentation/fb/framebuffer.txt linux/Documentation/fb/framebuffer.txt
--- v2.1.108/linux/Documentation/fb/framebuffer.txt	Wed Jun 24 22:54:01 1998
+++ linux/Documentation/fb/framebuffer.txt	Fri Jul 10 15:18:29 1998
@@ -1,4 +1,3 @@
-
 			The Frame Buffer Device
 			-----------------------
 
@@ -158,10 +157,9 @@
 ---------------------
 
 A monitor draws an image on the screen by using an electron beam (3 electron
-beams for most color models, 1 electron beam for Trinitron color monitors and
-monochrone monitors). The front of the screen is covered by a pattern of
-colored phospors (pixels). If a phospor is hit by an electron, it emits a
-photon and thus becomes visible.
+beams for color models, 1 electron beam for monochrome monitors). The front of
+the screen is covered by a pattern of colored phosphors (pixels). If a phosphor
+is hit by an electron, it emits a photon and thus becomes visible.
 
 The electron beam draws horizontal lines (scanlines) from left to right, and
 from the top to the bottom of the screen. By modifying the intensity of the
@@ -275,7 +273,7 @@
 
 1) Pixelclock:
    xfree: in MHz
-   fb: In Picoseconds (ps)
+   fb: in picoseconds (ps)
 
    pixclock = 1000000 / DCF
 
diff -u --recursive --new-file v2.1.108/linux/Documentation/fb/vesafb.txt linux/Documentation/fb/vesafb.txt
--- v2.1.108/linux/Documentation/fb/vesafb.txt	Wed Jun 24 22:54:01 1998
+++ linux/Documentation/fb/vesafb.txt	Fri Jul 10 15:18:29 1998
@@ -18,7 +18,7 @@
  * It provides a nice large console (128 cols + 48 lines with 1024x768)
    without using tiny, unreadable fonts.
  * You can run XF68_FBDev on top of /dev/fb0 (=> non-accelerated X11
-   support for every VBE 2.0 compilant graphics board).
+   support for every VBE 2.0 compliant graphics board).
  * Most important: boot logo :-)
 
 Disadvantages:
diff -u --recursive --new-file v2.1.108/linux/Documentation/filesystems/ncpfs.txt linux/Documentation/filesystems/ncpfs.txt
--- v2.1.108/linux/Documentation/filesystems/ncpfs.txt	Tue Apr  2 02:33:13 1996
+++ linux/Documentation/filesystems/ncpfs.txt	Wed Jul  8 10:33:42 1998
@@ -1,12 +1,12 @@
-ncpfs is a filesystem which understands the NCP protocol, designed by the
-Novell Corporation for their NetWare(tm) product. NCP is functionally
-similar to the NFS used in the tcp/ip community.
-To mount a Netware-Filesystem, you need a special mount program, which
-can be found in ncpfs package. Homesite for ncpfs is
+The ncpfs filesystem understands the NCP protocol, designed by the
+Novell Corporation for their NetWare(tm) product.  NCP is functionally
+similar to the NFS used in the TCP/IP community.
+To mount a NetWare filesystem, you need a special mount program, which
+can be found in the ncpfs package.  The home site for ncpfs is
 ftp.gwdg.de/pub/linux/misc/ncpfs, but sunsite and its many mirrors
 will have it as well.
 
 Related products are linware and mars_nwe, which will give Linux partial
-NetWare Server functionality.
-Linware's home site is: klokan.sh.cvut.cz/pub/linux/linware,
-Mars_nwe can be found on ftp.gwdg.de/pub/linux/misc/ncpfs.
+NetWare server functionality.  Linware's home site is
+klokan.sh.cvut.cz/pub/linux/linware; mars_nwe can be found on
+ftp.gwdg.de/pub/linux/misc/ncpfs.
diff -u --recursive --new-file v2.1.108/linux/Documentation/sound/ALS007 linux/Documentation/sound/ALS007
--- v2.1.108/linux/Documentation/sound/ALS007	Tue Jun 23 10:01:19 1998
+++ linux/Documentation/sound/ALS007	Fri Jul 10 14:03:35 1998
@@ -1,29 +1,29 @@
-ALS-007 based soundcards
-========================
+ALS-007 based sound cards
+=========================
 
-Support for soundcards based around the Avance Logic ALS-007 chip is
+Support for sound cards based around the Avance Logic ALS-007 chip is
 included.  The ALS-007 is a single chip PnP sound solution which is mostly
 hardware compatible with the Sound Blaster 16 card, with most differences
-occuring in the use of the mixer registers.  For this reason the ALS-007 
+occurring in the use of the mixer registers.  For this reason the ALS-007 
 code is integrated as part of the Sound Blaster 16 driver (adding only 800
 bytes to the SB16 driver).
 
-To use an ALS-007 soundcard under Linux, enable the following options in the
+To use an ALS-007 sound card under Linux, enable the following options in the
 sound configuration section of the kernel config:
   - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support
   - FM synthesizer (YM3812/OPL-3) support 
 Since the ALS-007 is a PnP card, the sound driver probably should be
-compiled as a module, with the isapnptools used to wake up the soundcard.
+compiled as a module, with the isapnptools used to wake up the sound card.
 Set the "I/O base for SB", "Sound Blaster IRQ" and "Sound Blaster DMA" (8 bit -
 either 0, 1 or 3) to the values used in your particular installation (they
 should match the values used to configure the card using isapnp).  The
 ALS-007 does NOT implement 16 bit DMA, so the "Sound Blaster 16 bit DMA"
-should be set to -1.  If you wish to use the externel MPU-401 interface on
+should be set to -1.  If you wish to use the external MPU-401 interface on
 the card, "MPU401 I/O base of SB16" and "SB MPU401 IRQ" should be set to
 the appropriate values for your installation.  (Note that the ALS-007
 requires a separate IRQ for the MPU-401, so don't specify -1 here).  (Note
 that the base port of the internal FM synth is fixed at 0x388 on the ALS007; 
-in any case the FM synth location is not setable in the kernel config).
+in any case the FM synth location cannot be set in the kernel configuration).
 
 The resulting sound driver will provide the following capabilities:
   - 8 and 16 bit audio playback
diff -u --recursive --new-file v2.1.108/linux/Documentation/sound/AWE32 linux/Documentation/sound/AWE32
--- v2.1.108/linux/Documentation/sound/AWE32	Tue Jun 23 10:01:19 1998
+++ linux/Documentation/sound/AWE32	Tue Jul 14 10:29:31 1998
@@ -1,64 +1,122 @@
-From: Nicola Bernardelli <nbern@mail.protos.it>
+	Installing and using Creative AWE midi sound under Linux.
 
-     In order to load SB-AWE related drivers on recent kernels (tested
-with 2.1.86 and 2.1.88) with modularized sound support these lines can
-be issued (of course with the suitable values for the parameters)
-after PNP setup:
-
-modprobe  sound.o
-insmod  uart401.o
-insmod  sb.o io=0x220 irq=5 dma=1 dma16=5 mpu_io=0x330
-insmod  awe_wave.o
-
-     Alternatively, in order to also have automatic load on demand
-(not of the awe support, which would anyway most likely also require a
-call to sfxload), these lines can be added to /etc/conf.modules, of
-course with the suitable values for the parameters):
-
-alias char-major-14 sb
-post-install sb modprobe "-k" "adlib_card"
-options sb io=0x220 irq=5 dma=1 dma16=5 mpu_io=0x330
-options adlib_card io=0x388     # FM synthesiser
+This documentation is devoted to the Creative Sound Blaster AWE32, AWE64 and 
+SB32.
 
-and then these two commands can be issued:
+1) Make sure you have an ORIGINAL Creative SB32, AWE32 or AWE64 card. This is 
+important, because the driver works only with real Creative cards.
 
-modprobe sb
+2) If your card is NOT "Plug-n-Play" (I myself don't know Creative AWE non 
+plug'n'play cards however) then go to 5th step now. In the other case
+proceed to step 3.
+
+3) You should obtain isapnptools. I looked through other PnP packages 
+for Linux, but all they are either in deep unstable beta/alpha releases or 
+they are much worse than isapnptools. In my case isapnptools were included in 
+a Linux distribution (Red Hat 5.0). If you also already have them then go to 
+step 4.
+
+The latest copy of isapnptools-1.15 is available from 
+ftp://ftp.demon.co.uk/pub/unix/linux/utils/ (I tested isapnptools-1.15.tgz)
+You should gunzip/untar it to something like /usr/local/ 
+(cp isapnptools-1.15.tgz /usr/local/; cd /usr/local/;
+tar -xzf isapnptools-1.15.tgz). 
+
+Compile the package (make) and install it (make install).
+If something goes wrong check the INSTALL file in isapnptools-1.15 directory.
+
+4) Now do a "pnpdump > /etc/isapnp.conf". File /etc/isapnp.conf will contain 
+info about PnP devices you may have. If you want you can read the manual page
+about isapnp.conf file (man isapnp.conf). Most lines of your isapnp.conf file are 
+commented. You should uncomment lines which don't conflict with your 
+configuration.
+
+ATTENTION! Device Audio should have 1 IRQ, 2 DMA and 3 base I/O resources.
+If you don't have such a configuration you should manually add the resources to
+the isapnp.conf file. After editing I got these lines in the Audio device
+section (I ripped out all the comments):
+
+"(CONFIGURE CTL0044/1132685 (LD 0 (INT 0 (IRQ 5 (MODE +E))) (DMA 0 (CHANNEL 1))
+ (DMA 1 (CHANNEL 5)) (IO 0 (BASE 0x220)) (IO 1 (BASE 0x330)) (IO 2 (BASE 0x388))
+ (ACT Y)))"
+
+(In your case CTL044/1132685 numbers may be other)
+
+Don't forget to uncomment (ACT Y)!
+
+The next device is the on-board IDE controller. You may enable it if you wish,
+but it will not effect sound.
+
+Then WaveTable goes. For some reason Plug-n-Play detects only one I/O port,
+but the wavetable needs THREE! My working string is:
+
+"(CONFIGURE CTL044/1132685 (LD 2 (IO 0 (BASE 0x0620)) (IO 1 (BASE 0x0A20)) 
+(IO 3 (BASE 0x0E20)) (ACT Y) ))"
+
+Resources 0x0620, 0x0A20 and 0x0E20 should work. Other on-board devices: 
+Gameport and StereoEnhance are not required to be inited.
+
+Now you can execute "isapnp /etc/isapnp.conf". No errors should be reported.
+If you correctly installed isapnptools, then isapnp will run every boot time.
+
+5) Now you should recompile the kernel. I recommend using development kernels,
+because the AWE32 driver is included in them.  ATTENTION!  In kernels 2.1.102, 
+2.1.103, 2.1.104-pre1 and 2.1.104 (not the others) the lowlevel sound driver
+is not working. You should use the patch available at 
+http://members.xoom.com/yar/history.html. If you are using stable kernel
+releases 2.0.x, then get the latest version (3.8s9) of 
+OSS/Free at http://members.xoom.com/yar/ossfree38s9-linux20x.tar.gz
+and gunzip/untar it in /usr/src/ (assuming you keep your kernel source in
+/usr/src/linux).  Then go to /usr/src/linux/ and view the README file. That
+file contains info about kernel compilation and installation.
+
+In "make (x,menu)config" select in "Sound":
+"Sound card support", "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support",
+"Generic OPL2/OPL3 FM synthesizer support" and "FM synthesizer (YM3812/OPL-3) 
+support" as <M> (module).
+
+If you use kernel version 2.0.x or version 2.1.y (y <= 2.1.104) skip substep a,
+on 2.1.105 or later go through it.
+
+substep a:
+In "make (x,menu)config" select in "Sound":
+select "OSS sound modules" as <M> (module)
+
+In "Additional low level sound drivers":
+"Additional low level sound drivers", "AWE32 synth" as <M> (module).
+Select "Additional low level sound drivers" as [y] (or [*] (yes)) (If it is not
+available as [y], select it as <M> (module))
+Now recompile the kernel (make dep; make (b)zImage; make modules; 
+make modules_install), update your boot loader and boot new kernel.
+
+6) Now download awesfx program 
+http://members.xoom.com/yar/awesfx-0.4.2.tgz here. Compile it.
+Copy sfxload program to /bin (or /sbin if you wish). To enable AWE midi 
+synthesis you should also get the sound bank file for general midi from
+http://members.xoom.com/yar/synthgm.sbk.gz. Copy it to
+/usr and gunzip it there.
+
+7) Edit /etc/rc.d/rc.local, inserting at the end of the file:
+
+modprobe sound
+insmod uart401
+insmod sb io=0x220 irq=5 dma=1 dma16=5 mpu_io=0x330
 insmod awe_wave
+sfxload /usr/synthfm.sbk
 
-------------------------------------------------------------------------------
+(on io=0xaaa irq=b.... you should use your own settings)
+That will enable the Sound Blaster and AWE wave synthesis.
 
-After picking up the second approach, you may want to put these lines
-on an ossfreeOn script:
+To play midi files you should get one of these programs:
 
- -----
-#!/bin/sh
+Playmidi 2.4 or higher: http://playmidi.openprojects.net
+Drvmidi 4.2.b: http://members.xoom.com/yar/awemidi-0.4.2b.tgz
 
-modprobe sb
-insmod awe_wave
+(These are available at all major Linux FTP sites and may already be
+ in your distribution)
 
-# A call to 'aumix -L' (attention to what the home dir is at boot
-# time, you may put a link /.aumixrc -> /home/<sounduser>/.aumixrc) to
-# restore mixer device levels and a call to 'sfxload <path to
-# soundfont bank>' may be added in a customized ossfreeSetup script:
-
-if [ -x /usr/local/sbin/ossfreeSetup ] ; then
-  /usr/local/sbin/ossfreeSetup
-fi
- -----
-
-And these lines in an ossfreeOff script:
-
- -----
-#!/bin/sh
-
-# NOT set -e, maybe not all of them are currently loaded.
-
-rmmod awe_wave 
-rmmod adlib_card
-rmmod opl3
-rmmod sb
-rmmod uart401
-rmmod sound
-rmmod soundcore
- -----
+If something goes wrong please e-mail me. All comments and suggestions are
+welcome.
 
+			    Yaroslav Rosomakho (alons55@dialup.ptt.ru)
+				    http://members.xoom.com/yar
diff -u --recursive --new-file v2.1.108/linux/Documentation/sound/CS4232 linux/Documentation/sound/CS4232
--- v2.1.108/linux/Documentation/sound/CS4232	Thu Mar 26 15:57:02 1998
+++ linux/Documentation/sound/CS4232	Fri Jul 10 14:03:35 1998
@@ -1,23 +1,23 @@
-
-insmod sound
-insmod ad1848
-insmod uart401
-insmod cs4232 io=* irq=* dma=* dma2=*
-
-This configures the crystal CS423x sound chip and activates its DSP
-functions. On some cards the non PnP setup the board attempts to do fails.
-If you have problems use the kernel PnP facilities.
-
-io is the I/O address of the WSS (normally 0x534)
-irq is the IRQ of this device
-dma/dma2 are the DMA channels. DMA2 may well be 0
-
-
-To get midi synth facilities add
-
-insmod opl3 io=*
-
-io= I/O address of the OPL3 synthesizer. This will be shown in /proc/sys/pnp
-and is normally 0x388
-
-
+To configure the Crystal CS423x sound chip and activate its DSP functions,
+modules may be loaded in this order:
+  
+	modprobe sound
+	insmod ad1848
+	insmod uart401
+	insmod cs4232 io=* irq=* dma=* dma2=*
+  
+This is the meaning of the parameters:
+  
+	io--I/O address of the Windows Sound System (normally 0x534)
+	irq--IRQ of this device
+	dma and dma2--DMA channels (DMA2 may be 0)
+  
+On some cards, the board attempts to do non-PnP setup, and fails.  If you
+have problems, use Linux' PnP facilities. 
+  
+To get MIDI facilities add
+  
+	insmod opl3 io=*
+  
+where "io" is the I/O address of the OPL3 synthesizer. This will be shown
+in /proc/sys/pnp and is normally 0x388.
diff -u --recursive --new-file v2.1.108/linux/Documentation/sound/ESS1868 linux/Documentation/sound/ESS1868
--- v2.1.108/linux/Documentation/sound/ESS1868	Tue Jun  9 11:57:28 1998
+++ linux/Documentation/sound/ESS1868	Fri Jul 10 14:03:35 1998
@@ -1,6 +1,6 @@
 Documentation for the ESS1868F AudioDrive PnP sound card
 
-The ESS1868 Sound card is a PnP ESS1688 compatible 16-bit sound card.
+The ESS1868 sound card is a PnP ESS1688-compatible 16-bit sound card.
 
 Notes about configuring the sound card:
 
diff -u --recursive --new-file v2.1.108/linux/Documentation/sound/MultiSound linux/Documentation/sound/MultiSound
--- v2.1.108/linux/Documentation/sound/MultiSound	Tue Jun 23 10:01:19 1998
+++ linux/Documentation/sound/MultiSound	Fri Jul 10 14:03:35 1998
@@ -25,7 +25,7 @@
 must obtain these images from Turtle Beach (they are included in the
 MultiSound Development Kits), and place them in /etc/sound for
 example, and give the full paths in the Linux configuration.  Please
-note these files must be binary files, not assember.
+note these files must be binary files, not assembler.
 
 * You need the following information to use this driver: the card's
 I/O base (i.e. 0x250), the IRQ (i.e. 5), and the shared memory area
@@ -88,8 +88,8 @@
 			to zero).
 
 
-Creating and Obtaining Firmware
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Obtaining and Creating Firmware Files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 	For the Classic/Tahiti/Monterey
 	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~	
diff -u --recursive --new-file v2.1.108/linux/Documentation/sound/Multisound linux/Documentation/sound/Multisound
--- v2.1.108/linux/Documentation/sound/Multisound	Tue Jun 23 10:01:19 1998
+++ linux/Documentation/sound/Multisound	Wed Dec 31 16:00:00 1969
@@ -1,4 +0,0 @@
-Turtle Beach Multisound support is not yet integrated. If you'd like
-to test it or help out in finishing the module please see
-
-	http://www.rpi.edu/~veliaa/pinlinux.html
diff -u --recursive --new-file v2.1.108/linux/Documentation/sound/Opti linux/Documentation/sound/Opti
--- v2.1.108/linux/Documentation/sound/Opti	Sun Jun  7 11:16:26 1998
+++ linux/Documentation/sound/Opti	Fri Jul 10 14:03:35 1998
@@ -10,8 +10,8 @@
 Please do not report inconsistencies on older Linux
 kernels.
 
-The OPTi 82C931 is supported in its non PnP mode.
-Usually you do not need to set jumpers etc... The sound driver
+The OPTi 82C931 is supported in its non-PnP mode.
+Usually you do not need to set jumpers, etc. The sound driver
 will check the card status and if it is required it will
 force the card into a mode in which it can be programmed.
 
@@ -34,7 +34,7 @@
   Support for OPTi MAD16 and/or Mozart based cards	 	(CONFIG_MAD16)
   FM synthesizer (YM3812/OPL-3) support				(CONFIG_YM3812)
 
-The configuration menu may ask for addresses, irq lines or dma
+The configuration menu may ask for addresses, IRQ lines or DMA
 channels. If the card is used as a module the module loading
 options will override these values.
 
@@ -61,8 +61,8 @@
 the options of your choice. This file is normally installed as
 /etc/isapnp.conf.
 
-The driver had one limitation WRT io port resources.
-IO3 base should be 0x0E0C. Isapnp allows other io ports but this
+The driver has one limitation with respect to I/O port resources:
+IO3 base must be 0x0E0C.  Although isapnp allows other ports, this
 address is hard-coded into the driver.
 
 Using kmod and autoloading the sound driver
@@ -117,7 +117,7 @@
 post-install mad16 /sbin/ad1848_mixer_reroute 14 8 15 3 16 6
 
 This sets resources and options for the mad16 and opl3 drivers.
-I use 2 dma channels (only one is required) to enable full duplex.
+I use two DMA channels (only one is required) to enable full duplex.
 joystick=1 enables the joystick port. cdtype=0 disables the cd port.
 You can also set mpu_io and mpu_irq in the mad16 options for the
 uart401 driver.
diff -u --recursive --new-file v2.1.108/linux/Documentation/sound/Soundblaster linux/Documentation/sound/Soundblaster
--- v2.1.108/linux/Documentation/sound/Soundblaster	Tue Jun 23 10:01:19 1998
+++ linux/Documentation/sound/Soundblaster	Fri Jul 10 14:03:35 1998
@@ -1,17 +1,16 @@
-
-insmod sound
+modprobe sound
 insmod uart401
 insmod sb ...
 
-This loads the driver for the soundblaster and assorted clones. Cards that
+This loads the driver for the Sound Blaster and assorted clones. Cards that
 are covered by other drivers should not be using this driver.
 
-The soundblaster module takes the following arguments
+The Sound Blaster module takes the following arguments
 
-io		I/O address of the soundblaster chip
-irq		IRQ of the soundblaster chip
-dma     	8bit DMA channel for the soundblaster
-dma16   	16bit DMA channel for SB16 and equivalent cards
+io		I/O address of the Sound Blaster chip
+irq		IRQ of the Sound Blaster chip
+dma     	8-bit DMA channel for the Sound Blaster
+dma16   	16-bit DMA channel for SB16 and equivalent cards
 mpu_io		I/O for MPU chip if present
 
 mad16=1 	Set when loading this as part of the MAD16 setup only
diff -u --recursive --new-file v2.1.108/linux/Documentation/sound/es1370 linux/Documentation/sound/es1370
--- v2.1.108/linux/Documentation/sound/es1370	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sound/es1370	Fri Jul 10 14:03:35 1998
@@ -0,0 +1,62 @@
+ALaw/uLaw sample formats
+------------------------
+
+This driver does not support the ALaw/uLaw sample formats.
+ALaw is the default mode when opening a sound device
+using OSS/Free. The reason for the lack of support is
+that the hardware does not support these formats, and adding
+conversion routines to the kernel would lead to very ugly
+code in the presence of the mmap interface to the driver.
+And since xquake uses mmap, mmap is considered important :-)
+and no sane application uses ALaw/uLaw these days anyway.
+In short, playing a Sun .au file as follows:
+
+cat my_file.au > /dev/dsp
+
+does not work. Instead, you may use the play script from
+Chris Bagwell's sox-12.14 package (available from the URL
+below) to play many different audio file formats.
+The script automatically determines the audio format
+and does do audio conversions if necessary.
+http://home.sprynet.com/sprynet/cbagwell/projects.html
+
+
+Blocking vs. nonblocking IO
+---------------------------
+
+Unlike OSS/Free this driver honours the O_NONBLOCK file flag
+not only during open, but also during read and write.
+This is an effort to make the sound driver interface more
+regular. Timidity has problems with this; a patch
+is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html.
+(Timidity patched will also run on OSS/Free).
+
+
+MIDI UART
+---------
+
+The driver supports a simple MIDI UART interface, with
+no ioctl's supported.
+
+
+MIDI synthesizer
+----------------
+
+This soundcard does not have any hardware MIDI synthesizer;
+MIDI synthesis has to be done in software. To allow this
+the driver/soundcard supports two PCM (/dev/dsp) interfaces.
+The second one goes to the mixer "synth" setting and supports
+only a limited set of sampling rates (44100, 22050, 11025, 5512).
+By setting lineout to 1 on the driver command line
+(eg. insmod es1370 lineout=1) it is even possible on some
+cards to convert the LINEIN jack into a second LINEOUT jack, thus
+making it possible to output four independent audio channels!
+
+There is a freely available software package that allows
+MIDI file playback on this soundcard called Timidity.
+See http://www.cgs.fi/~tt/timidity/.
+
+
+
+Thomas Sailer
+sailer@ife.ee.ethz.ch
diff -u --recursive --new-file v2.1.108/linux/Documentation/sound/es1371 linux/Documentation/sound/es1371
--- v2.1.108/linux/Documentation/sound/es1371	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sound/es1371	Fri Jul 10 14:03:35 1998
@@ -0,0 +1,56 @@
+ALaw/uLaw sample formats
+------------------------
+
+This driver does not support the ALaw/uLaw sample formats.
+ALaw is the default mode when opening a sound device
+using OSS/Free. The reason for the lack of support is
+that the hardware does not support these formats, and adding
+conversion routines to the kernel would lead to very ugly
+code in the presence of the mmap interface to the driver.
+And since xquake uses mmap, mmap is considered important :-)
+and no sane application uses ALaw/uLaw these days anyway.
+In short, playing a Sun .au file as follows:
+
+cat my_file.au > /dev/dsp
+
+does not work. Instead, you may use the play script from
+Chris Bagwell's sox-12.14 package (available from the URL
+below) to play many different audio file formats.
+The script automatically determines the audio format
+and does do audio conversions if necessary.
+http://home.sprynet.com/sprynet/cbagwell/projects.html
+
+
+Blocking vs. nonblocking IO
+---------------------------
+
+Unlike OSS/Free this driver honours the O_NONBLOCK file flag
+not only during open, but also during read and write.
+This is an effort to make the sound driver interface more
+regular. Timidity has problems with this; a patch
+is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html.
+(Timidity patched will also run on OSS/Free).
+
+
+MIDI UART
+---------
+
+The driver supports a simple MIDI UART interface, with
+no ioctl's supported.
+
+
+MIDI synthesizer
+----------------
+
+This soundcard does not have any hardware MIDI synthesizer;
+MIDI synthesis has to be done in software. To allow this
+the driver/soundcard supports two PCM (/dev/dsp) interfaces.
+
+There is a freely available software package that allows
+MIDI file playback on this soundcard called Timidity.
+See http://www.cgs.fi/~tt/timidity/.
+
+
+
+Thomas Sailer
+sailer@ife.ee.ethz.ch
diff -u --recursive --new-file v2.1.108/linux/Documentation/sound/mwave linux/Documentation/sound/mwave
--- v2.1.108/linux/Documentation/sound/mwave	Sun Jun  7 11:16:26 1998
+++ linux/Documentation/sound/mwave	Fri Jul 10 14:03:35 1998
@@ -1,4 +1,4 @@
-	How to try and survive an IBM Mwave under Linux SB drivers
+	How to try to survive an IBM Mwave under Linux SB drivers
 
 
 * IBM refuses to provide documentation. If anyone can ever find out what
@@ -13,33 +13,34 @@
 ----------------------------------------------------------------------------
 
 OK, first thing - the IRQ problem IS a problem, whether the test is bypassed or
-not. And it is NOT a linux problem - it is an MWAVE problem that is fixed with
-the latest MWAVE patches. So, in other words, don't bypass the test for MWAVES!
+not.  It is NOT a Linux problem, but an MWAVE problem that is fixed with the
+latest MWAVE patches. So, in other words, don't bypass the test for MWAVES!
 
-My config is Win 95 on HDA1, swap on HDA2, RH 5 on HDA3.
+I have Windows 95 on /dev/hda1, swap on /dev/hda2, and Red Hat 5 on /dev/hda3.
 
 The steps, then:
 
     Boot to Linux. 
-    Mount Win95 file system (assume mount mount = /dos95)
+    Mount Windows 95 file system (assume mount point = /dos95).
     mkdir /dos95/linux
     mkdir /dos95/linux/boot
     mkdir /dos95/linux/boot/parms
 
-    Copy the kernel, any initrd image, and loadlin to /dos95/linux/boot
+    Copy the kernel, any initrd image, and loadlin to /dos95/linux/boot/.
 
-    Reboot to win 95
+    Reboot to Windows 95.
 
-    Edit C:/msdos.sys and add/change the following:
+    Edit C:/msdos.sys and add or change the following:
 
         Logo=0
         BootGUI=0
 
-        [Note msdos.sys IS a text file but it needs to be 'unhidden' and make
-         read-writable before it can be edited]
+    Note that msdos.sys is a text file but it needs to be made 'unhidden',
+    readable and writable before it can be edited.  This can be done with
+    DOS' "attrib" command.
 
-    Edit Config .sys to have multiple config menus. I have one for win95, and
-    five for linux. Like this:
+    Edit config.sys to have multiple config menus. I have one for windows 95 and
+    five for Linux, like this:
 ------------
 [menu]
 menuitem=W95, Windows 95
@@ -70,11 +71,11 @@
 SHELL=c:\command.com /e:2048
 -------------------
 
-The important things are the SHELL and DEVICE statements
+The important things are the SHELL and DEVICE statements.
 
-    Then change Autoexec.bat. Basically everything in there originally should be
-    done ONLY when Win95 is booted. Then you add new things specifically for
-    Linux.  Mine is as follows
+    Then change autoexec.bat. Basically everything in there originally should be
+    done ONLY when Windows 95 is booted. Then you add new things specifically
+    for Linux.  Mine is as follows
 
 ---------------
 @ECHO OFF
@@ -111,35 +112,35 @@
 
 ------------------------
 
-Now build a file in c:\linux\boot\parms for each linux config that you have.
+Now build a file in c:\linux\boot\parms for each Linux config that you have.
 
 For example, my LINDOC3 config is for a docked Thinkpad at runlevel 3 with no
-initrd image, and has a parm file named LINDOC3.PAR in c:\linux\boot\parms:
+initrd image, and has a parameter file named LINDOC3.PAR in c:\linux\boot\parms:
 
 -----------------------
 #   LOADLIN @param_file image=other_image root=/dev/other
 #
 #	Linux Console in docking station
 #
-c:\linux\boot\zImage.krn         # first value must be the filename of the Linux-kernel
-root=/dev/hda3                    # the device which gets mounted as root FS
-ro                                      # Other kernel arguments go here
+c:\linux\boot\zImage.krn        # First value must be filename of Linux kernel.
+root=/dev/hda3                  # device which gets mounted as root FS
+ro                              # Other kernel arguments go here.
 apm=off
 doc=yes
 3
 -----------------------
 
-the doc=yes parm is an environment variable that my init scripts use, it is not
+The doc=yes parameter is an environment variable used by my init scripts, not
 a kernel argument.
 
-However, the apm=off parm IS a kernel argument!  APM, at least in my setup,
+However, the apm=off parameter IS a kernel argument!  APM, at least in my setup,
 causes the kernel to crash when loaded via loadlin (but NOT when loaded via
 LILO). The APM stuff COULD be forced out of the kernel via the kernel compile
-options. BUT I, instead, got hold of an unofficial patch to the APM drivers that
-allows them to be dynamically deactivated via kernel arguments. Whatever you
-chose to document, APM, it seems, MUST be off for setups like mine.
+options. Instead, I got an unofficial patch to the APM drivers that allows them
+to be dynamically deactivated via kernel arguments. Whatever you chose to
+document, APM, it seems, MUST be off for setups like mine.
 
-Now check the C:\MWW\MWCONFIG.REF looks like this:
+Now make sure C:\MWW\MWCONFIG.REF looks like this:
 
 ----------------------
 [NativeDOS]
@@ -167,16 +168,16 @@
 
 Default=SBPRO
 
-Reboot to Win95 and choose Linux. When booted, use sndconfig to configure the  
-sound modules and VOILA - ThinkPad sound with Linux.
+Reboot to Windows 95 and choose Linux. When booted, use sndconfig to configure
+the sound modules and voilà - ThinkPad sound with Linux.
 
-Now the gotchas - You can either have CD sound OR Mixers but not both. That's a
-problem with the SB1.5(CD sound) or SBPRO(Mixers) settings. No-one knows why
+Now the gotchas - you can either have CD sound OR Mixers but not both. That's a
+problem with the SB1.5 (CD sound) or SBPRO (Mixers) settings. No one knows why
 this is!
 
-And, for some reason MPEG3 files, when played through mpg123, sound like they
-are playing at 1/8th speed - not very useful!!!!!!!!!!!! If you have ANY insight
-on why this second thing might be happening I would be grateful.
+For some reason MPEG3 files, when played through mpg123, sound like they
+are playing at 1/8th speed - not very useful!  If you have ANY insight
+on why this second thing might be happening, I would be grateful.
 
 ===========================================================
     _/      _/_/_/_/
@@ -188,5 +189,3 @@
 _/    _/
  _/_/
 ===========================================================
-
-
diff -u --recursive --new-file v2.1.108/linux/Documentation/sound/sonicvibes linux/Documentation/sound/sonicvibes
--- v2.1.108/linux/Documentation/sound/sonicvibes	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sound/sonicvibes	Fri Jul 10 14:03:35 1998
@@ -0,0 +1,73 @@
+ALaw/uLaw sample formats
+------------------------
+
+This driver does not support the ALaw/uLaw sample formats.
+ALaw is the default mode when opening a sound device
+using OSS/Free. The reason for the lack of support is
+that the hardware does not support these formats, and adding
+conversion routines to the kernel would lead to very ugly
+code in the presence of the mmap interface to the driver.
+And since xquake uses mmap, mmap is considered important :-)
+and no sane application uses ALaw/uLaw these days anyway.
+In short, playing a Sun .au file as follows:
+
+cat my_file.au > /dev/dsp
+
+does not work. Instead, you may use the play script from
+Chris Bagwell's sox-12.14 package (available from the URL
+below) to play many different audio file formats.
+The script automatically determines the audio format
+and does do audio conversions if necessary.
+http://home.sprynet.com/sprynet/cbagwell/projects.html
+
+
+Blocking vs. nonblocking IO
+---------------------------
+
+Unlike OSS/Free this driver honours the O_NONBLOCK file flag
+not only during open, but also during read and write.
+This is an effort to make the sound driver interface more
+regular. Timidity has problems with this; a patch
+is available from http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html.
+(Timidity patched will also run on OSS/Free).
+
+
+MIDI UART
+---------
+
+The driver supports a simple MIDI UART interface, with
+no ioctl's supported.
+
+
+MIDI synthesizer
+----------------
+
+The card both has an OPL compatible FM synthesizer as well as
+a wavetable synthesizer.
+
+I haven't managed so far to get the OPL synth running.
+
+Using the wavetable synthesizer requires allocating
+1-4MB of physically contiguous memory, which isn't possible
+currently on Linux without ugly hacks like the bigphysarea
+patch. Therefore, the driver doesn't support wavetable
+synthesis.
+
+
+No support from S3
+------------------
+
+I do not get any support from S3. Therefore, the driver
+still has many problems. For example, although the manual
+states that the chip should be able to access the sample
+buffer anywhere in 32bit address space, I haven't managed to
+get it working with buffers above 16M. Therefore, the card
+has the same disadvantages as ISA soundcards.
+
+Given that the card is also very noisy, and if you haven't
+already bought it, you should strongly opt for one of the
+comparatively priced Ensoniq products.
+
+
+Thomas Sailer
+sailer@ife.ee.ethz.ch
diff -u --recursive --new-file v2.1.108/linux/Documentation/sound/ultrasound linux/Documentation/sound/ultrasound
--- v2.1.108/linux/Documentation/sound/ultrasound	Sat May  2 14:19:52 1998
+++ linux/Documentation/sound/ultrasound	Fri Jul 10 14:03:35 1998
@@ -1,19 +1,18 @@
-
-insmod sound
+modprobe sound
 insmod ad1848
 insmod gus io=* irq=* dma=* ...
 
-This loads the driver for the Gravis Ultrasound family of soundcards.
+This loads the driver for the Gravis Ultrasound family of sound cards.
 
-The gus modules takes the following arguments
+The gus module takes the following arguments
 
-io		I/O address of the ultrasound card (eg. io=0x220)
-irq		IRQ of the soundblaster card 
-dma     	DMA channel for the soundblaster
+io		I/O address of the Ultrasound card (eg. io=0x220)
+irq		IRQ of the Sound Blaster card 
+dma     	DMA channel for the Sound Blaster
 dma16   	2nd DMA channel, only needed for full duplex operation
 type		1 for PnP card
 gus16		1 for using 16 bit sampling daughter board
-no_wave_dma	Set to disable dma usage for wavetable (see note)
+no_wave_dma	Set to disable DMA usage for wavetable (see note)
 db16		???
 
 
diff -u --recursive --new-file v2.1.108/linux/Documentation/svga.txt linux/Documentation/svga.txt
--- v2.1.108/linux/Documentation/svga.txt	Wed Jun 24 22:54:02 1998
+++ linux/Documentation/svga.txt	Fri Jul 10 15:18:29 1998
@@ -70,7 +70,7 @@
    The mode list usually contains a few basic modes and some VESA modes.  In
 case your chipset has been detected, some chipset-specific modes are shown as
 well (some of these might be missing or unusable on your machine as different
-BIOSs are often shipped with the same card and the mode numbers depend purely
+BIOSes are often shipped with the same card and the mode numbers depend purely
 on the VGA BIOS).
 
    The modes displayed on the menu are partially sorted: The list starts with
@@ -139,8 +139,8 @@
 
    If you add 0x8000 to the mode ID, the program will try to recalculate
 vertical display timing according to mode parameters, which can be used to
-eliminate some annoying bugs of certain VGA BIOSs (usually those used for
-cards with S3 chipsets and old Cirrus Logic BIOSs) -- mainly extra lines at the
+eliminate some annoying bugs of certain VGA BIOSes (usually those used for
+cards with S3 chipsets and old Cirrus Logic BIOSes) -- mainly extra lines at the
 end of the display.
 
 4. Options
@@ -179,7 +179,7 @@
 top of the menu).
 
    CONFIG_VIDEO_400_HACK - force setting of 400 scan lines for standard VGA
-modes. This option is intended to be used on certain buggy BIOSs which draw
+modes. This option is intended to be used on certain buggy BIOSes which draw
 some useless logo using font download and then fail to reset the correct mode.
 Don't use unless needed as it forces resetting the video card.
 
diff -u --recursive --new-file v2.1.108/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.108/linux/MAINTAINERS	Wed Jul  1 19:38:51 1998
+++ linux/MAINTAINERS	Fri Jul 10 14:03:35 1998
@@ -470,6 +470,13 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
+PCI SOUND DRIVERS (ES1370, ES1371 and SONICVIBES)
+P:	Thomas Sailer
+M:	sailer@ife.ee.ethz.ch
+L:	linux-sound@vger.rutgers.edu
+W:	http://www.ife.ee.ethz.ch/~sailer/linux/pciaudio.html
+S:	Maintained
+
 PCNET32 NETWORK DRIVER
 P:	Thomas Bogendoerfer
 M:	tsbogend@alpha.franken.de
diff -u --recursive --new-file v2.1.108/linux/Makefile linux/Makefile
--- v2.1.108/linux/Makefile	Wed Jul  1 19:38:51 1998
+++ linux/Makefile	Wed Jul  1 19:39:31 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 108
+SUBLEVEL = 109
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
diff -u --recursive --new-file v2.1.108/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.1.108/linux/arch/alpha/config.in	Wed Jul  1 19:38:52 1998
+++ linux/arch/alpha/config.in	Fri Jul 10 15:18:29 1998
@@ -173,10 +173,6 @@
 #bool 'Echo console messages on /dev/ttyS0 (COM1)' CONFIG_SERIAL_ECHO
 
 if [ "$CONFIG_PCI" = "y" ]; then
-  bool 'TGA Console Support' CONFIG_TGA_CONSOLE
-#  if [ "$CONFIG_TGA_CONSOLE" = "y" ]; then
-#    bool 'VGA Console Support' CONFIG_VGA_CONSOLE
-#  fi
   bool 'PCI quirks' CONFIG_PCI_QUIRKS
   if [ "$CONFIG_PCI_QUIRKS" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
     bool 'PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
@@ -203,12 +199,6 @@
 fi
 endmenu
 
-bool 'Support for frame buffer devices' CONFIG_FB
-if [ "$CONFIG_FB" = "y" ]; then
-  define_bool CONFIG_PCI_CONSOLE y
-fi
-source drivers/video/Config.in
-
 source drivers/pnp/Config.in
 
 source drivers/block/Config.in
@@ -264,6 +254,18 @@
 
 source drivers/char/Config.in
 
+if [ "$CONFIG_VT" = "y" ]; then
+  mainmenu_option next_comment
+  comment 'Console drivers'
+  bool 'VGA text console' CONFIG_VGA_CONSOLE
+  bool 'Support for frame buffer devices' CONFIG_FB
+  if [ "$CONFIG_FB" = "y" ]; then
+    define_bool CONFIG_PCI_CONSOLE y
+  fi
+  source drivers/video/Config.in
+  endmenu
+fi
+
 mainmenu_option next_comment
 comment 'Sound'
 
@@ -289,7 +291,3 @@
 
 bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
 endmenu
-
-if [ "$CONFIG_TGA_CONSOLE" = "n" ]; then
-  define_bool CONFIG_VGA_CONSOLE y
-fi
diff -u --recursive --new-file v2.1.108/linux/arch/alpha/kernel/alpha_ksyms.c linux/arch/alpha/kernel/alpha_ksyms.c
--- v2.1.108/linux/arch/alpha/kernel/alpha_ksyms.c	Fri May  8 23:14:41 1998
+++ linux/arch/alpha/kernel/alpha_ksyms.c	Fri Jul 10 15:18:29 1998
@@ -14,6 +14,7 @@
 #include <linux/in.h>
 #include <linux/in6.h>
 #include <linux/pci.h>
+#include <linux/tty.h>
 
 #include <asm/io.h>
 #include <asm/hwrpb.h>
@@ -46,6 +47,7 @@
 EXPORT_SYMBOL(local_irq_count);
 EXPORT_SYMBOL(enable_irq);
 EXPORT_SYMBOL(disable_irq);
+EXPORT_SYMBOL(screen_info);
 
 /* platform dependent support */
 EXPORT_SYMBOL(_inb);
@@ -62,7 +64,7 @@
 EXPORT_SYMBOL(_writel);
 EXPORT_SYMBOL(_memcpy_fromio);
 EXPORT_SYMBOL(_memcpy_toio);
-EXPORT_SYMBOL(_memset_io);
+EXPORT_SYMBOL(_memset_c_io);
 EXPORT_SYMBOL(insb);
 EXPORT_SYMBOL(insw);
 EXPORT_SYMBOL(insl);
@@ -85,6 +87,7 @@
 EXPORT_SYMBOL(memmove);
 EXPORT_SYMBOL(__memcpy);
 EXPORT_SYMBOL(__memset);
+EXPORT_SYMBOL(__memsetw);
 EXPORT_SYMBOL(__constant_c_memset);
 
 EXPORT_SYMBOL(dump_thread);
diff -u --recursive --new-file v2.1.108/linux/arch/alpha/kernel/setup.c linux/arch/alpha/kernel/setup.c
--- v2.1.108/linux/arch/alpha/kernel/setup.c	Wed Jul  1 19:38:52 1998
+++ linux/arch/alpha/kernel/setup.c	Fri Jul 10 15:18:29 1998
@@ -90,7 +90,7 @@
 	16			/* orig-video-points */
 #else
 	0, 25,			/* orig-x, orig-y */
-	{ 0, 0 },		/* unused */
+	0,	 		/* unused */
 	0,			/* orig-video-page */
 	0,			/* orig-video-mode */
 	80,			/* orig-video-cols */
@@ -223,12 +223,12 @@
 	setup_smp();
 #endif
 
-#ifdef CONFIG_VGA_CONSOLE
+#ifdef CONFIG_VT
+#if defined(CONFIG_VGA_CONSOLE)
 	conswitchp = &vga_con;
+#elif defined(CONFIG_DUMMY_CONSOLE)
+	conswitchp = &dummy_con;
 #endif
-#ifdef CONFIG_FB
-	/* Frame buffer device based console */
-	conswitchp = &fb_con;
 #endif
 }
 
diff -u --recursive --new-file v2.1.108/linux/arch/arm/config.in linux/arch/arm/config.in
--- v2.1.108/linux/arch/arm/config.in	Fri May  8 23:14:41 1998
+++ linux/arch/arm/config.in	Fri Jul 10 15:18:29 1998
@@ -108,8 +108,12 @@
 source drivers/acorn/block/Config.in
 
 source arch/arm/drivers/char/Config.in
+
+mainmenu_option next_comment
+comment 'Console drivers'
 bool 'Support Frame buffer devices' CONFIG_FB
 source drivers/video/Config.in
+endmenu
 
 if [ "$CONFIG_NET" = "y" ]; then
   source net/Config.in
diff -u --recursive --new-file v2.1.108/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.1.108/linux/arch/i386/config.in	Wed Jun 24 22:54:03 1998
+++ linux/arch/i386/config.in	Sun Jul 12 22:36:20 1998
@@ -56,8 +56,6 @@
   tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA
 fi
 
-bool 'Video mode selection support' CONFIG_VIDEO_SELECT
-
 tristate 'Parallel port support' CONFIG_PARPORT
 if [ "$CONFIG_PARPORT" != "n" ]; then
   dep_tristate '   PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
@@ -66,10 +64,6 @@
   fi
 fi
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  bool 'Support for frame buffer devices (EXPERIMENTAL)' CONFIG_FB
-fi
-
 endmenu
 
 source drivers/pnp/Config.in
@@ -125,11 +119,19 @@
 
 source fs/nls/Config.in
 
-define_bool CONFIG_VGA_CONSOLE y
-
-source drivers/video/Config.in
-
 source drivers/char/Config.in
+
+if [ "$CONFIG_VT" = "y" ]; then
+  mainmenu_option next_comment
+  comment 'Console drivers'
+  bool 'VGA text console' CONFIG_VGA_CONSOLE
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    bool 'Video mode selection support' CONFIG_VIDEO_SELECT
+    bool 'Support for frame buffer devices (EXPERIMENTAL)' CONFIG_FB
+  fi
+  source drivers/video/Config.in
+  endmenu
+fi
 
 mainmenu_option next_comment
 comment 'Sound'
diff -u --recursive --new-file v2.1.108/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.108/linux/arch/i386/defconfig	Wed Jun 24 22:54:03 1998
+++ linux/arch/i386/defconfig	Fri Jul 10 15:31:53 1998
@@ -39,7 +39,6 @@
 CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
 CONFIG_BINFMT_MISC=y
-# CONFIG_VIDEO_SELECT is not set
 # CONFIG_PARPORT is not set
 
 #
@@ -245,7 +244,6 @@
 # CONFIG_UFS_FS is not set
 # CONFIG_MAC_PARTITION is not set
 # CONFIG_NLS is not set
-CONFIG_VGA_CONSOLE=y
 
 #
 # Character devices
@@ -277,6 +275,11 @@
 # Ftape, the floppy tape device driver
 #
 # CONFIG_FTAPE is not set
+
+#
+# Console drivers
+#
+CONFIG_VGA_CONSOLE=y
 
 #
 # Sound
diff -u --recursive --new-file v2.1.108/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.1.108/linux/arch/i386/kernel/entry.S	Wed Jul  1 19:38:52 1998
+++ linux/arch/i386/kernel/entry.S	Tue Jul  7 13:03:41 1998
@@ -94,18 +94,31 @@
 	movl %dx,%ds; \
 	movl %dx,%es;
 
-#define RESTORE_ALL \
-	popl %ebx; \
-	popl %ecx; \
-	popl %edx; \
-	popl %esi; \
-	popl %edi; \
-	popl %ebp; \
-	popl %eax; \
-	popl %ds; \
-	popl %es; \
-	addl $4,%esp; \
-	iret
+#define RESTORE_ALL	\
+	popl %ebx;	\
+	popl %ecx;	\
+	popl %edx;	\
+	popl %esi;	\
+	popl %edi;	\
+	popl %ebp;	\
+	popl %eax;	\
+1:	popl %ds;	\
+2:	popl %es;	\
+3:	addl $4,%esp;	\
+	iret;		\
+.section fixup,"ax";	\
+4:	pushl $0;	\
+	popl %ds;	\
+	jmp 2b;		\
+5:	pushl $0;	\
+	popl %es;	\
+	jmp 3b;		\
+.previous;		\
+.section __ex_table,"a";\
+	.align 4;	\
+	.long 1b,4b;	\
+	.long 2b,5b;	\
+.previous
 
 #define GET_CURRENT(reg) \
 	movl %esp, reg; \
diff -u --recursive --new-file v2.1.108/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- v2.1.108/linux/arch/i386/kernel/head.S	Tue Jun 23 10:01:19 1998
+++ linux/arch/i386/kernel/head.S	Wed Jul 15 16:24:26 1998
@@ -148,80 +148,6 @@
  * we don't need to preserve eflags.
  */
 
-/*
- * Don't do this here. Do it in the CPU init code in C, right now we
- * must _not_ play unsafe games with the IO ports.
- *
- * A PPro and a P-II will also preserve the flags after a division,
- * so this "cyrix" test is just completely unsafe. IO port 0x22 is
- * some magic ACPI port on non-cyrix chips, so if this trashes that
- * we're screwed.
- *
- * This code belongs in "asm-i386/bugs.h" anyway (at which point we
- * _have_ done a CPUID for CPU's that support it directly, so we'll
- * be able to tell a intel P-II trivially at that point).
- */
-#undef CYRIX_CODE_BREAKAGE
-#ifdef CYRIX_CODE_BREAKAGE
-
-	/*
-	 * A Cyrix preserves flags in cases where other CPUs change
-	 * them in undefined ways. We need to know this since we may
-	 * need to enable the CPUID instruction at least. (Cyrix chips
-	 * prior to M2 have CPUID disabled by default, the Cx486s
-	 * didn't have it at all.)
-	 */
-	xor %ax,%ax
-	sahf
-	movb $5,%al
-	movb $2,%bl
-	div %bl
-	lahf
-	cmpb $2,%ah
-	jne ncyrix
-
-	/*
-	 * It behaves like a Cyrix so put "Cyrix" in the vendor id
-	 * field. It may be overwritten later with the real thing
-	 * if CPUID works.
-	 */
-	movl $0x69727943,X86_VENDOR_ID			# low 4 chars
-	movl $0x00000078,X86_VENDOR_ID+4		# next 4 chars
-
-	/*
-	 * N.B. The pattern of accesses to 0x22 and 0x23 is *important*
-	 *      so do not try and "optimise" it! For the same reason we
-	 *	do all this with interrupts off just to be sure.
-	 */
-#define setCx86(reg, val) \
-	movb reg,%al;	\
-	outb %al,$0x22;	\
-	movb val,%al;	\
-	outb %al,$0x23
-
-#define getCx86(reg) \
-	movb reg,%al;	\
-	outb %al,$0x22;	\
-	inb $0x23,%al
-
-	getCx86($0xc3)		# get CCR3
-	movb %al,%cl		# Save old value
-	movb %al,%bl
-	andb $0x0f,%bl		# Enable all config registers (for CCR4 access)
-	orb $0x10,%bl
-	setCx86($0xc3,%bl)
-
-	getCx86($0xe8)		# CCR4 |= CPUID
-	orb $0x80,%al
-	movb %al,%bl
-	setCx86($0xe8,%bl)
-
-	setCx86($0xc3,%cl)	# Restore old CCR3
-
-ncyrix:
-
-#endif
-
 	movl $3,X86		# at least 386
 	pushfl			# push EFLAGS
 	popl %eax		# get EFLAGS
@@ -246,7 +172,7 @@
 	pushl %ecx		# restore original EFLAGS
 	popfl
 	andl $0x200000,%eax
-	je nocpuid
+	je is486
 
 	/* get vendor info */
 	xorl %eax,%eax			# call CPUID with 0 -> return vendor ID
@@ -257,7 +183,7 @@
 	movl %ecx,X86_VENDOR_ID+8	# last 4 chars
 
 	orl %eax,%eax			# do we have processor info as well?
-	je nocpuid
+	je is486
 
 	movl $1,%eax		# Use the CPUID instruction to get CPU type
 	cpuid
@@ -270,44 +196,6 @@
 	andb $0x0f,%cl		# mask mask revision
 	movb %cl,X86_MASK
 	movl %edx,X86_CAPABILITY
-
-nocpuid:
-
-#ifdef CYRIX_CODE_BREAKAGE
-	/*
-	 * Even if we had CPUID Cyrix tries to look compatible with
-	 * Intel so we have to go elsewhere for the nitty gritty.
-	 */
-	cmpl $0x69727943,X86_VENDOR_ID		# "Cyri[x.*]"?
-	jne is486				# maybe ...
-
-	movb $0xfe,X86_MODEL			# Generic Cx486?
-	movb $0,X86_MASK
-
-	getCx86($0xc3)		# Test for DEVID by writing CCR3
-	movb %al,%cl
-	movb %al,%bl
-	orb $0x80,%bl
-	setCx86($0xc3,%bl)
-	getCx86($0xc0)		# dummy to change bus
-	getCx86($0xc3)
-	cmpb %al,%cl
-	je is486		# not writable == no DEVID
-
-	setCx86($0xc3,%cl)	# restore CCR3
-
-	getCx86($0xff)		# get DEVID in preference to any CPUID
-	movb %al,X86_MASK
-	getCx86($0xfe)
-	movb %al,X86_MODEL
-	andb $0xf0,%al		# Check for 6x86(L)
-	cmp $0x30,%al
-	jnz is486
-	getCx86($0xe9)		# CCR5: reset SLOP bit, so that the udelay loop
-	andb $0xfd,%al		# works well on 6x86(L) CPU's.
-	movb %al,%bl
-	setCx86($0xe9,%bl)
-#endif
 
 is486:
 	movl %cr0,%eax		# 486 or better
diff -u --recursive --new-file v2.1.108/linux/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c
--- v2.1.108/linux/arch/i386/kernel/i386_ksyms.c	Fri May  8 23:14:42 1998
+++ linux/arch/i386/kernel/i386_ksyms.c	Fri Jul 10 15:18:29 1998
@@ -103,3 +103,7 @@
 EXPORT_SYMBOL(mca_isenabled);
 EXPORT_SYMBOL(mca_isadapter);
 #endif
+
+#ifdef CONFIG_VT
+EXPORT_SYMBOL(screen_info);
+#endif
diff -u --recursive --new-file v2.1.108/linux/arch/i386/kernel/ldt.c linux/arch/i386/kernel/ldt.c
--- v2.1.108/linux/arch/i386/kernel/ldt.c	Thu Apr 23 20:21:28 1998
+++ linux/arch/i386/kernel/ldt.c	Tue Jul  7 12:35:14 1998
@@ -35,21 +35,29 @@
 
 static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
 {
+	struct mm_struct * mm = current->mm;
+	void * ldt;
+	__u32 entry_1, entry_2, *lp;
+	__u16 selector, reg_fs, reg_gs;
+	int error;
 	struct modify_ldt_ldt_s ldt_info;
-	unsigned long *lp;
-	struct mm_struct * mm;
-	int error, i;
 
+	error = -EINVAL;
 	if (bytecount != sizeof(ldt_info))
-		return -EINVAL;
-	error = copy_from_user(&ldt_info, ptr, sizeof(ldt_info));
-	if (error)
-		return -EFAULT; 	
-
-	if ((ldt_info.contents == 3 && (oldmode || ldt_info.seg_not_present == 0)) || ldt_info.entry_number >= LDT_ENTRIES)
-		return -EINVAL;
-
-	mm = current->mm;
+		goto out;
+	error = -EFAULT; 	
+	if (copy_from_user(&ldt_info, ptr, sizeof(ldt_info)))
+		goto out;
+
+	error = -EINVAL;
+	if (ldt_info.entry_number >= LDT_ENTRIES)
+		goto out;
+	if (ldt_info.contents == 3) {
+		if (oldmode)
+			goto out;
+		if (ldt_info.seg_not_present == 0)
+			goto out;
+	}
 
 	/*
 	 * Horrible dependencies! Try to get rid of this. This is wrong,
@@ -62,60 +70,97 @@
 	 * For no good reason except historical, the GDT index of the LDT
 	 * is chosen to follow the index number in the task[] array.
 	 */
-	if (!mm->segments) {
-		for (i=1 ; i<NR_TASKS ; i++) {
-			if (task[i] == current) {
-				if (!(mm->segments = (void *) vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE)))
-					return -ENOMEM;
-				memset(mm->segments, 0, LDT_ENTRIES*LDT_ENTRY_SIZE);
-				set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, mm->segments, LDT_ENTRIES);
-				load_ldt(i);
-			}
+	ldt = mm->segments;
+	if (!ldt) {
+		error = -ENOMEM;
+		ldt = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
+		if (!ldt)
+			goto out;
+		memset(ldt, 0, LDT_ENTRIES*LDT_ENTRY_SIZE);
+		/*
+		 * Make sure someone else hasn't allocated it for us ...
+		 */
+		if (!mm->segments) {
+			int i = current->tarray_ptr - &task[0];
+			mm->segments = ldt;
+			set_ldt_desc(gdt+(i<<1)+FIRST_LDT_ENTRY, ldt, LDT_ENTRIES);
+			load_ldt(i);
+			if (mm->count > 1)
+				printk(KERN_WARNING
+					"LDT allocated for cloned task!\n");
+		} else {
+			vfree(ldt);
 		}
 	}
-	
-	lp = (unsigned long *) (LDT_ENTRY_SIZE * ldt_info.entry_number + (unsigned long) mm->segments);
+
+	/*
+	 * Check whether the entry to be changed is currently in use.
+	 * If it is, we may need extra validation checks in case the
+	 * kernel is forced to save and restore the selector.
+	 *
+	 * Note: we check the fs and gs values as well, as these are
+	 * loaded by the signal code and during a task switch.
+	 */
+	selector = (ldt_info.entry_number << 3) | 4;
+	__asm__("movw %%fs,%0" : "=r"(reg_fs));
+	__asm__("movw %%gs,%0" : "=r"(reg_gs));
+
+	lp = (__u32 *) ((selector & ~7) + (char *) ldt);
+
    	/* Allow LDTs to be cleared by the user. */
-   	if (ldt_info.base_addr == 0 && ldt_info.limit == 0
-		&& (oldmode ||
-			(  ldt_info.contents == 0
-			&& ldt_info.read_exec_only == 1
-			&& ldt_info.seg_32bit == 0
-			&& ldt_info.limit_in_pages == 0
-			&& ldt_info.seg_not_present == 1
-			&& ldt_info.useable == 0 )) ) {
-		*lp = 0;
-		*(lp+1) = 0;
-		return 0;
+   	if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
+		if (oldmode ||
+		    (ldt_info.contents == 0		&&
+		     ldt_info.read_exec_only == 1	&&
+		     ldt_info.seg_32bit == 0		&&
+		     ldt_info.limit_in_pages == 0	&&
+		     ldt_info.seg_not_present == 1	&&
+		     ldt_info.useable == 0 )) {
+			entry_1 = 0;
+			entry_2 = 0;
+			goto out_check;
+		}
 	}
-	*lp = ((ldt_info.base_addr & 0x0000ffff) << 16) |
+
+	entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
 		  (ldt_info.limit & 0x0ffff);
-	*(lp+1) = (ldt_info.base_addr & 0xff000000) |
-		  ((ldt_info.base_addr & 0x00ff0000)>>16) |
+	entry_2 = (ldt_info.base_addr & 0xff000000) |
+		  ((ldt_info.base_addr & 0x00ff0000) >> 16) |
 		  (ldt_info.limit & 0xf0000) |
-		  (ldt_info.contents << 10) |
 		  ((ldt_info.read_exec_only ^ 1) << 9) |
+		  (ldt_info.contents << 10) |
+		  ((ldt_info.seg_not_present ^ 1) << 15) |
 		  (ldt_info.seg_32bit << 22) |
 		  (ldt_info.limit_in_pages << 23) |
-		  ((ldt_info.seg_not_present ^1) << 15) |
 		  0x7000;
-	if (!oldmode) *(lp+1) |= (ldt_info.useable << 20);
-	return 0;
+	if (!oldmode)
+		entry_2 |= (ldt_info.useable << 20);
+
+out_check:
+	/* OK to change the entry ...  */
+	*lp	= entry_1;
+	*(lp+1)	= entry_2;
+	error = 0;
+out:
+	return error;
 }
 
 asmlinkage int sys_modify_ldt(int func, void *ptr, unsigned long bytecount)
 {
-	int ret;
+	int ret = -ENOSYS;
 
 	lock_kernel();
-	if (func == 0)
+	switch (func) {
+	case 0:
 		ret = read_ldt(ptr, bytecount);
-	else if (func == 1)
+		break;
+	case 1:
 		ret = write_ldt(ptr, bytecount, 1);
-	else  if (func == 0x11)
+		break;
+	case 0x11:
 		ret = write_ldt(ptr, bytecount, 0);
-	else
-		ret = -ENOSYS;
+		break;
+	}
 	unlock_kernel();
 	return ret;
 }
diff -u --recursive --new-file v2.1.108/linux/arch/i386/kernel/mtrr.c linux/arch/i386/kernel/mtrr.c
--- v2.1.108/linux/arch/i386/kernel/mtrr.c	Wed Jun 24 22:54:03 1998
+++ linux/arch/i386/kernel/mtrr.c	Fri Jul 10 14:01:13 1998
@@ -152,8 +152,6 @@
 #define TRUE  1
 #define FALSE 0
 
-#define X86_FEATURE_MTRR	0x1000		/* memory type registers */
-
 #define MTRRcap_MSR     0x0fe
 #define MTRRdefType_MSR 0x2ff
 
diff -u --recursive --new-file v2.1.108/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.1.108/linux/arch/i386/kernel/process.c	Wed Jun 24 22:54:03 1998
+++ linux/arch/i386/kernel/process.c	Wed Jul 15 12:52:33 1998
@@ -48,12 +48,10 @@
 
 spinlock_t semaphore_wake_lock = SPIN_LOCK_UNLOCKED;
 
-struct task_struct *last_task_used_math = NULL;
-
 #ifdef __SMP__
-asmlinkage void ret_from_smpfork(void) __asm__("ret_from_smpfork");
+asmlinkage void ret_from_fork(void) __asm__("ret_from_smpfork");
 #else
-asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call");
+asmlinkage void ret_from_fork(void) __asm__("ret_from_sys_call");
 #endif
 
 #ifdef CONFIG_APM
@@ -425,17 +423,70 @@
 		0xffff & regs->xds,0xffff & regs->xes);
 }
 
+/*
+ * Allocation and freeing of basic task resources.
+ *
+ * NOTE! The task struct and the stack go together
+ *
+ * The task structure is a two-page thing, and as such
+ * not reliable to allocate using the basic page alloc
+ * functions. We have a small cache of structures for
+ * when the allocations fail..
+ *
+ * This extra buffer essentially acts to make for less
+ * "jitter" in the allocations..
+ */
+#define EXTRA_TASK_STRUCT	16
+static struct task_struct * task_struct_stack[EXTRA_TASK_STRUCT];
+static int task_struct_stack_ptr = -1;
+
+struct task_struct * alloc_task_struct(void)
+{
+	int index;
+	struct task_struct *ret;
+
+	index = task_struct_stack_ptr;
+	if (index >= EXTRA_TASK_STRUCT/2)
+		goto use_cache;
+	ret = (struct task_struct *) __get_free_pages(GFP_KERNEL,1);
+	if (!ret) {
+		index = task_struct_stack_ptr;
+		if (index >= 0) {
+use_cache:
+			ret = task_struct_stack[index];
+			task_struct_stack_ptr = index-1;
+		}
+	}
+	return ret;
+}
+
+void free_task_struct(struct task_struct *p)
+{
+	int index = task_struct_stack_ptr+1;
+
+	if (index < EXTRA_TASK_STRUCT) {
+		task_struct_stack[index] = p;
+		task_struct_stack_ptr = index;
+	} else
+		free_pages((unsigned long) p, 1);
+}
+
 void release_segments(struct mm_struct *mm)
 {
-	void * ldt;
+	void * ldt = mm->segments;
+	int nr;
 
 	/* forget local segments */
 	__asm__ __volatile__("movl %w0,%%fs ; movl %w0,%%gs ; lldt %w0"
 		: /* no outputs */
 		: "r" (0));
 	current->tss.ldt = 0;
+	/*
+	 * Set the GDT entry back to the default.
+	 */
+	nr = current->tarray_ptr - &task[0];
+	set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY, &default_ldt, 1);
 
-	ldt = mm->segments;
 	if (ldt) {
 		mm->segments = NULL;
 		vfree(ldt);
@@ -447,9 +498,7 @@
  */
 void exit_thread(void)
 {
-	/* forget lazy i387 state */
-	if (last_task_used_math == current)
-		last_task_used_math = NULL;
+	/* nothing to do ... */
 }
 
 void flush_thread(void)
@@ -462,71 +511,104 @@
 	/*
 	 * Forget coprocessor state..
 	 */
-#ifdef __SMP__
 	if (current->flags & PF_USEDFPU) {
+		current->flags &= ~PF_USEDFPU;
 		stts();
 	}
-#else
-	if (last_task_used_math == current) {
-		last_task_used_math = NULL;
-		stts();
-	}
-#endif
 	current->used_math = 0;
-	current->flags &= ~PF_USEDFPU;
 }
 
 void release_thread(struct task_struct *dead_task)
 {
 }
 
+static inline void unlazy_fpu(struct task_struct *tsk)
+{
+	if (tsk->flags & PF_USEDFPU) {
+		tsk->flags &= ~PF_USEDFPU;
+		__asm__("fnsave %0":"=m" (tsk->tss.i387));
+		stts();
+		asm volatile("fwait");
+	}
+}
+
+/*
+ * If new_mm is NULL, we're being called to set up the LDT descriptor
+ * for a clone task. Each clone must have a separate entry in the GDT.
+ */
 void copy_segments(int nr, struct task_struct *p, struct mm_struct *new_mm)
 {
-	int ldt_size = 1;
-	void * ldt = &default_ldt;
 	struct mm_struct * old_mm = current->mm;
+	void * old_ldt = old_mm->segments, * ldt = old_ldt;
+	int ldt_size = LDT_ENTRIES;
 
 	p->tss.ldt = _LDT(nr);
-	if (old_mm->segments) {
-		new_mm->segments = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
-		if (new_mm->segments) {
-			ldt = new_mm->segments;
-			ldt_size = LDT_ENTRIES;
-			memcpy(ldt, old_mm->segments, LDT_ENTRIES*LDT_ENTRY_SIZE);
+	if (old_ldt) {
+		if (new_mm) {
+			ldt = vmalloc(LDT_ENTRIES*LDT_ENTRY_SIZE);
+			new_mm->segments = ldt;
+			if (!ldt) {
+				printk(KERN_WARNING "ldt allocation failed\n");
+				goto no_ldt;
+			}
+			memcpy(ldt, old_ldt, LDT_ENTRIES*LDT_ENTRY_SIZE);
 		}
+	} else {
+	no_ldt:
+		ldt = &default_ldt;
+		ldt_size = 1;
 	}
 	set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY, ldt, ldt_size);
 }
 
+/*
+ * Save a segment.
+ */
+#define savesegment(seg,value) \
+	asm volatile("movl %%" #seg ",%0":"=m" (*(int *)&(value)))
+
+/*
+ * Load a segment. Fall back on loading the zero
+ * segment if something goes wrong..
+ */
+#define loadsegment(seg,value)			\
+	asm volatile("\n"			\
+		"1:\t"				\
+		"movl %0,%%" #seg "\n"		\
+		"2:\n"				\
+		".section fixup,\"ax\"\n"	\
+		"3:\t"				\
+		"pushl $0\n\t"			\
+		"popl %%" #seg "\n\t"		\
+		"jmp 2b\n"			\
+		".previous\n"			\
+		".section __ex_table,\"a\"\n\t"	\
+		".align 4\n\t"			\
+		".long 1b,3b\n"			\
+		".previous"			\
+		: :"m" (*(unsigned int *)&(value)))
+
 int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
 	struct task_struct * p, struct pt_regs * regs)
 {
 	struct pt_regs * childregs;
 
-	p->tss.tr = _TSS(nr);
-	p->tss.es = __KERNEL_DS;
-	p->tss.cs = __KERNEL_CS;
-	p->tss.ss = __KERNEL_DS;
-	p->tss.ds = __KERNEL_DS;
-	p->tss.fs = __USER_DS;
-	p->tss.gs = __USER_DS;
-	p->tss.ss0 = __KERNEL_DS;
-	p->tss.esp0 = 2*PAGE_SIZE + (unsigned long) p;
-	childregs = ((struct pt_regs *) (p->tss.esp0)) - 1;
-	p->tss.esp = (unsigned long) childregs;
-#ifdef __SMP__
-	p->tss.eip = (unsigned long) ret_from_smpfork;
-	p->tss.eflags = regs->eflags & 0xffffcdff;  /* iopl always 0 for a new process */
-#else
-	p->tss.eip = (unsigned long) ret_from_sys_call;
-	p->tss.eflags = regs->eflags & 0xffffcfff;  /* iopl always 0 for a new process */
-#endif
-	p->tss.ebx = (unsigned long) p;
+	childregs = ((struct pt_regs *) (2*PAGE_SIZE + (unsigned long) p)) - 1;
 	*childregs = *regs;
 	childregs->eax = 0;
 	childregs->esp = esp;
-	p->tss.back_link = 0;
+	childregs->eflags = regs->eflags & 0xffffcfff;  /* iopl always 0 for a new process */	
+
+	p->tss.esp = (unsigned long) childregs;
+	p->tss.esp0 = (unsigned long) (childregs+1);
+	p->tss.ss0 = __KERNEL_DS;
+
+	p->tss.tr = _TSS(nr);
 	set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
+	p->tss.eip = (unsigned long) ret_from_fork;
+
+	savesegment(fs,p->tss.fs);
+	savesegment(gs,p->tss.gs);
 
 	/*
 	 * a bitmap offset pointing outside of the TSS limit causes a nicely
@@ -535,12 +617,8 @@
 	 */
 	p->tss.bitmap = sizeof(struct thread_struct);
 
-#ifdef __SMP__
-	if (current->flags & PF_USEDFPU)
-#else
-	if (last_task_used_math == current)
-#endif
-		__asm__("clts ; fnsave %0 ; frstor %0":"=m" (p->tss.i387));
+	unlazy_fpu(current);
+	p->tss.i387 = current->tss.i387;
 
 	return 0;
 }
@@ -552,16 +630,10 @@
 {
 	int fpvalid;
 
-	if ((fpvalid = current->used_math) != 0) {
-		if (boot_cpu_data.hard_math) {
-			if (last_task_used_math == current) {
-				__asm__("clts ; fsave %0; fwait": :"m" (*fpu));
-			}
-			else
-				memcpy(fpu,&current->tss.i387.hard,sizeof(*fpu));
-		} else {
-			memcpy(fpu,&current->tss.i387.hard,sizeof(*fpu));
-		}
+	fpvalid = current->used_math;
+	if (fpvalid) {
+		unlazy_fpu(current);
+		memcpy(fpu,&current->tss.i387.hard,sizeof(*fpu));
 	}
 
 	return fpvalid;
@@ -597,8 +669,8 @@
 	dump->regs.eax = regs->eax;
 	dump->regs.ds = regs->xds;
 	dump->regs.es = regs->xes;
-	__asm__("movl %%fs,%0":"=r" (dump->regs.fs));
-	__asm__("movl %%gs,%0":"=r" (dump->regs.gs));
+	savesegment(fs,dump->regs.fs);
+	savesegment(gs,dump->regs.gs);
 	dump->regs.orig_eax = regs->orig_eax;
 	dump->regs.eip = regs->eip;
 	dump->regs.cs = regs->xcs;
@@ -607,6 +679,90 @@
 	dump->regs.ss = regs->xss;
 
 	dump->u_fpvalid = dump_fpu (regs, &dump->i387);
+}
+
+/*
+ * This special macro can be used to load a debugging register
+ */
+#define loaddebug(tsk,register) \
+		__asm__("movl %0,%%db" #register  \
+			: /* no output */ \
+			:"r" (tsk->debugreg[register]))
+
+
+/*
+ *	switch_to(x,yn) should switch tasks from x to y.
+ *
+ * We fsave/fwait so that an exception goes off at the right time
+ * (as a call from the fsave or fwait in effect) rather than to
+ * the wrong process. Lazy FP saving no longer makes any sense
+ * with modern CPU's, and this simplifies a lot of things (SMP
+ * and UP become the same).
+ *
+ * NOTE! We used to use the x86 hardware context switching. The
+ * reason for not using it any more becomes apparent when you
+ * try to recover gracefully from saved state that is no longer
+ * valid (stale segment register values in particular). With the
+ * hardware task-switch, there is no way to fix up bad state in
+ * a reasonable manner.
+ *
+ * The fact that Intel documents the hardware task-switching to
+ * be slow is a fairly red herring - this code is not noticeably
+ * faster. However, there _is_ some room for improvement here,
+ * so the performance issues may eventually be a valid point.
+ * More important, however, is the fact that this allows us much
+ * more flexibility.
+ */
+void __switch_to(struct task_struct *prev, struct task_struct *next)
+{
+	/* Do the FPU save and set TS if it wasn't set before.. */
+	unlazy_fpu(prev);
+
+	/*
+	 * Reload TR, LDT and the page table pointers..
+	 *
+	 * We need TR for the IO permission bitmask (and
+	 * the vm86 bitmasks in case we ever use enhanced
+	 * v86 mode properly).
+	 *
+	 * We may want to get rid of the TR register some
+	 * day, and copy the bitmaps around by hand. Oh,
+	 * well. In the meantime we have to clear the busy
+	 * bit in the TSS entry, ugh.
+	 */
+	gdt_table[next->tss.tr >> 3].b &= 0xfffffdff;
+	asm volatile("ltr %0": :"g" (*(unsigned short *)&next->tss.tr));
+
+	/* Re-load LDT if necessary */
+	if (next->mm->segments != prev->mm->segments)
+		asm volatile("lldt %0": :"g" (*(unsigned short *)&next->tss.ldt));
+
+	/* Re-load page tables */
+	if (next->tss.cr3 != prev->tss.cr3)
+		asm volatile("movl %0,%%cr3": :"r" (next->tss.cr3));
+
+	/*
+	 * Save away %fs and %gs. No need to save %es and %ds, as
+	 * those are always kernel segments while inside the kernel.
+	 * Restore the new values.
+	 */
+	asm volatile("movl %%fs,%0":"=m" (*(int *)&prev->tss.fs));
+	asm volatile("movl %%gs,%0":"=m" (*(int *)&prev->tss.gs));
+
+	loadsegment(fs,next->tss.fs);
+	loadsegment(gs,next->tss.gs);
+
+	/*
+	 * Now maybe reload the debug registers
+	 */
+	if (next->debugreg[7]){
+		loaddebug(next,0);
+		loaddebug(next,1);
+		loaddebug(next,2);
+		loaddebug(next,3);
+		loaddebug(next,6);
+		loaddebug(next,7);
+	}
 }
 
 asmlinkage int sys_fork(struct pt_regs regs)
diff -u --recursive --new-file v2.1.108/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.1.108/linux/arch/i386/kernel/ptrace.c	Thu May  7 22:51:46 1998
+++ linux/arch/i386/kernel/ptrace.c	Wed Jul  8 11:38:05 1998
@@ -624,14 +624,8 @@
 #ifdef CONFIG_MATH_EMULATION
 			if ( boot_cpu_data.hard_math ) {
 #endif
-			  if (last_task_used_math == child) {
-			    clts();
-			    __asm__("fnsave %0; fwait":"=m" (child->tss.i387.hard));
-			    last_task_used_math = NULL;
-			    stts();
-			  }
-			  __copy_to_user((void *)data, &child->tss.i387.hard,
-					 sizeof(struct user_i387_struct));
+				__copy_to_user((void *)data, &child->tss.i387.hard,
+						sizeof(struct user_i387_struct));
 #ifdef CONFIG_MATH_EMULATION
 			} else {
 			  save_i387_soft(&child->tss.i387.soft,
@@ -652,13 +646,10 @@
 #ifdef CONFIG_MATH_EMULATION
 			if ( boot_cpu_data.hard_math ) {
 #endif
-			  if (last_task_used_math == child) {
-			    /* Discard the state of the FPU */
-			    last_task_used_math = NULL;
-			  }
 			  __copy_from_user(&child->tss.i387.hard, (void *)data,
 					   sizeof(struct user_i387_struct));
 			  child->flags &= ~PF_USEDFPU;
+			  stts();
 #ifdef CONFIG_MATH_EMULATION
 			} else {
 			  restore_i387_soft(&child->tss.i387.soft,
diff -u --recursive --new-file v2.1.108/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.1.108/linux/arch/i386/kernel/setup.c	Wed Jul  1 19:38:52 1998
+++ linux/arch/i386/kernel/setup.c	Wed Jul 15 15:17:00 1998
@@ -32,6 +32,7 @@
 #ifdef CONFIG_BLK_DEV_RAM
 #include <linux/blk.h>
 #endif
+#include <asm/processor.h>
 #include <linux/console.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -43,6 +44,7 @@
 
 char ignore_irq13 = 0;		/* set if exception 16 works */
 struct cpuinfo_x86 boot_cpu_data = { 0, 0, 0, 0, -1, 1, 0, 0, -1 };
+static char Cx86_step[8];       /* decoded Cyrix step number */
 
 /*
  * Bus types ..
@@ -173,7 +175,7 @@
 			if (to != command_line) to--;
 			if (!memcmp(from+4, "nopentium", 9)) {
 				from += 9+4;
-				boot_cpu_data.x86_capability &= ~8;
+				boot_cpu_data.x86_capability &= ~X86_FEATURE_PSE;
 			} else {
 				memory_end = simple_strtoul(from+4, &from, 0);
 				if ( *from == 'K' || *from == 'k' ) {
@@ -219,83 +221,14 @@
 	request_region(0xf0,0x10,"fpu");
 
 #ifdef CONFIG_VT
-#ifdef CONFIG_FB
-	conswitchp = &fb_con;
-#else
+#if defined(CONFIG_VGA_CONSOLE)
 	conswitchp = &vga_con;
+#elif defined(CONFIG_DUMMY_CONSOLE)
+	conswitchp = &dummy_con;
 #endif
 #endif
 }
 
-/*
- *	Detection of CPU model.
- */
-
-extern inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx)
-{
-	__asm__("cpuid"
-		: "=a" (*eax),
-		  "=b" (*ebx),
-		  "=c" (*ecx),
-		  "=d" (*edx)
-		: "a" (op)
-		: "cc");
-}
-
-__initfunc(static int cyrix_model(struct cpuinfo_x86 *c))
-{
-	int nr = c->x86_model;
-	char *buf = c->x86_model_id;
-
-	/* Cyrix claims they have a TSC, but it is broken */
-	c->x86_capability &= ~16;
-
-	/* Note that some of the possibilities this decoding allows
-	 * have never actually been manufactured - but those that
-	 * do actually exist are correctly decoded.
-	 */
-	if (nr < 0x20) {
-		strcpy(buf, "Cx486");
-		if (!(nr & 0x10)) {
-			sprintf(buf+5, "%c%s%c",
-				(nr & 0x01) ? 'D' : 'S',
-				(nr & 0x04) ? "Rx" : "LC",
-				(nr & 0x02) ? '2' : '\000');
-		} else if (!(nr & 0x08)) {
-			sprintf(buf+5, "S%s%c",
-				(nr & 0x01) ? "2" : "",
-				(nr & 0x02) ? 'e' : '\000');
-		} else {
-			sprintf(buf+5, "DX%c",
-				nr == 0x1b ? '2'
-					: (nr == 0x1f ? '4' : '\000'));
-		}
-	} else if (nr >= 0x20 && nr <= 0x4f) {	/* 5x86, 6x86 or Gx86 */
-		char *s = "";
-		if (nr >= 0x30 && nr < 0x40) {	/* 6x86 */
-			if (c->x86 == 5 && (c->x86_capability & (1 << 8)))
-				s = "L";	/* 6x86L */
-			else if (c->x86 == 6)
-				s = "MX";	/* 6x86MX */
-			}
-		sprintf(buf, "%cx86%s %cx Core/Bus Clock",
-			"??56G"[nr>>4],
-			s,
-			"12??43"[nr & 0x05]);
-	} else if (nr >= 0x50 && nr <= 0x5f) {	/* Cyrix 6x86MX */
-		sprintf(buf, "6x86MX %c%sx Core/Bus Clock",
-			"12233445"[nr & 0x07],
-			(nr && !(nr&1)) ? ".5" : "");
-	} else if (nr >= 0xfd && c->cpuid_level < 0) {
-		/* Probably 0xfd (Cx486[SD]LC with no ID register)
-		 * or 0xfe (Cx486 A step with no ID register).
-		 */
-		strcpy(buf, "Cx486");
-	} else
-		return 0;	/* Use CPUID if applicable */
-	return 1;
-}
-
 __initfunc(static int amd_model(struct cpuinfo_x86 *c))
 {
 	unsigned int n, dummy, *v;
@@ -315,6 +248,153 @@
 	return 1;
 }
 
+/*
+ * Use the Cyrix DEVID CPU registers if avail. to get more detailed info.
+ */
+__initfunc(static void do_cyrix_devid(struct cpuinfo_x86 *c))
+{
+	unsigned char ccr2, ccr3;
+
+	/* we test for DEVID by checking whether CCR3 is writable */
+	cli();
+	ccr3 = getCx86(CX86_CCR3);
+	setCx86(CX86_CCR3, ccr3 ^ 0x80);
+	getCx86(0xc0);   /* dummy to change bus */
+
+	if (getCx86(CX86_CCR3) == ccr3) {       /* no DEVID regs. */
+		ccr2 = getCx86(CX86_CCR2);
+		setCx86(CX86_CCR2, ccr2 ^ 0x04);
+		getCx86(0xc0);  /* dummy */
+
+		if (getCx86(CX86_CCR2) == ccr2) /* old Cx486SLC/DLC */
+			c->x86_model = 0xfd;
+		else {                          /* Cx486S A step */
+			setCx86(CX86_CCR2, ccr2);
+			c->x86_model = 0xfe;
+		}
+	}
+	else {
+		setCx86(CX86_CCR3, ccr3);  /* restore CCR3 */
+
+		/* read DIR0 and DIR1 CPU registers */
+		c->x86_model = getCx86(CX86_DIR0);
+		c->x86_mask = getCx86(CX86_DIR1);
+	}
+	sti();
+}
+
+static char Cx86_model[][9] __initdata = {
+	"Cx486", "Cx486", "5x86 ", "6x86", "MediaGX ", "6x86MX ",
+	"M II ", "Unknown"
+};
+static char Cx486_name[][5] __initdata = {
+	"SLC", "DLC", "SLC2", "DLC2", "SRx", "DRx",
+	"SRx2", "DRx2"
+};
+static char Cx486S_name[][4] __initdata = {
+	"S", "S2", "Se", "S2e"
+};
+static char Cx486D_name[][4] __initdata = {
+	"DX", "DX2", "?", "?", "?", "DX4"
+};
+static char Cx86_cb[] __initdata = "?.5x Core/Bus Clock";
+static char cyrix_model_mult1[] __initdata = "12??43";
+static char cyrix_model_mult2[] __initdata = "12233445";
+static char cyrix_model_oldstep[] __initdata = "A step";
+
+__initfunc(static void cyrix_model(struct cpuinfo_x86 *c))
+{
+	unsigned char dir0_msn, dir0_lsn, dir1;
+	char *buf = c->x86_model_id;
+	const char *p = NULL;
+
+	do_cyrix_devid(c);
+
+	dir0_msn = c->x86_model >> 4;
+	dir0_lsn = c->x86_model & 0xf;
+	dir1 = c->x86_mask;
+
+	/* common case stepping number -- exceptions handled below */
+	sprintf(Cx86_step, "%d.%d", (dir1 >> 4) + 1, dir1 & 0x0f);
+
+	/* Now cook; the original recipe is by Channing Corn, from Cyrix.
+	 * We do the same thing for each generation: we work out
+	 * the model, multiplier and stepping.
+	 */
+	switch (dir0_msn) {
+		unsigned char tmp;
+
+	case 0: /* Cx486SLC/DLC/SRx/DRx */
+		p = Cx486_name[dir0_lsn & 7];
+		break;
+
+	case 1: /* Cx486S/DX/DX2/DX4 */
+		p = (dir0_lsn & 8) ? Cx486D_name[dir0_lsn & 5]
+			: Cx486S_name[dir0_lsn & 3];
+		break;
+
+	case 2: /* 5x86 */
+		Cx86_cb[2] = cyrix_model_mult1[dir0_lsn & 5];
+		p = Cx86_cb+2;
+		break;
+
+	case 3: /* 6x86/6x86L */
+		Cx86_cb[1] = ' ';
+		Cx86_cb[2] = cyrix_model_mult1[dir0_lsn & 5];
+		if (dir1 > 0x21) { /* 686L */
+			Cx86_cb[0] = 'L';
+			p = Cx86_cb;
+			Cx86_step[0]++;
+		} else             /* 686 */
+			p = Cx86_cb+1;
+		break;
+
+	case 4: /* MediaGX/GXm */
+		/* GXm supports extended cpuid levels 'ala' AMD */
+		if (c->cpuid_level == 2) {
+			amd_model(c);  /* get CPU marketing name */
+			return;
+		}
+		else {  /* MediaGX */
+			Cx86_cb[2] = (dir0_lsn & 1) ? '3' : '4';
+			p = Cx86_cb+2;
+			Cx86_step[0] = (dir1 & 0x20) ? '1' : '2';
+		}
+		break;
+
+        case 5: /* 6x86MX/M II */
+		/* the TSC is broken (for now) */
+		c->x86_capability &= ~16;
+
+		if (dir1 > 7) dir0_msn++;  /* M II */
+		tmp = (!(dir0_lsn & 7) || dir0_lsn & 1) ? 2 : 0;
+		Cx86_cb[tmp] = cyrix_model_mult2[dir0_lsn & 7];
+		p = Cx86_cb+tmp;
+        	if (((dir1 & 0x0f) > 4) || ((dir1 & 0xf0) == 0x20))
+			Cx86_step[0]++;
+		break;
+
+	case 0xf:  /* Cyrix 486 without DIR registers */
+		switch (dir0_lsn) {
+		case 0xd:  /* either a 486SLC or DLC w/o DEVID */
+			dir0_msn = 0;
+			p = Cx486_name[(c->hard_math) ? 1 : 0];
+			break;
+
+		case 0xe:  /* a 486S A step */
+			dir0_msn = 0;
+			p = Cx486S_name[0];
+			strcpy(Cx86_step, cyrix_model_oldstep);
+			c->x86_mask = 1; /* must != 0 to print */
+			break;
+		break;
+		}
+	}
+	strcpy(buf, Cx86_model[dir0_msn & 7]);
+	if (p) strcat(buf, p);
+	return;
+}
+
 __initfunc(void get_cpu_vendor(struct cpuinfo_x86 *c))
 {
 	char *v = c->x86_vendor_id;
@@ -323,7 +403,7 @@
 		c->x86_vendor = X86_VENDOR_INTEL;
 	else if (!strcmp(v, "AuthenticAMD"))
 		c->x86_vendor = X86_VENDOR_AMD;
-	else if (!strncmp(v, "Cyrix", 5))
+	else if (!strcmp(v, "CyrixInstead"))
 		c->x86_vendor = X86_VENDOR_CYRIX;
 	else if (!strcmp(v, "UMC UMC UMC "))
 		c->x86_vendor = X86_VENDOR_UMC;
@@ -355,15 +435,6 @@
 	  { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)", 
 	    NULL, "Pentium II (Deschutes)", NULL, NULL, NULL, NULL, NULL, NULL,
 	    NULL, NULL, NULL, NULL }},
-	{ X86_VENDOR_CYRIX,	4,
-	  { NULL, NULL, NULL, NULL, "MediaGX", NULL, NULL, NULL, NULL, "5x86",
-	    NULL, NULL, NULL, NULL, NULL, NULL }},
-	{ X86_VENDOR_CYRIX,	5,
-	  { NULL, NULL, "6x86", NULL, "GXm", NULL, NULL, NULL, NULL,
-	    NULL, NULL, NULL, NULL, NULL, NULL, NULL }},
-	{ X86_VENDOR_CYRIX,	6,
-	  { "6x86MX", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
-	    NULL, NULL, NULL, NULL, NULL, NULL }},
 	{ X86_VENDOR_AMD,	4,
 	  { NULL, NULL, NULL, "DX/2", NULL, NULL, NULL, "DX/2-WB", "DX/4",
 	    "DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", "Am5x86-WB" }},
@@ -396,8 +467,10 @@
 	    c->cpuid_level < 0)
 		return;
 
-	if (c->x86_vendor == X86_VENDOR_CYRIX && cyrix_model(c))
+	if (c->x86_vendor == X86_VENDOR_CYRIX) {
+		cyrix_model(c);
 		return;
+	}
 
 	if (c->x86_model < 16)
 		for (i=0; i<sizeof(cpu_models)/sizeof(struct cpu_model_info); i++)
@@ -437,9 +510,12 @@
 	else
 		printk("%s", c->x86_model_id);
 
-	if (c->x86_mask)
-		printk(" stepping %02x", c->x86_mask);
-
+	if (c->x86_mask) {
+		if (c->x86_vendor == X86_VENDOR_CYRIX)
+			printk(" stepping %s", Cx86_step);
+		else
+			printk(" stepping %02x", c->x86_mask);
+	}
 	printk("\n");
 }
 
@@ -451,12 +527,12 @@
 {
 	char *p = buffer;
 	int sep_bug;
-        static char *x86_cap_flags[] = {
-                "fpu", "vme", "de", "pse", "tsc", "msr", "6", "mce",
-                "cx8", "9", "10", "sep", "12", "pge", "14", "cmov",
-                "16", "17", "18", "19", "20", "21", "22", "mmx",
-                "24", "25", "26", "27", "28", "29", "30", "31"
-        };
+	static char *x86_cap_flags[] = {
+	        "fpu", "vme", "de", "pse", "tsc", "msr", "6", "mce",
+	        "cx8", "9", "10", "sep", "12", "pge", "14", "cmov",
+	        "16", "17", "18", "19", "20", "21", "22", "mmx",
+	        "24", "25", "26", "27", "28", "29", "30", "31"
+	};
 	struct cpuinfo_x86 *c = cpu_data;
 	int i, n;
 
@@ -475,9 +551,7 @@
 			       c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown");
 		if (c->x86_mask) {
 			if (c->x86_vendor == X86_VENDOR_CYRIX)
-				p += sprintf(p, "stepping\t: %d rev %d\n",
-					     c->x86_mask >> 4,
-					     c->x86_mask & 0x0f);
+				p += sprintf(p, "stepping\t: %s\n", Cx86_step);
 			else
 				p += sprintf(p, "stepping\t: %d\n", c->x86_mask);
 		} else
@@ -496,7 +570,7 @@
 				x86_cap_flags[12] = "mtrr";
 				x86_cap_flags[14] = "mca";
 				x86_cap_flags[16] = "pat";
-				x86_cap_flags[17] = "pse";
+				x86_cap_flags[17] = "pse36";
 				x86_cap_flags[24] = "osfxsr";
 			}
 		}
@@ -504,10 +578,10 @@
 		sep_bug = c->x86_vendor == X86_VENDOR_INTEL &&
 			  c->x86 == 0x06 &&
 			  c->cpuid_level >= 0 &&
-			  (c->x86_capability & 0x800) &&
+			  (c->x86_capability & X86_FEATURE_SEP) &&
 			  c->x86_model < 3 &&
 			  c->x86_mask < 3;
-        
+	
 		p += sprintf(p, "fdiv_bug\t: %s\n"
 			        "hlt_bug\t\t: %s\n"
 			        "sep_bug\t\t: %s\n"
@@ -533,5 +607,5 @@
 			     (c->loops_per_sec+2500)/500000,
 			     ((c->loops_per_sec+2500)/5000) % 100);
 	}
-        return p - buffer;
+	return p - buffer;
 }
diff -u --recursive --new-file v2.1.108/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v2.1.108/linux/arch/i386/kernel/signal.c	Tue Jun 23 10:01:19 1998
+++ linux/arch/i386/kernel/signal.c	Wed Jul  8 11:39:23 1998
@@ -153,17 +153,10 @@
 
 static inline int restore_i387_hard(struct _fpstate *buf)
 {
-#ifdef __SMP__
 	if (current->flags & PF_USEDFPU) {
+		current->flags &= ~PF_USEDFPU;
 		stts();
 	}
-#else
-	if (current == last_task_used_math) {
-		last_task_used_math = NULL;
-		stts();
-	}
-#endif
-	current->flags &= ~PF_USEDFPU;
 	return __copy_from_user(&current->tss.i387.hard, buf, sizeof(*buf));
 }
 
@@ -315,20 +308,12 @@
 
 static inline int save_i387_hard(struct _fpstate * buf)
 {
-#ifdef __SMP__
 	if (current->flags & PF_USEDFPU) {
-		__asm__ __volatile__("fnsave %0":"=m"(current->tss.i387.hard));
-		stts();
 		current->flags &= ~PF_USEDFPU;
-	}
-#else
-	if (current == last_task_used_math) {
 		__asm__ __volatile__("fnsave %0":"=m"(current->tss.i387.hard));
-		last_task_used_math = NULL;
-		__asm__ __volatile__("fwait");	/* not needed on 486+ */
 		stts();
 	}
-#endif
+	asm volatile("fwait");
 	current->tss.i387.hard.status = current->tss.i387.hard.swd;
 	if (__copy_to_user(buf, &current->tss.i387.hard, sizeof(*buf)))
 		return -1;
diff -u --recursive --new-file v2.1.108/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- v2.1.108/linux/arch/i386/kernel/time.c	Sun Jun  7 11:16:27 1998
+++ linux/arch/i386/kernel/time.c	Fri Jul 10 14:01:13 1998
@@ -25,6 +25,7 @@
 #include <linux/init.h>
 #include <linux/smp.h>
 
+#include <asm/processor.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -534,7 +535,7 @@
  */
 #ifndef CONFIG_APM
 	/* If we have the CPU hardware time counters, use them */
-	if (boot_cpu_data.x86_capability & 16) {
+	if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) { 
 		do_gettimeoffset = do_fast_gettimeoffset;
 		do_get_fast_time = do_x86_get_fast_time;
 
diff -u --recursive --new-file v2.1.108/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.1.108/linux/arch/i386/kernel/traps.c	Wed Jun 24 22:54:03 1998
+++ linux/arch/i386/kernel/traps.c	Thu Jul  9 11:34:33 1998
@@ -66,23 +66,6 @@
 	unlock_kernel(); \
 }
 
-#define get_seg_byte(seg,addr) ({ \
-register unsigned char __res; \
-__asm__("pushl %%fs;movl %%ax,%%fs;movb %%fs:%2,%%al;popl %%fs" \
-	:"=a" (__res):"0" (seg),"m" (*(addr))); \
-__res;})
-
-#define get_seg_long(seg,addr) ({ \
-register unsigned long __res; \
-__asm__("pushl %%fs;movl %%ax,%%fs;movl %%fs:%2,%%eax;popl %%fs" \
-	:"=a" (__res):"0" (seg),"m" (*(addr))); \
-__res;})
-
-#define _fs() ({ \
-register unsigned short __res; \
-__asm__("movl %%fs,%%ax":"=a" (__res):); \
-__res;})
-
 void page_exception(void);
 
 asmlinkage void divide_error(void);
@@ -118,6 +101,7 @@
 static void show_registers(struct pt_regs *regs)
 {
 	int i;
+	int in_kernel = 1;
 	unsigned long esp;
 	unsigned short ss;
 	unsigned long *stack, addr, module_start, module_end;
@@ -126,6 +110,7 @@
 	esp = (unsigned long) &regs->esp;
 	ss = __KERNEL_DS;
 	if (regs->xcs & 3) {
+		in_kernel = 0;
 		esp = regs->esp;
 		ss = regs->xss & 0xffff;
 	}
@@ -138,53 +123,59 @@
 	printk("ds: %04x   es: %04x   ss: %04x\n",
 		regs->xds & 0xffff, regs->xes & 0xffff, ss);
 	store_TR(i);
-	printk("Process %s (pid: %d, process nr: %d, stackpage=%08lx)\nStack: ",
+	printk("Process %s (pid: %d, process nr: %d, stackpage=%08lx)",
 		current->comm, current->pid, 0xffff & i, 4096+(unsigned long)current);
-	stack = (unsigned long *) esp;
-	for(i=0; i < kstack_depth_to_print; i++) {
-		if (((long) stack & 4095) == 0)
-			break;
-		if (i && ((i % 8) == 0))
-			printk("\n       ");
-		printk("%08lx ", get_seg_long(ss,stack++));
-	}
-	printk("\nCall Trace: ");
-	stack = (unsigned long *) esp;
-	i = 1;
-	module_start = PAGE_OFFSET + (max_mapnr << PAGE_SHIFT);
-	module_start = ((module_start + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1));
-	module_end = module_start + MODULE_RANGE;
-	while (((long) stack & 4095) != 0) {
-		addr = get_seg_long(ss, stack++);
-		/*
-		 * If the address is either in the text segment of the
-		 * kernel, or in the region which contains vmalloc'ed
-		 * memory, it *may* be the address of a calling
-		 * routine; if so, print it so that someone tracing
-		 * down the cause of the crash will be able to figure
-		 * out the call path that was taken.
-		 */
-		if (((addr >= (unsigned long) &_stext) &&
-		     (addr <= (unsigned long) &_etext)) ||
-		    ((addr >= module_start) && (addr <= module_end))) {
+
+	/*
+	 * When in-kernel, we also print out the stack and code at the
+	 * time of the fault..
+	 */
+	if (in_kernel) {
+		printk("\nStack: ");
+		stack = (unsigned long *) esp;
+		for(i=0; i < kstack_depth_to_print; i++) {
+			if (((long) stack & 4095) == 0)
+				break;
 			if (i && ((i % 8) == 0))
 				printk("\n       ");
-			printk("[<%08lx>] ", addr);
-			i++;
+			printk("%08lx ", *stack++);
+		}
+		printk("\nCall Trace: ");
+		stack = (unsigned long *) esp;
+		i = 1;
+		module_start = PAGE_OFFSET + (max_mapnr << PAGE_SHIFT);
+		module_start = ((module_start + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1));
+		module_end = module_start + MODULE_RANGE;
+		while (((long) stack & 4095) != 0) {
+			addr = *stack++;
+			/*
+			 * If the address is either in the text segment of the
+			 * kernel, or in the region which contains vmalloc'ed
+			 * memory, it *may* be the address of a calling
+			 * routine; if so, print it so that someone tracing
+			 * down the cause of the crash will be able to figure
+			 * out the call path that was taken.
+			 */
+			if (((addr >= (unsigned long) &_stext) &&
+			     (addr <= (unsigned long) &_etext)) ||
+			    ((addr >= module_start) && (addr <= module_end))) {
+				if (i && ((i % 8) == 0))
+					printk("\n       ");
+				printk("[<%08lx>] ", addr);
+				i++;
+			}
 		}
+		printk("\nCode: ");
+		for(i=0;i<20;i++)
+			printk("%02x ", ((unsigned char *)regs->eip)[i]);
+		printk("\n");
 	}
-	printk("\nCode: ");
-	for(i=0;i<20;i++)
-		printk("%02x ",0xff & get_seg_byte(regs->xcs & 0xffff,(i+(char *)regs->eip)));
-	printk("\n");
 }	
 
 spinlock_t die_lock;
 
-void die_if_kernel(const char * str, struct pt_regs * regs, long err)
+void die(const char * str, struct pt_regs * regs, long err)
 {
-	if ((regs->eflags & VM_MASK) || (3 & regs->xcs) == 3)
-		return;
 	console_verbose();
 	spin_lock_irq(&die_lock);
 	printk("%s: %04lx\n", str, err & 0xffff);
@@ -193,6 +184,12 @@
 	do_exit(SIGSEGV);
 }
 
+static void die_if_kernel(const char * str, struct pt_regs * regs, long err)
+{
+	if (!(regs->eflags & VM_MASK) && !(3 & regs->xcs))
+		die(str, regs, err);
+}
+
 DO_VM86_ERROR( 0, SIGFPE,  "divide error", divide_error, current)
 DO_VM86_ERROR( 3, SIGTRAP, "int3", int3, current)
 DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow, current)
@@ -200,7 +197,7 @@
 DO_ERROR( 6, SIGILL,  "invalid operand", invalid_op, current)
 DO_VM86_ERROR( 7, SIGSEGV, "device not available", device_not_available, current)
 DO_ERROR( 8, SIGSEGV, "double fault", double_fault, current)
-DO_ERROR( 9, SIGFPE,  "coprocessor segment overrun", coprocessor_segment_overrun, last_task_used_math)
+DO_ERROR( 9, SIGFPE,  "coprocessor segment overrun", coprocessor_segment_overrun, current)
 DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS, current)
 DO_ERROR(11, SIGBUS,  "segment not present", segment_not_present, current)
 DO_ERROR(12, SIGBUS,  "stack segment", stack_segment, current)
@@ -224,17 +221,34 @@
 
 asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
 {
+	if (regs->eflags & VM_MASK)
+		goto gp_in_vm86;
+
+	if (!(regs->xcs & 3))
+		goto gp_in_kernel;
+
 	lock_kernel();
-	if (regs->eflags & VM_MASK) {
-		handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code);
-		goto out;
-	}
-	die_if_kernel("general protection",regs,error_code);
 	current->tss.error_code = error_code;
 	current->tss.trap_no = 13;
 	force_sig(SIGSEGV, current);	
-out:
+	return;
+
+gp_in_vm86:
+	lock_kernel();
+	handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code);
 	unlock_kernel();
+	return;
+
+gp_in_kernel:
+	{
+		unsigned long fixup;
+		fixup = search_exception_table(regs->eip);
+		if (fixup) {
+			regs->eip = fixup;
+			return;
+		}
+		die("general protection fault", regs, error_code);
+	}
 }
 
 static void mem_parity_error(unsigned char reason, struct pt_regs * regs)
@@ -295,9 +309,7 @@
 		__asm__("movl %0,%%db7"
 			: /* no output */
 			: "r" (0));
-		goto out;
 	}
-	die_if_kernel("debug",regs,error_code);
 out:
 	unlock_kernel();
 }
@@ -313,16 +325,7 @@
 
 	lock_kernel();
 	clts();
-#ifdef __SMP__
 	task = current;
-#else
-	task = last_task_used_math;
-	last_task_used_math = NULL;
-	if (!task) {
-		__asm__("fnclex");
-		goto out;
-	}
-#endif
 	/*
 	 *	Save the info for the exception handler
 	 */
@@ -333,9 +336,6 @@
 	force_sig(SIGFPE, task);
 	task->tss.trap_no = 16;
 	task->tss.error_code = 0;
-#ifndef __SMP__
-out:
-#endif
 	unlock_kernel();
 }
 
@@ -373,15 +373,6 @@
  *	case we swap processors. We also don't use the coprocessor
  *	timer - IRQ 13 mode isn't used with SMP machines (thank god).
  */
-#ifndef __SMP__
-	if (last_task_used_math == current)
-		return;
-	if (last_task_used_math)
-		__asm__("fnsave %0":"=m" (last_task_used_math->tss.i387));
-	else
-		__asm__("fnclex");
-	last_task_used_math = current;
-#endif
 
 	if(current->used_math)
 		__asm__("frstor %0": :"m" (current->tss.i387));
diff -u --recursive --new-file v2.1.108/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- v2.1.108/linux/arch/i386/mm/fault.c	Tue Jun 23 10:01:20 1998
+++ linux/arch/i386/mm/fault.c	Tue Jul  7 11:04:58 1998
@@ -22,7 +22,7 @@
 #include <asm/pgtable.h>
 #include <asm/hardirq.h>
 
-extern void die_if_kernel(const char *,struct pt_regs *,long);
+extern void die(const char *,struct pt_regs *,long);
 
 /*
  * Ugly, ugly, but the goto's result in better assembly..
@@ -101,7 +101,7 @@
 	__asm__("movl %%cr2,%0":"=r" (address));
 
 	if (local_irq_count[smp_processor_id()])
-		die_if_kernel("page fault from irq handler",regs,error_code);
+		die("page fault from irq handler",regs,error_code);
 	tsk = current;
 	mm = tsk->mm;
 
@@ -235,7 +235,7 @@
 		printk(KERN_ALERT "*pte = %08lx\n", page);
 	}
 	lock_kernel();
-	die_if_kernel("Oops", regs, error_code);
+	die("Oops", regs, error_code);
 	do_exit(SIGKILL);
 	unlock_kernel();
 }
diff -u --recursive --new-file v2.1.108/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.1.108/linux/arch/i386/mm/init.c	Tue Jun 23 10:01:20 1998
+++ linux/arch/i386/mm/init.c	Fri Jul 10 14:01:13 1998
@@ -22,12 +22,12 @@
 #include <linux/blk.h>
 #endif
 
+#include <asm/processor.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/dma.h>
 
-extern void die_if_kernel(char *,struct pt_regs *,long);
 extern void show_net_buffers(void);
 
 void __bad_pte_kernel(pmd_t *pmd)
@@ -171,23 +171,6 @@
 #define X86_CR4_MCE		0x0040		/* Machine check enable */
 #define X86_CR4_PGE		0x0080		/* enable global pages */
 #define X86_CR4_PCE		0x0100		/* enable performance counters at ipl 3 */
-
-#define X86_FEATURE_FPU		0x0001		/* internal FPU */
-#define X86_FEATURE_VME		0x0002		/* vm86 extensions */
-#define X86_FEATURE_DE		0x0004		/* debugging extensions */
-#define X86_FEATURE_PSE		0x0008		/* Page size extensions */
-#define X86_FEATURE_TSC		0x0010		/* Time stamp counter */
-#define X86_FEATURE_MSR		0x0020		/* RDMSR/WRMSR */
-#define X86_FEATURE_PAE		0x0040		/* Physical address extension */
-#define X86_FEATURE_MCE		0x0080		/* Machine check exception */
-#define X86_FEATURE_CXS		0x0100		/* cmpxchg8 available */
-#define X86_FEATURE_APIC	0x0200		/* internal APIC */
-#define X86_FEATURE_10		0x0400
-#define X86_FEATURE_11		0x0800
-#define X86_FEATURE_MTRR	0x1000		/* memory type registers */
-#define X86_FEATURE_PGE		0x2000		/* Global page */
-#define X86_FEATURE_MCA		0x4000		/* Machine Check Architecture */
-#define X86_FEATURE_CMOV	0x8000		/* Cmov/fcomi */
 
 /*
  * Save the cr4 feature set we're using (ie
diff -u --recursive --new-file v2.1.108/linux/arch/m68k/amiga/config.c linux/arch/m68k/amiga/config.c
--- v2.1.108/linux/arch/m68k/amiga/config.c	Fri May  8 23:14:42 1998
+++ linux/arch/m68k/amiga/config.c	Fri Jul 10 15:18:30 1998
@@ -375,7 +375,9 @@
   mach_floppy_setup    = amiga_floppy_setup;
 #endif
   mach_reset           = amiga_reset;
-  conswitchp           = &fb_con;
+#ifdef CONFIG_DUMMY_CONSOLE  
+  conswitchp           = &dummy_con;
+#endif
   kd_mksound           = amiga_mksound;
 #ifdef CONFIG_MAGIC_SYSRQ
   mach_sysrq_key = 0x5f;	     /* HELP */
diff -u --recursive --new-file v2.1.108/linux/arch/m68k/apollo/config.c linux/arch/m68k/apollo/config.c
--- v2.1.108/linux/arch/m68k/apollo/config.c	Fri May  8 23:14:42 1998
+++ linux/arch/m68k/apollo/config.c	Fri Jul 10 15:18:30 1998
@@ -109,7 +109,9 @@
 	mach_floppy_setup    = dn_dummy_floppy_setup;
 #endif
 	mach_reset	     = dn_dummy_reset;  /* */
-	conswitchp	     = &fb_con;
+#ifdef CONFIG_DUMMY_CONSOLE
+	conswitchp	     = &dummy_con;
+#endif
 #if 0
 	mach_fb_init 	     = dn_fb_init; 
 	mach_video_setup     = dn_dummy_video_setup; 
diff -u --recursive --new-file v2.1.108/linux/arch/m68k/atari/config.c linux/arch/m68k/atari/config.c
--- v2.1.108/linux/arch/m68k/atari/config.c	Tue Jun 23 10:01:20 1998
+++ linux/arch/m68k/atari/config.c	Fri Jul 10 15:18:30 1998
@@ -262,7 +262,9 @@
 #ifdef CONFIG_ATARI_FLOPPY
     mach_floppy_setup	 = atari_floppy_setup;
 #endif
-    conswitchp	         = &fb_con;
+#ifdef CONFIG_DUMMY_CONSOLE
+    conswitchp	         = &dummy_con;
+#endif
     mach_max_dma_address = 0xffffff;
     kd_mksound		 = atari_mksound;
 #ifdef CONFIG_MAGIC_SYSRQ
diff -u --recursive --new-file v2.1.108/linux/arch/m68k/hp300/config.c linux/arch/m68k/hp300/config.c
--- v2.1.108/linux/arch/m68k/hp300/config.c	Tue Jun 23 10:01:20 1998
+++ linux/arch/m68k/hp300/config.c	Fri Jul 10 15:18:30 1998
@@ -66,8 +66,8 @@
 #ifdef CONFIG_HEARTBEAT
   mach_heartbeat       = hp300_pulse;
 #endif
-#ifdef CONFIG_FB
-  conswitchp	       = &fb_con;
+#ifdef CONFIG_DUMMY_CONSOLE
+  conswitchp	       = &dummy_con;
 #endif
   mach_max_dma_address = 0xffffffff;
 }
diff -u --recursive --new-file v2.1.108/linux/arch/m68k/mac/config.c linux/arch/m68k/mac/config.c
--- v2.1.108/linux/arch/m68k/mac/config.c	Tue Jun 23 10:01:21 1998
+++ linux/arch/m68k/mac/config.c	Fri Jul 10 15:18:30 1998
@@ -274,7 +274,7 @@
     mach_mksound         = mac_mksound;
 #endif
     mach_reset           = mac_reset;
-    conswitchp	         = &fb_con;
+    conswitchp	         = &dummy_con;
     mach_max_dma_address = 0xffffffff;
 #if 0
     mach_debug_init	 = mac_debug_init;
diff -u --recursive --new-file v2.1.108/linux/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c
--- v2.1.108/linux/arch/ppc/kernel/chrp_setup.c	Wed Jun 24 22:54:04 1998
+++ linux/arch/ppc/kernel/chrp_setup.c	Fri Jul 10 15:18:30 1998
@@ -258,9 +258,8 @@
 	 */
 	sio_init();
 
-#ifdef CONFIG_FB
-	/* Frame buffer device based console */
-	conswitchp = &fb_con;
+#ifdef CONFIG_DUMMY_CONSOLE
+	conswitchp = &dummy_con;
 #endif
 #ifdef CONFIG_ABSCON_COMPAT
 	/* Console wrapper */
diff -u --recursive --new-file v2.1.108/linux/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c
--- v2.1.108/linux/arch/ppc/kernel/pmac_setup.c	Wed Jun 24 22:54:04 1998
+++ linux/arch/ppc/kernel/pmac_setup.c	Fri Jul 10 15:18:30 1998
@@ -137,9 +137,8 @@
 	find_via_cuda();
 	find_via_pmu();
 
-#ifdef CONFIG_FB
-	/* Frame buffer device based console */
-	conswitchp = &fb_con;
+#ifdef CONFIG_DUMMY_CONSOLE
+	conswitchp = &dummy_con;
 #endif
 #ifdef CONFIG_ABSCON_COMPAT
 	/* Console wrapper */
diff -u --recursive --new-file v2.1.108/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.1.108/linux/arch/sparc/config.in	Fri May  8 23:14:45 1998
+++ linux/arch/sparc/config.in	Fri Jul 10 15:18:30 1998
@@ -36,6 +36,13 @@
 else
 	bool 'Support for SUN4 machines (disables SUN4[CDM] support)' CONFIG_SUN4
 
+	mainmenu_option next_comment
+	comment 'Console drivers'
+	bool 'PROM console' CONFIG_PROM_CONSOLE
+	bool 'Support Frame buffer devices' CONFIG_FB
+	source drivers/video/Config.in
+	endmenu
+
 	# Global things across all Sun machines.
 	define_bool CONFIG_SBUS y
 	define_bool CONFIG_SBUSCHAR y
@@ -48,8 +55,10 @@
 	define_bool CONFIG_SUN_AUXIO y
 	define_bool CONFIG_SUN_IO y
 	if [ "$CONFIG_SUN4" = "y" ]; then
-		bool 'Sun FB drivers appear in PROCFS' SUN_FBS_IN_PROCFS
-		bool 'bwtwo support' SUN_FB_BWTWO
+		if [ "$CONFIG_DUMMY_CONSOLE" = "n" ]; then
+			bool 'Sun FB drivers appear in PROCFS' SUN_FBS_IN_PROCFS
+			bool 'bwtwo support' SUN_FB_BWTWO
+		fi
 	else
 		source drivers/sbus/char/Config.in
 		source drivers/sbus/audio/Config.in
diff -u --recursive --new-file v2.1.108/linux/arch/sparc/kernel/setup.c linux/arch/sparc/kernel/setup.c
--- v2.1.108/linux/arch/sparc/kernel/setup.c	Thu Apr 23 20:21:30 1998
+++ linux/arch/sparc/kernel/setup.c	Fri Jul 10 15:18:30 1998
@@ -120,7 +120,6 @@
 #define BOOTME_KGDB   0xc
 
 #ifdef CONFIG_SUN_CONSOLE
-extern char *console_fb_path;
 static int console_fb = 0;
 #endif
 static unsigned long memory_size __initdata = 0;
@@ -220,7 +219,6 @@
 					prom_printf ("Using /dev/ttyb as console.\n");
 				} else {
 					console_fb = 1;
-					console_fb_path = commands;
 				}
 			} else
 #endif
@@ -466,6 +464,13 @@
 	init_task.mm->context = (unsigned long) NO_CONTEXT;
 	init_task.tss.kregs = &fake_swapper_regs;
 
+	if (!serial_console) {
+#ifdef CONFIG_PROM_CONSOLE
+		conswitchp = &prom_con;
+#elif defined(CONFIG_DUMMY_CONSOLE)
+		conswitchp = &dummy_con;
+#endif
+	}
 }
 
 asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on)
diff -u --recursive --new-file v2.1.108/linux/arch/sparc64/config.in linux/arch/sparc64/config.in
--- v2.1.108/linux/arch/sparc64/config.in	Fri May  8 23:14:46 1998
+++ linux/arch/sparc64/config.in	Fri Jul 10 15:18:30 1998
@@ -1,4 +1,4 @@
-# $Id: config.in,v 1.46 1998/05/05 17:17:21 jj Exp $
+# $Id: config.in,v 1.51 1998/07/08 10:21:14 jj Exp $
 # For a description of the syntax of this configuration file,
 # see the Configure script.
 #
@@ -26,6 +26,13 @@
 
 bool 'Support for AP1000 multicomputer' CONFIG_AP1000
 
+mainmenu_option next_comment
+comment 'Console drivers'
+bool 'PROM console' CONFIG_PROM_CONSOLE
+bool 'Support Frame buffer devices' CONFIG_FB
+source drivers/video/Config.in
+endmenu
+
 if [ "$CONFIG_AP1000" = "y" ]; then
 	define_bool CONFIG_NO_KEYBOARD y
 	define_bool CONFIG_APFDDI y
@@ -45,6 +52,7 @@
 	define_bool CONFIG_SUN_AUXIO y
 	define_bool CONFIG_SUN_IO y
 	define_bool CONFIG_PCI y
+	define_bool CONFIG_PCI_CONSOLE y
 	source drivers/sbus/char/Config.in
 	source drivers/sbus/audio/Config.in
 fi
diff -u --recursive --new-file v2.1.108/linux/arch/sparc64/kernel/ebus.c linux/arch/sparc64/kernel/ebus.c
--- v2.1.108/linux/arch/sparc64/kernel/ebus.c	Fri May  8 23:14:46 1998
+++ linux/arch/sparc64/kernel/ebus.c	Fri Jul 10 15:18:30 1998
@@ -361,7 +361,9 @@
 		++num_ebus;
 	}
 
+#ifndef CONFIG_FB
 	pci_console_init();
+#endif
 
 #ifdef CONFIG_SUN_OPENPROMIO
 	openprom_init();
diff -u --recursive --new-file v2.1.108/linux/arch/sparc64/kernel/setup.c linux/arch/sparc64/kernel/setup.c
--- v2.1.108/linux/arch/sparc64/kernel/setup.c	Thu Apr 23 20:21:32 1998
+++ linux/arch/sparc64/kernel/setup.c	Fri Jul 10 15:18:30 1998
@@ -1,4 +1,4 @@
-/*  $Id: setup.c,v 1.20 1998/02/24 17:02:39 jj Exp $
+/*  $Id: setup.c,v 1.26 1998/07/08 10:21:15 jj Exp $
  *  linux/arch/sparc64/kernel/setup.c
  *
  *  Copyright (C) 1995,1996  David S. Miller (davem@caip.rutgers.edu)
@@ -26,6 +26,7 @@
 #include <linux/blk.h>
 #include <linux/init.h>
 #include <linux/inet.h>
+#include <linux/console.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -41,13 +42,15 @@
 #include <net/ipconfig.h>
 #endif
 
+#undef PROM_DEBUG_CONSOLE
+
 struct screen_info screen_info = {
 	0, 0,			/* orig-x, orig-y */
-	{ 0, 0, },		/* unused */
+	0,			/* unused */
 	0,			/* orig-video-page */
 	0,			/* orig-video-mode */
 	128,			/* orig-video-cols */
-	0,0,0,			/* ega_ax, ega_bx, ega_cx */
+	0, 0, 0,		/* unused, ega_bx, unused */
 	54,			/* orig-video-lines */
 	0,                      /* orig-video-isVGA */
 	16                      /* orig-video-points */
@@ -63,7 +66,9 @@
 
 extern unsigned long sparc64_ttable_tl0;
 #if CONFIG_SUN_CONSOLE
-extern void console_restore_palette(void);
+void console_restore_palette(void) {
+/* FIXME */
+}
 #endif
 asmlinkage void sys_sync(void);	/* it's really int */
 
@@ -102,8 +107,7 @@
 #define BOOTME_KGDB   0x4
 
 #ifdef CONFIG_SUN_CONSOLE
-extern char *console_fb_path;
-static int console_fb = 0;
+static int console_fb __initdata = 0;
 #endif
 static unsigned long memory_size = 0;
 
@@ -191,7 +195,6 @@
 					prom_printf ("Using /dev/ttyb as console.\n");
 				} else {
 					console_fb = 1;
-					console_fb_path = commands;
 				}
 			} else
 #endif
@@ -235,21 +238,31 @@
 char saved_command_line[256];
 char reboot_command[256];
 
-unsigned long phys_base;
+extern unsigned long phys_base;
 
 static struct pt_regs fake_swapper_regs = { { 0, }, 0, 0, 0, 0 };
 
-#if 0
-#include <linux/console.h>
+extern struct consw sun_serial_con;
 
-static void prom_cons_write(struct console *con, const char *str, unsigned count)
+#ifdef PROM_DEBUG_CONSOLE
+static void
+prom_console_write(struct console *con, const char *s, unsigned n)
 {
-        while (count--)
-                prom_printf("%c", *str++);
+	prom_printf("%s", s);
 }
 
 static struct console prom_console = {
-        "PROM", prom_cons_write, 0, 0, 0, 0, 0, CON_PRINTBUFFER, 0, 0, 0
+	"prom",
+	prom_console_write,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
 };
 #endif
 
@@ -260,7 +273,7 @@
 	unsigned long lowest_paddr;
 	int total, i;
 
-#if 0
+#ifdef PROM_DEBUG_CONSOLE
 	register_console(&prom_console);
 #endif
 
@@ -396,6 +409,13 @@
 #else
 	serial_console = 0;
 #endif
+	if (!serial_console) {
+#ifdef CONFIG_PROM_CONSOLE
+		conswitchp = &prom_con;
+#elif defined(CONFIG_DUMMY_CONSOLE)	
+		conswitchp = &dummy_con;
+#endif
+	}
 }
 
 asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on)
diff -u --recursive --new-file v2.1.108/linux/drivers/block/ide-pci.c linux/drivers/block/ide-pci.c
--- v2.1.108/linux/drivers/block/ide-pci.c	Wed May 20 19:10:37 1998
+++ linux/drivers/block/ide-pci.c	Sun Jul 12 22:30:50 1998
@@ -314,6 +314,8 @@
 		if (no_autodma)
 			hwif->no_autodma = 1;
 #ifdef CONFIG_BLK_DEV_IDEDMA
+		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_SIS5513))
+			hwif->no_autodma = 1;	/* too many SIS-5513 systems have troubles */
 		if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) ||
 		    ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 0x80))) {
 			unsigned int extra = (!mate && IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246)) ? 16 : 0;
diff -u --recursive --new-file v2.1.108/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.1.108/linux/drivers/char/Makefile	Wed Jun 24 22:54:04 1998
+++ linux/drivers/char/Makefile	Fri Jul 10 15:18:30 1998
@@ -24,8 +24,8 @@
 LX_OBJS  := pty.o
 
 ifdef CONFIG_VT
-L_OBJS += console.o vt.o vc_screen.o consolemap.o consolemap_deftbl.o
-LX_OBJS += selection.o
+L_OBJS += vt.o vc_screen.o consolemap.o consolemap_deftbl.o
+LX_OBJS += console.o selection.o
 endif
 ifdef CONFIG_FB
   LX_OBJS += fbmem.o
diff -u --recursive --new-file v2.1.108/linux/drivers/char/console.c linux/drivers/char/console.c
--- v2.1.108/linux/drivers/char/console.c	Wed Jun 24 22:54:04 1998
+++ linux/drivers/char/console.c	Fri Jul 10 15:31:12 1998
@@ -3,39 +3,8 @@
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
+
 /*
- *	console.c
- *
- * This module exports the console io functions:
- *
- *     'int vc_allocate(unsigned int console)'
- *     'int vc_cons_allocated(unsigned int console)'
- *     'int vc_resize(unsigned long lines, unsigned long cols)'
- *     'int vc_resize_con(unsigned long lines, unsigned long cols,
- *			  unsigned int currcons)'
- *     'void vc_disallocate(unsigned int currcons)'
- *
- *     'unsigned long con_init(unsigned long)'
- *     'int con_open(struct tty_struct *tty, struct file * filp)'
- *     'void con_write(struct tty_struct * tty)'
- *     'void vt_console_print(struct console *co, const char * b,
- *                            unsigned count)'
- *     'void update_screen(int new_console)'
- *
- *     'void do_blank_screen(int)'
- *     'void do_unblank_screen(void)'
- *     'void poke_blanked_console(void)'
- *
- *     'unsigned short *screen_pos(int currcons, int w_offset, int viewed)'
- *     'void complement_pos(int currcons, int offset)'
- *     'void invert_screen(int currcons, int offset, int count, int shift)'
- *
- *     'void scrollback(int lines)'
- *     'void scrollfront(int lines)'
- *
- *     'void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry)'
- *     'int mouse_reporting(void)'
- *
  * Hopefully this will be a rather complete VT102 implementation.
  *
  * Beeping thanks to John T Kohl.
@@ -87,36 +56,20 @@
  *   registers (e.g. a TMS340x0 GSP).
  *
  *   The interface to the hardware is specified using a special structure
- *   (struct consw) which contains function pointers to the following
- *   operations:
- *
- *     unsigned long con_startup(unsigned long kmem_start,
- *				 const char **display_desc)
- *     void con_init(struct vc_data *conp)
- *     int con_deinit(struct vc_data *conp)
- *     int con_clear(struct vc_data *conp, int sy, int sx, int height,
- *		     int width)
- *     int con_putc(struct vc_data *conp, int c, int y, int x)
- *     int con_putcs(struct vc_data *conp, const char *s, int count, int y,
- *		     int x)
- *     int con_cursor(struct vc_data *conp, int mode)
- *     int con_scroll(struct vc_data *conp, int t, int b, int dir, int count)
- *     int con_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
- *		     int height, int width)
- *     int con_switch(struct vc_data *conp)
- *     int con_blank(int blank)
- *     int con_get_font(struct vc_data *conp, int *w, int *h, char *data)
- *     int con_set_font(struct vc_data *conp, int w, int h, char *data)
- *     int con_set_palette(struct vc_data *conp, unsigned char *table)
- *     int con_scrolldelta(struct vc_data *conp, int lines)
+ *   (struct consw) which contains function pointers to console operations
+ *   (see <linux/console.h> for more information).
  *
  * Support for changeable cursor shape
  * by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz>, August 1997
  *
  * Ported to i386 and con_scrolldelta fixed
  * by Emmanuel Marty <core@ggi-project.org>, April 1998
+ *
+ * Resurrected character buffers in videoram plus lots of other trickery
+ * by Martin Mares <mj@atrey.karlin.mff.cuni.cz>, July 1998
  */
 
+#include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
@@ -133,6 +86,7 @@
 #include <linux/selection.h>
 #include <linux/console_struct.h>
 #include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
 #include <linux/consolemap.h>
 #include <linux/timer.h>
 #include <linux/interrupt.h>
@@ -145,11 +99,7 @@
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
-#include <asm/bitops.h>
 
-#ifndef CONFIG_ABSCON_COMPAT
-#define INCLUDE_LINUX_LOGO_DATA
-#endif
 #include <asm/linux_logo.h>
 
 #include "console_macros.h"
@@ -157,48 +107,8 @@
 
 struct consw *conswitchp = NULL;
 
-int (*console_show_logo)(void) __initdata = NULL;
-
 static int vesa_blank_mode = 0; /* 0:none 1:suspendV 2:suspendH 3:powerdown */
 
-static inline void hide_cursor(int currcons)
-{
-    sw->con_cursor(vc_cons[currcons].d,CM_ERASE);
-}
-
-void set_cursor(int currcons)
-{
-    if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
-	return;
-    if (deccm)
-	sw->con_cursor(vc_cons[currcons].d,CM_DRAW);
-    else
-	hide_cursor(currcons);
-}
-
-    /*
-     *  Adjust the screen to fit a font of a certain height
-     *
-     *  Returns < 0 for error, 0 if nothing changed, and the number
-     *  of lines on the adjusted console if changed.
-     */
-
-int con_adjust_height(unsigned long fontheight)
-{
-    int currcons = fg_console;
-    /* ++Geert: Always assume that the number of lines did change? */
-    return video_num_lines;
-}
-
-/* dummy functions */
-
-void no_scroll(char *str, int *ints)
-{
-}
-
-
-#define BLANK 0x0020
-
 /* A bitmap for codes <32. A bit of 1 indicates that the code
  * corresponding to that bit number invokes some special action
  * (such as cursor movement) and should not be displayed as a
@@ -213,60 +123,36 @@
 #define DEFAULT_BELL_PITCH	750
 #define DEFAULT_BELL_DURATION	(HZ/8)
 
-/*
- *  NOTE!!! We sometimes disable and enable interrupts for a short while
- * (to put a word in video IO), but this will work even for keyboard
- * interrupts. We know interrupts aren't enabled when getting a keyboard
- * interrupt, as we use trap-gates. Hopefully all is well.
- */
-
 #ifndef MIN
 #define MIN(a,b)	((a) < (b) ? (a) : (b))
 #endif
 
-struct tty_driver console_driver;
-static int console_refcount;
 static struct tty_struct *console_table[MAX_NR_CONSOLES];
 static struct termios *console_termios[MAX_NR_CONSOLES];
 static struct termios *console_termios_locked[MAX_NR_CONSOLES];
-unsigned short *vc_scrbuf[MAX_NR_CONSOLES];
 struct vc vc_cons [MAX_NR_CONSOLES];
 
 static int con_open(struct tty_struct *, struct file *);
-static void vc_init(unsigned int console, unsigned long rows,
-		    unsigned long cols, int do_clear);
+static void vc_init(unsigned int console, unsigned int rows,
+		    unsigned int cols, int do_clear);
 static void blank_screen(void);
 static void unblank_screen(void);
-extern void change_console(unsigned int);
-extern void poke_blanked_console(void);
 static void update_attr(int currcons);
 static void gotoxy(int currcons, int new_x, int new_y);
 static void save_cur(int currcons);
 static void reset_terminal(int currcons, int do_clear);
-extern void reset_vc(unsigned int new_console);
-extern void vt_init(void);
-extern void set_vesa_blanking(unsigned long arg);
-extern void vesa_blank(void);
-extern void vesa_powerdown(void);
-extern void compute_shiftstate(void);
-extern void reset_palette(int currcons);
-extern void set_palette(void);
+static void con_flush_chars(struct tty_struct *tty);
 
 static int printable = 0;		/* Is console ready for printing? */
 
-int		video_mode_512ch = 0;	/* 512-character mode */
-unsigned long	video_font_height;	/* Height of current screen font */
-unsigned long	video_scan_lines;	/* Number of scan lines on screen */
 static unsigned short console_charmask = 0x0ff;
 
-/* used by kbd_bh - set by keyboard_interrupt */
-       int do_poke_blanked_console = 0;
-       int console_blanked = 0;
+int do_poke_blanked_console = 0;
+int console_blanked = 0;
+
 static int blankinterval = 10*60*HZ;
 static int vesa_off_interval = 0;
 
-static char putcs_buf[256];
-
 /*
  * fg_console is the current virtual console,
  * last_console is the last used one,
@@ -278,6 +164,336 @@
 int want_console = -1;
 int kmsg_redirect = 0;
 
+/*
+ *	Low-Level Functions
+ */
+
+#define IS_FG (currcons == fg_console)
+
+#ifdef VT_BUF_VRAM_ONLY
+#define DO_UPDATE 0
+#else
+#define DO_UPDATE IS_FG
+#endif
+
+static inline unsigned short *screenpos(int currcons, int offset, int viewed)
+{
+	unsigned short *p = (unsigned short *)(visible_origin + offset);
+	return p;
+}
+
+/* FIXME: Can we do the whole scrollback logic here and call the driver
+ *	  only for changing the display? Yes, there is some magic with
+ *	  blanked screens, but we could unblank here. (Argh, there might
+ *	  be some problems with unblanking since this is called from real
+ *	  interrupts...)  --mj
+ */
+static inline void scrolldelta(int lines)
+{
+	int currcons = fg_console;
+
+	clear_selection();
+	if (vcmode == KD_TEXT)
+		sw->con_scrolldelta(vc_cons[currcons].d, lines);
+}
+
+static void scrup(int currcons, unsigned int t, unsigned int b, int nr)
+{
+	unsigned short *d, *s;
+
+	if (t+nr >= b)
+		nr = b - t - 1;
+	if (b > video_num_lines || t >= b || nr < 1)
+		return;
+	if (IS_FG && sw->con_scroll(vc_cons[currcons].d, t, b, SM_UP, nr))
+		return;
+	d = (unsigned short *) (origin+video_size_row*t);
+	s = (unsigned short *) (origin+video_size_row*(t+nr));
+	scr_memcpyw(d, s, (b-t-nr) * video_size_row);
+	scr_memsetw(d + (b-t-nr) * video_num_columns, video_erase_char, video_size_row*nr);
+}
+
+static void
+scrdown(int currcons, unsigned int t, unsigned int b, int nr)
+{
+	unsigned short *s;
+	unsigned int count;
+	unsigned int step;
+
+	if (t+nr >= b)
+		nr = b - t - 1;
+	if (b > video_num_lines || t >= b || nr < 1)
+		return;
+	if (IS_FG && sw->con_scroll(vc_cons[currcons].d, t, b, SM_DOWN, nr))
+		return;
+	s = (unsigned short *) (origin+video_size_row*(b-nr-1));
+	step = video_num_columns * nr;
+	count = b - t - nr;
+	while (count--) {
+		scr_memcpyw(s + step, s, video_size_row);
+		s -= video_num_columns;
+	}
+	count = nr;
+	while (count--) {
+		s += video_num_columns;
+		scr_memsetw(s, video_erase_char, video_size_row);
+	}
+}
+
+static void update_attr(int currcons)
+{
+/*
+ * ++roman: I completely changed the attribute format for monochrome
+ * mode (!can_do_color). The formerly used MDA (monochrome display
+ * adapter) format didn't allow the combination of certain effects.
+ * Now the attribute is just a bit vector:
+ *  Bit 0..1: intensity (0..2)
+ *  Bit 2   : underline
+ *  Bit 3   : reverse
+ *  Bit 7   : blink
+ *
+ * ++Geert: TODO: Because the attributes have different meanings
+ * for monochrome and color, they should really be converted if
+ * can_do_color changes...
+ *
+ * FIXME: Monochrome attributes on MDA/HGC/etc
+ */
+	if (!can_do_color) {
+		/* Special treatment for monochrome */
+		attr = intensity |
+			(underline ? 4 : 0) |
+			((reverse ^ decscnm) ? 8 : 0) |
+			(blink ? 0x80 : 0);
+		video_erase_char = ' ' | ((reverse ^ decscnm) ? 0x800 : 0);
+		return;
+	}
+	attr = color;
+	if (can_do_color) {
+		if (underline)
+			attr = (attr & 0xf0) | ulcolor;
+		else if (intensity == 0)
+			attr = (attr & 0xf0) | halfcolor;
+	}
+	if (reverse ^ decscnm)
+		attr = reverse_video_char(attr);
+	if (blink)
+		attr ^= 0x80;
+	if (intensity == 2)
+		attr ^= 0x08;
+	if (decscnm)
+		video_erase_char = (reverse_video_char(color) << 8) | ' ';
+	else
+		video_erase_char = (color << 8) | ' ';
+}
+
+/* Note: inverting the screen twice should revert to the original state */
+void invert_screen(int currcons, int offset, int count, int viewed)
+{
+	unsigned short *p;
+	int xx = (offset >> 1) % video_num_columns;
+	int yy = (offset >> 1) / video_num_columns;
+
+	count /= 2;
+	p = screenpos(currcons, offset, viewed);
+	if (can_do_color)
+		while (count--) {
+			unsigned short old = scr_readw(p);
+			unsigned short new = reverse_video_short(old);
+			scr_writew(new, p);
+			p++;
+			if (DO_UPDATE) {
+				sw->con_putc(vc_cons[currcons].d, new, yy, xx);
+				if (++xx == video_num_columns)
+					xx = 0, ++yy;
+			}
+		}
+	else
+		while (count--) {
+			unsigned short old = scr_readw(p);
+			unsigned short new = reverse_video_short_mono(old);
+			scr_writew(new, p);
+			p++;
+			if (DO_UPDATE) {
+				sw->con_putc(vc_cons[currcons].d, new, yy, xx);
+				if (++xx == video_num_columns)
+					xx = 0, ++yy;
+			}
+		}
+}
+
+/* used by selection: complement pointer position */
+void complement_pos(int currcons, int offset)
+{
+	static unsigned short *p = NULL;
+	static unsigned short old = 0;
+	static unsigned short oldx = 0, oldy = 0;
+
+	if (p) {
+		scr_writew(old, p);
+		if (DO_UPDATE)
+			sw->con_putc(vc_cons[currcons].d, old, oldy, oldx);
+	}
+	if (offset == -1)
+		p = NULL;
+	else {
+		unsigned short new;
+		p = screenpos(currcons, offset, 1);
+		old = scr_readw(p);
+		oldx = (offset >> 1) % video_num_columns;
+		oldy = (offset >> 1) / video_num_columns;
+		new = complement_video_short(old);
+		scr_writew(new, p);
+		if (DO_UPDATE)
+			sw->con_putc(vc_cons[currcons].d, new, oldy, oldx);
+	}
+}
+
+/* used by selection - convert a screen word to a glyph number */
+int scrw2glyph(unsigned short scr_word)
+{
+	return ( video_mode_512ch )
+		? ((scr_word & 0x0800) >> 3) + (scr_word & 0x00ff)
+		: scr_word & 0x00ff;
+}
+
+static void insert_char(int currcons, unsigned int nr)
+{
+	unsigned short *p, *q = (unsigned short *) pos;
+
+	p = q + video_num_columns - nr - x;
+	while (--p >= q)
+		scr_writew(scr_readw(p), p + nr);
+	scr_memsetw(q, video_erase_char, nr*2);
+	need_wrap = 0;
+	if (DO_UPDATE) {
+		unsigned short oldattr = attr;
+		sw->con_bmove(vc_cons[currcons].d,y,x,y,x+nr,1,
+			      video_num_columns-x-nr);
+		attr = video_erase_char >> 8;
+		while (nr--)
+			sw->con_putc(vc_cons[currcons].d,
+				     video_erase_char,y,x+nr);
+		attr = oldattr;
+	}
+}
+
+static void delete_char(int currcons, unsigned int nr)
+{
+	unsigned int i = x;
+	unsigned short *p = (unsigned short *) pos;
+
+	while (++i <= video_num_columns - nr) {
+		scr_writew(scr_readw(p+nr), p);
+		p++;
+	}
+	scr_memsetw(p, video_erase_char, nr*2);
+	need_wrap = 0;
+	if (DO_UPDATE) {
+		unsigned short oldattr = attr;
+		sw->con_bmove(vc_cons[currcons].d, y, x+nr, y, x, 1,
+			      video_num_columns-x-nr);
+		attr = video_erase_char >> 8;
+		while (nr--)
+			sw->con_putc(vc_cons[currcons].d,
+				     video_erase_char, y,
+				     video_num_columns-1-nr);
+		attr = oldattr;
+	}
+}
+
+/* FIXME: Does it make sense to hide cursor on non-fg consoles? */
+static inline void hide_cursor(int currcons)
+{
+    sw->con_cursor(vc_cons[currcons].d,CM_ERASE);
+}
+
+void set_cursor(int currcons)
+{
+    if (!IS_FG || console_blanked || vcmode == KD_GRAPHICS)
+	return;
+    if (deccm)
+	sw->con_cursor(vc_cons[currcons].d,CM_DRAW);
+    else
+	hide_cursor(currcons);
+}
+
+static void set_origin(int currcons)
+{
+	if (!IS_FG ||
+	    !sw->con_set_origin ||
+	    !sw->con_set_origin(vc_cons[currcons].d))
+		origin = (unsigned long) screenbuf;
+	visible_origin = origin;
+	scr_end = origin + screenbuf_size;
+	pos = origin + video_size_row*y + 2*x;
+}
+
+static inline void save_screen(void)
+{
+	int currcons = fg_console;
+	if (sw->con_save_screen)
+		sw->con_save_screen(vc_cons[currcons].d);
+}
+
+/*
+ *	Redrawing of screen
+ */
+
+void update_screen(int new_console)
+{
+	int currcons = fg_console;
+	int xx, yy, startx, attrib;
+	unsigned short *p, *q;
+	static int lock = 0;
+
+	if (lock)
+		return;
+	if (!vc_cons_allocated(new_console)) {
+		/* strange ... */
+		printk("update_screen: tty %d not allocated ??\n", new_console+1);
+		return;
+	}
+	lock = 1;
+
+	clear_selection();
+
+	fg_console = new_console;
+	set_origin(currcons);
+	currcons = new_console;
+	sw->con_cursor (vc_cons[currcons].d, CM_ERASE);
+	set_origin(currcons);
+	if (sw->con_switch (vc_cons[currcons].d)) {
+		/* Update the screen contents */
+		p = (unsigned short *)origin;
+		attrib = scr_readw(p) & 0xff00;
+		for (yy = 0; yy < video_num_lines; yy++) {
+			q = p;
+			for (startx = xx = 0; xx < video_num_columns; xx++) {
+				if (attrib != (scr_readw(p) & 0xff00)) {
+					if (p > q)
+						sw->con_putcs (vc_cons[currcons].d, q,
+							       p - q, yy, startx);
+					startx = xx;
+					q = p;
+					attrib = (scr_readw(p) & 0xff00);
+				}
+				p++;
+			}
+			if (p > q)
+				sw->con_putcs (vc_cons[currcons].d, q,
+					       p - q, yy, startx);
+		}
+	}
+	set_cursor (currcons);
+	set_leds();
+	compute_shiftstate();
+	lock = 0;
+}
+
+/*
+ *	Allocation, freeing and resizing of VTs.
+ */
+
 int vc_cons_allocated(unsigned int i)
 {
 	return (i < MAX_NR_CONSOLES && vc_cons[i].d);
@@ -288,12 +504,12 @@
     /* ++Geert: sw->con_init determines console size */
     sw = conswitchp;
     cons_num = currcons;
-    sw->con_init(vc_cons[currcons].d);
+    sw->con_init(vc_cons[currcons].d, 1);
     video_size_row = video_num_columns<<1;
     video_screen_size = video_num_lines*video_size_row;
 }
 
-int vc_allocate(unsigned int currcons)		/* return 0 on success */
+int vc_allocate(unsigned int currcons, int init)	/* return 0 on success */
 {
 	if (currcons >= MAX_NR_CONSOLES)
 	  return -ENXIO;
@@ -307,6 +523,9 @@
 	    /* due to the granularity of kmalloc, we waste some memory here */
 	    /* the alloc is done in two steps, to optimize the common situation
 	       of a 25x80 console (structsize=216, video_screen_size=4000) */
+	    /* although the numbers above are not valid since long ago, the
+	       point is still up-to-date and the comment still has its value
+	       even if only as a historical artifact.  --mj, July 1998 */
 	    p = (long) kmalloc(structsize, GFP_KERNEL);
 	    if (!p)
 		return -ENOMEM;
@@ -320,10 +539,13 @@
 		vt_cons[currcons] = NULL;
 		return -ENOMEM;
 	    }
-	    vc_scrbuf[currcons] = (unsigned short *) q;
-	    vc_cons[currcons].d->vc_kmalloced = 1;
-	    vc_cons[currcons].d->vc_screenbuf_size = video_screen_size;
-	    vc_init(currcons, video_num_lines, video_num_columns, 1);
+	    screenbuf = (unsigned short *) q;
+	    kmalloced = 1;
+	    screenbuf_size = video_screen_size;
+	    if (!sw->con_save_screen)
+	    	init = 0; /* If console does not have save_screen routine,
+	    		     we should clear the screen */
+	    vc_init(currcons, video_num_lines, video_num_columns, !init);
 	}
 	return 0;
 }
@@ -333,187 +555,105 @@
  * [this is to be used together with some user program
  * like resize that changes the hardware videomode]
  */
-int vc_resize(unsigned long lines, unsigned long cols)
+/* FIXME: Cursor sometimes disappears when resizing */
+/* FIXME: We could also take data from the scrollback buffer */
+int vc_resize(unsigned int lines, unsigned int cols,
+	      unsigned int first, unsigned int last)
 {
-	unsigned long cc, ll, ss, sr;
-	unsigned long occ, oll, oss, osr;
-	unsigned short *p;
-	unsigned int currcons = fg_console, i;
-	unsigned short *newscreens[MAX_NR_CONSOLES];
-	long ol, nl, rlth, rrem;
-
-	cc = (cols ? cols : video_num_columns);
-	ll = (lines ? lines : video_num_lines);
-	sr = cc << 1;
-	ss = sr * ll;
-
-	/*
-	 * Some earlier version had all consoles of potentially
-	 * different sizes, but that was really messy.
-	 * So now we only change if there is room for all consoles
-	 * of the same size.
-	 */
-	for (currcons = 0; currcons < MAX_NR_CONSOLES; currcons++) {
-	    if (!vc_cons_allocated(currcons))
-	      newscreens[currcons] = 0;
-	    else {
-		p = (unsigned short *) kmalloc(ss, GFP_USER);
-		if (!p) {
-		    for (i = 0; i< currcons; i++)
-		      if (newscreens[i])
-			kfree_s(newscreens[i], ss);
-		    return -ENOMEM;
-		}
-		newscreens[currcons] = p;
-	    }
-	}
-
-	for (currcons = 0; currcons < MAX_NR_CONSOLES; currcons++) {
-	    if (!vc_cons_allocated(currcons))
-	      continue;
-
-	    oll = video_num_lines;
-	    occ = video_num_columns;
-	    osr = video_size_row;
-	    oss = video_screen_size;
-
-	    video_num_lines = ll;
-	    video_num_columns = cc;
-	    video_size_row = sr;
-	    video_screen_size = ss;
-
-	    rlth = MIN(osr, sr);
-	    rrem = sr - rlth;
-	    ol = origin;
-	    nl = (long) newscreens[currcons];
-	    if (ll < oll)
-	      ol += (oll - ll) * osr;
-
-	    update_attr(currcons);
-	    while (ol < scr_end) {
-		memcpyw((unsigned short *) nl, (unsigned short *) ol, rlth);
-		if (rrem)
-		  memsetw((void *)(nl + rlth), video_erase_char, rrem);
-		ol += osr;
-		nl += sr;
-	    }
-
-	    if (kmalloced)
-	      kfree_s(vc_scrbuf[currcons], oss);
-	    vc_scrbuf[currcons] = newscreens[currcons];
-	    kmalloced = 1;
-	    screenbuf_size = ss;
-
-	    origin = video_mem_start = (long) vc_scrbuf[currcons];
-	    scr_end = video_mem_end = video_mem_start + ss;
-
-	    if (scr_end > nl)
-	      memsetw((void *) nl, video_erase_char, scr_end - nl);
-
-	    /* do part of a reset_terminal() */
-	    top = 0;
-	    bottom = video_num_lines;
-	    gotoxy(currcons, x, y);
-	    save_cur(currcons);
-	}
-
-	/* don't update in graphics mode */
-	if (currcons == fg_console && vt_cons[fg_console]->vc_mode == KD_TEXT)
-	    update_screen(fg_console);
-
-	set_cursor(fg_console);
-
-	return 0;
-}
-
-/*
- * ++Geert: Change # of rows and columns for one specific console.
- * Of course it's not messy to have all consoles of potentially different
- * sizes, except on PCish hardware :-)
- */
-void vc_resize_con(unsigned long lines, unsigned long cols,
-		   unsigned int currcons)
-{
-	unsigned long cc, ll, ss, sr;
-	unsigned long occ, oll, oss, osr;
-	unsigned short *newscreen;
-	long ol, nl, rlth, rrem;
-
-	if (!cols || !lines || currcons >= MAX_NR_CONSOLES)
-	    return;
+	unsigned int cc, ll, ss, sr, todo = 0;
+	unsigned int occ, oll, oss, osr;
+	unsigned short *p;
+	unsigned int currcons = fg_console, i;
+	unsigned short *newscreens[MAX_NR_CONSOLES];
+	unsigned long ol, nl, rlth, rrem;
 
-	cc = cols;
-	ll = lines;
+	cc = (cols ? cols : video_num_columns);
+	ll = (lines ? lines : video_num_lines);
 	sr = cc << 1;
 	ss = sr * ll;
 
-	if (!vc_cons_allocated(currcons))
-	    newscreen = 0;
-	else if (!(newscreen = (unsigned short *)kmalloc(ss, GFP_USER)))
-	    return;
-
-	if (vc_cons_allocated(currcons)) {
-	    oll = video_num_lines;
-	    occ = video_num_columns;
-	    osr = video_size_row;
-	    oss = video_screen_size;
-
-	    video_num_lines = ll;
-	    video_num_columns = cc;
-	    video_size_row = sr;
-	    video_screen_size = ss;
-
-	    rlth = MIN(osr, sr);
-	    rrem = sr - rlth;
-	    ol = origin;
-	    nl = (long) newscreen;
-	    if (ll < oll)
-		ol += (oll - ll) * osr;
-
-	    /* ++Geert: TODO: Because the attributes have different meanings
-	       for monochrome and color, they should really be converted if
-	       can_do_color changes... */
-	    update_attr(currcons);
-	    while (ol < scr_end) {
-		memcpyw((unsigned short *) nl, (unsigned short *) ol, rlth);
-		if (rrem)
-		    memsetw((void *)(nl + rlth), video_erase_char, rrem);
-		ol += osr;
-		nl += sr;
-	    }
+ 	for (currcons = first; currcons <= last; currcons++) {
+		if (!vc_cons_allocated(currcons) ||
+		    (cc == video_num_columns && ll == video_num_lines))
+			newscreens[currcons] = NULL;
+		else {
+			p = (unsigned short *) kmalloc(ss, GFP_USER);
+			if (!p) {
+				for (i = 0; i< currcons; i++)
+					if (newscreens[i])
+						kfree_s(newscreens[i], ss);
+				return -ENOMEM;
+			}
+			newscreens[currcons] = p;
+			todo++;
+		}
+	}
+	if (!todo)
+		return 0;
 
-	    if (kmalloced)
-		kfree_s(vc_scrbuf[currcons], oss);
-	    vc_scrbuf[currcons] = newscreen;
-	    kmalloced = 1;
-	    screenbuf_size = ss;
+	for (currcons = first; currcons <= last; currcons++) {
+		if (!newscreens[currcons] || !vc_cons_allocated(currcons))
+			continue;
 
-	    origin = video_mem_start = (long) vc_scrbuf[currcons];
-	    scr_end = video_mem_end = video_mem_start + ss;
+		oll = video_num_lines;
+		occ = video_num_columns;
+		osr = video_size_row;
+		oss = video_screen_size;
+
+		video_num_lines = ll;
+		video_num_columns = cc;
+		video_size_row = sr;
+		video_screen_size = ss;
+
+		rlth = MIN(osr, sr);
+		rrem = sr - rlth;
+		ol = origin;
+		nl = (long) newscreens[currcons];
+		if (ll < oll)
+			ol += (oll - ll) * osr;
+
+		update_attr(currcons);
+		while (ol < scr_end) {
+			scr_memcpyw((unsigned short *) nl, (unsigned short *) ol, rlth);
+			if (rrem)
+				scr_memsetw((void *)(nl + rlth), video_erase_char, rrem);
+			ol += osr;
+			nl += sr;
+		}
 
-	    if (scr_end > nl)
-		memsetw((void *) nl, video_erase_char, scr_end - nl);
+		if (kmalloced)
+			kfree_s(screenbuf, oss);
+		screenbuf = newscreens[currcons];
+		kmalloced = 1;
+		screenbuf_size = ss;
+		set_origin(currcons);
+
+		if (scr_end > nl)
+			scr_memsetw((void *) nl, video_erase_char, scr_end - nl);
+
+		/* do part of a reset_terminal() */
+		top = 0;
+		bottom = video_num_lines;
+		gotoxy(currcons, x, y);
+		save_cur(currcons);
+
+		if (console_table[currcons]) {
+			struct winsize ws, *cws = &console_table[currcons]->winsize;
+			memset(&ws, 0, sizeof(ws));
+			ws.ws_row = video_num_lines;
+			ws.ws_col = video_num_columns;
+			if ((ws.ws_row != cws->ws_row || ws.ws_col != cws->ws_col) &&
+			    console_table[currcons]->pgrp > 0)
+				kill_pg(console_table[currcons]->pgrp, SIGWINCH, 1);
+			*cws = ws;
+		}
 
-	    /* do part of a reset_terminal() */
-	    top = 0;
-	    bottom = video_num_lines;
-	    gotoxy(currcons, x, y);
-	    save_cur(currcons);
-
-	    if (console_table[currcons]) {
-		struct winsize ws, *cws = &console_table[currcons]->winsize;
-		ws.ws_row = video_num_lines;
-		ws.ws_col = video_num_columns;
-		if ((ws.ws_row != cws->ws_row || ws.ws_col != cws->ws_col) &&
-		    console_table[currcons]->pgrp > 0)
-		    kill_pg(console_table[currcons]->pgrp, SIGWINCH, 1);
-		*cws = ws;
-	    }
+		if (IS_FG && vt_cons[fg_console]->vc_mode == KD_TEXT)
+			update_screen(fg_console);
 	}
 
-	/* don't update in graphics mode */
-	if (currcons == fg_console && vt_cons[fg_console]->vc_mode == KD_TEXT)
-	    update_screen(fg_console);
+	set_cursor(fg_console);
+	return 0;
 }
 
 
@@ -522,13 +662,16 @@
 	if (vc_cons_allocated(currcons)) {
 	    sw->con_deinit(vc_cons[currcons].d);
 	    if (kmalloced)
-	      kfree_s(vc_scrbuf[currcons], screenbuf_size);
+		kfree_s(screenbuf, screenbuf_size);
 	    if (currcons >= MIN_NR_CONSOLES)
-	      kfree_s(vc_cons[currcons].d, structsize);
-	    vc_cons[currcons].d = 0;
+		kfree_s(vc_cons[currcons].d, structsize);
+	    vc_cons[currcons].d = NULL;
 	}
 }
 
+/*
+ *	VT102 emulator
+ */
 
 #define set_kbd(x) set_vc_kbd_mode(kbd_table+currcons,x)
 #define clr_kbd(x) clr_vc_kbd_mode(kbd_table+currcons,x)
@@ -551,7 +694,7 @@
 /* the default colour table, for VGA+ colour systems */
 int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa,
     0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff};
-int default_grn[] = {0x00,0x00,0xaa,0xaa,0x00,0x00,0xaa,0xaa,
+int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa,
     0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff};
 int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa,
     0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff};
@@ -595,15 +738,6 @@
 	gotoxy(currcons, new_x, decom ? (top+new_y) : new_y);
 }
 
-/*
- * Hardware scrollback support
- */
-static inline void scrolldelta(int lines)
-{
-    int currcons = fg_console;
-    sw->con_scrolldelta(vc_cons[currcons].d, lines);
-}
-
 void scrollback(int lines)
 {
 	int currcons = fg_console;
@@ -622,50 +756,6 @@
 	scrolldelta(lines);
 }
 
-static void scrup(int currcons, unsigned int t, unsigned int b, int nr)
-{
-	unsigned short *d, *s;
-
-	if (t+nr >= b)
-		nr = b - t - 1;
-	if (b > video_num_lines || t >= b || nr < 1)
-		return;
-	d = (unsigned short *) (origin+video_size_row*t);
-	s = (unsigned short *) (origin+video_size_row*(t+nr));
-	memcpyw(d, s, (b-t-nr) * video_size_row);
-	memsetw(d + (b-t-nr) * video_num_columns, video_erase_char, video_size_row*nr);
-	if (currcons == fg_console)
-		sw->con_scroll(vc_cons[currcons].d, t, b, SM_UP, nr);
-}
-
-static void
-scrdown(int currcons, unsigned int t, unsigned int b, int nr)
-{
-	unsigned short *s;
-	unsigned int count;
-	unsigned int step;
-
-	if (t+nr >= b)
-		nr = b - t - 1;
-	if (b > video_num_lines || t >= b || nr < 1)
-		return;
-	s = (unsigned short *) (origin+video_size_row*(b-nr-1));
-	step = video_num_columns * nr;
-	count = b - t - nr;
-	while (count--) {
-		memcpyw(s + step, s, video_size_row);
-		s -= video_num_columns;
-	}
-	count = nr;
-	while (count--) {
-		s += video_num_columns;
-		memsetw(s, video_erase_char, video_size_row);
-	}
-	has_scrolled = 1;
-	if (currcons == fg_console)
-		sw->con_scroll(vc_cons[currcons].d, t, b, SM_DOWN, nr);
-}
-
 static void lf(int currcons)
 {
     	/* don't scroll if above bottom of scrolling region, or
@@ -716,14 +806,14 @@
 
 static void csi_J(int currcons, int vpar)
 {
-	unsigned long count;
+	unsigned int count;
 	unsigned short * start;
 
 	switch (vpar) {
 		case 0:	/* erase from cursor to end of display */
 			count = (scr_end-pos)>>1;
 			start = (unsigned short *) pos;
-			if (currcons == fg_console) {
+			if (DO_UPDATE) {
 				/* do in two stages */
 				sw->con_clear(vc_cons[currcons].d, y, x, 1,
 					      video_num_columns-x);
@@ -735,7 +825,7 @@
 		case 1:	/* erase from start to cursor */
 			count = ((pos-origin)>>1)+1;
 			start = (unsigned short *) origin;
-			if (currcons == fg_console) {
+			if (DO_UPDATE) {
 				/* do in two stages */
 				sw->con_clear(vc_cons[currcons].d, 0, 0, y,
 					      video_num_columns);
@@ -746,7 +836,7 @@
 		case 2: /* erase whole display */
 			count = video_num_columns * video_num_lines;
 			start = (unsigned short *) origin;
-			if (currcons == fg_console)
+			if (DO_UPDATE)
 				sw->con_clear(vc_cons[currcons].d, 0, 0,
 					      video_num_lines,
 					      video_num_columns);
@@ -754,41 +844,41 @@
 		default:
 			return;
 	}
-	memsetw(start, video_erase_char, 2*count);
+	scr_memsetw(start, video_erase_char, 2*count);
 	need_wrap = 0;
 }
 
 static void csi_K(int currcons, int vpar)
 {
-	unsigned long count;
+	unsigned int count;
 	unsigned short * start;
 
 	switch (vpar) {
 		case 0:	/* erase from cursor to end of line */
 			count = video_num_columns-x;
 			start = (unsigned short *) pos;
-			if (currcons == fg_console)
+			if (DO_UPDATE)
 				sw->con_clear(vc_cons[currcons].d, y, x, 1,
 					      video_num_columns-x);
 			break;
 		case 1:	/* erase from start of line to cursor */
 			start = (unsigned short *) (pos - (x<<1));
 			count = x+1;
-			if (currcons == fg_console)
+			if (DO_UPDATE)
 				sw->con_clear(vc_cons[currcons].d, y, 0, 1,
 					      x + 1);
 			break;
 		case 2: /* erase whole line */
 			start = (unsigned short *) (pos - (x<<1));
 			count = video_num_columns;
-			if (currcons == fg_console)
+			if (DO_UPDATE)
 				sw->con_clear(vc_cons[currcons].d, y, 0, 1,
 					      video_num_columns);
 			break;
 		default:
 			return;
 	}
-	memsetw(start, video_erase_char, 2 * count);
+	scr_memsetw(start, video_erase_char, 2 * count);
 	need_wrap = 0;
 }
 
@@ -800,56 +890,12 @@
 		vpar++;
 	count = (vpar > video_num_columns-x) ? (video_num_columns-x) : vpar;
 
-	memsetw((unsigned short *) pos, video_erase_char, 2 * count);
-	if (currcons == fg_console)
+	scr_memsetw((unsigned short *) pos, video_erase_char, 2 * count);
+	if (DO_UPDATE)
 		sw->con_clear(vc_cons[currcons].d, y, x, 1, count);
 	need_wrap = 0;
 }
 
-static void update_attr(int currcons)
-{
-/*
- * ++roman: I completely changed the attribute format for monochrome
- * mode (!can_do_color). The formerly used MDA (monochrome display
- * adapter) format didn't allow the combination of certain effects.
- * Now the attribute is just a bit vector:
- *  Bit 0..1: intensity (0..2)
- *  Bit 2   : underline
- *  Bit 3   : reverse
- *  Bit 7   : blink
- *
- * ++Geert: TODO: Because the attributes have different meanings
- * for monochrome and color, they should really be converted if
- * can_do_color changes...
- */
-	if (!can_do_color) {
-		/* Special treatment for monochrome */
-		attr = intensity |
-			(underline ? 4 : 0) |
-			((reverse ^ decscnm) ? 8 : 0) |
-			(blink ? 0x80 : 0);
-		video_erase_char = ' ' | ((reverse ^ decscnm) ? 0x800 : 0);
-		return;
-	}
-	attr = color;
-	if (can_do_color) {
-		if (underline)
-			attr = (attr & 0xf0) | ulcolor;
-		else if (intensity == 0)
-			attr = (attr & 0xf0) | halfcolor;
-	}
-	if (reverse ^ decscnm)
-		attr = reverse_video_char(attr);
-	if (blink)
-		attr ^= 0x80;
-	if (intensity == 2)
-		attr ^= 0x08;
-	if (decscnm)
-		video_erase_char = (reverse_video_char(color) << 8) | ' ';
-	else
-		video_erase_char = (color << 8) | ' ';
-}
-
 static void default_attr(int currcons)
 {
 	intensity = 1;
@@ -903,264 +949,99 @@
 				toggle_meta = 0;
 				break;
 			case 12: /* ANSI X3.64-1979 (SCO-ish?)
-				  * Select second alternate font, toggle
-				  * high bit before displaying as ROM char.
-				  */
-				translate = set_translate(IBMPC_MAP);
-				disp_ctrl = 1;
-				toggle_meta = 1;
-				break;
-			case 21:
-			case 22:
-				intensity = 1;
-				break;
-			case 24:
-				underline = 0;
-				break;
-			case 25:
-				blink = 0;
-				break;
-			case 27:
-				reverse = 0;
-				break;
-			case 38: /* ANSI X3.64-1979 (SCO-ish?)
-				  * Enables underscore, white foreground
-				  * with white underscore (Linux - use
-				  * default foreground).
-				  */
-				color = (def_color & 0x0f) | background;
-				underline = 1;
-				break;
-			case 39: /* ANSI X3.64-1979 (SCO-ish?)
-				  * Disable underline option.
-				  * Reset colour to default? It did this
-				  * before...
-				  */
-				color = (def_color & 0x0f) | background;
-				underline = 0;
-				break;
-			case 49:
-				color = (def_color & 0xf0) | foreground;
-				break;
-			default:
-				if (par[i] >= 30 && par[i] <= 37)
-					color = color_table[par[i]-30]
-						| background;
-				else if (par[i] >= 40 && par[i] <= 47)
-					color = (color_table[par[i]-40]<<4)
-						| foreground;
-				break;
-		}
-	update_attr(currcons);
-}
-
-static void respond_string(const char * p, struct tty_struct * tty)
-{
-	while (*p) {
-		tty_insert_flip_char(tty, *p, 0);
-		p++;
-	}
-	tty_schedule_flip(tty);
-}
-
-static void cursor_report(int currcons, struct tty_struct * tty)
-{
-	char buf[40];
-
-	sprintf(buf, "\033[%ld;%ldR", y + (decom ? top+1 : 1), x+1);
-	respond_string(buf, tty);
-}
-
-static inline void status_report(struct tty_struct * tty)
-{
-	respond_string("\033[0n", tty);	/* Terminal ok */
-}
-
-static inline 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 via ioctl(TIOCLINUX) and through set_selection */
-int mouse_reporting(void)
-{
-	int currcons = fg_console;
-
-	return report_mouse;
-}
-
-int tioclinux(struct tty_struct *tty, unsigned long arg)
-{
-	char type, data;
-
-	if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
-		return -EINVAL;
-	if (current->tty != tty && !suser())
-		return -EPERM;
-	if (get_user(type, (char *)arg))
-		return -EFAULT;
-	switch (type)
-	{
-		case 2:
-			return set_selection(arg, tty, 1);
-		case 3:
-			return paste_selection(tty);
-		case 4:
-			do_unblank_screen();
-			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.
-	 */
-	 		data = shift_state;
-			return __put_user(data, (char *) arg);
-		case 7:
-			data = mouse_reporting();
-			return __put_user(data, (char *) arg);
-		case 10:
-			set_vesa_blanking(arg);
-			return 0;
-		case 11:	/* set kmsg redirect */
-			if (!suser())
-				return -EPERM;
-			if (get_user(data, (char *)arg+1))
-					return -EFAULT;
-			kmsg_redirect = data;
-			return 0;
-		case 12:	/* get fg_console */
-			return fg_console;
-	}
-	return -EINVAL;
-}
-
-static inline unsigned short *screenpos(int currcons, int offset, int viewed)
-{
-	unsigned short *p = (unsigned short *)(origin + offset);
-	return p;
+				  * Select second alternate font, toggle
+				  * high bit before displaying as ROM char.
+				  */
+				translate = set_translate(IBMPC_MAP);
+				disp_ctrl = 1;
+				toggle_meta = 1;
+				break;
+			case 21:
+			case 22:
+				intensity = 1;
+				break;
+			case 24:
+				underline = 0;
+				break;
+			case 25:
+				blink = 0;
+				break;
+			case 27:
+				reverse = 0;
+				break;
+			case 38: /* ANSI X3.64-1979 (SCO-ish?)
+				  * Enables underscore, white foreground
+				  * with white underscore (Linux - use
+				  * default foreground).
+				  */
+				color = (def_color & 0x0f) | background;
+				underline = 1;
+				break;
+			case 39: /* ANSI X3.64-1979 (SCO-ish?)
+				  * Disable underline option.
+				  * Reset colour to default? It did this
+				  * before...
+				  */
+				color = (def_color & 0x0f) | background;
+				underline = 0;
+				break;
+			case 49:
+				color = (def_color & 0xf0) | foreground;
+				break;
+			default:
+				if (par[i] >= 30 && par[i] <= 37)
+					color = color_table[par[i]-30]
+						| background;
+				else if (par[i] >= 40 && par[i] <= 47)
+					color = (color_table[par[i]-40]<<4)
+						| foreground;
+				break;
+		}
+	update_attr(currcons);
 }
 
-static inline void visual_putc_attr(int currcons, unsigned short new,
-				    int yy, int xx)
+static void respond_string(const char * p, struct tty_struct * tty)
 {
-    unsigned short oldattr = attr;
-    attr = new >> 8;
-    sw->con_putc(vc_cons[currcons].d, new & 0xff, yy, xx);
-    attr = oldattr;
-}
-
-static inline void visual_putc_attr_next(int currcons, unsigned short new,
-					 int *yy, int *xx)
-{
-    attr = new >> 8;
-    sw->con_putc(vc_cons[currcons].d, new & 0xff, *yy, *xx);
-    if (++(*xx) == video_num_columns)
-	*xx = 0, ++(*yy);
+	while (*p) {
+		tty_insert_flip_char(tty, *p, 0);
+		p++;
+	}
+	tty_schedule_flip(tty);
 }
 
-
-/* Note: inverting the screen twice should revert to the original state */
-void invert_screen(int currcons, int offset, int count, int viewed)
+static void cursor_report(int currcons, struct tty_struct * tty)
 {
-	unsigned short *p;
-	int xx = (offset >> 1) % video_num_columns;
-	int yy = (offset >> 1) / video_num_columns;
-	unsigned short oldattr = attr;
+	char buf[40];
 
-	count /= 2;
-	p = screenpos(currcons, offset, viewed);
-	if (can_do_color)
-		while (count--) {
-			unsigned short old = scr_readw(p);
-			unsigned short new = reverse_video_short(old);
-			scr_writew(new, p);
-			p++;
-			if (currcons == fg_console)
-				visual_putc_attr_next(currcons, new, &yy, &xx);
-		}
-	else
-		while (count--) {
-			unsigned short old = scr_readw(p);
-			unsigned short new = reverse_video_short_mono(old);
-			scr_writew(new, p);
-			p++;
-			if (currcons == fg_console)
-				visual_putc_attr_next(currcons, new, &yy, &xx);
-		}
-	attr = oldattr;
+	sprintf(buf, "\033[%d;%dR", y + (decom ? top+1 : 1), x+1);
+	respond_string(buf, tty);
 }
 
-/* used by selection: complement pointer position */
-void complement_pos(int currcons, int offset)
+static inline void status_report(struct tty_struct * tty)
 {
-	static unsigned short *p = NULL;
-	static unsigned short old = 0;
-	static unsigned short oldx = 0, oldy = 0;
-
-	if (p) {
-		scr_writew(old, p);
-		if (currcons == fg_console)
-			visual_putc_attr(currcons, old, oldy, oldx);
-	}
-	if (offset == -1)
-		p = NULL;
-	else {
-		unsigned short new;
-		p = screenpos(currcons, offset, 1);
-		old = scr_readw(p);
-		oldx = (offset >> 1) % video_num_columns;
-		oldy = (offset >> 1) / video_num_columns;
-		new = complement_video_short(old);
-		scr_writew(new, p);
-		if (currcons == fg_console)
-			visual_putc_attr(currcons, new, oldy, oldx);
-	}
+	respond_string("\033[0n", tty);	/* Terminal ok */
 }
 
-/* used by selection */
-unsigned short screen_word(int currcons, int offset, int viewed)
+static inline void respond_ID(struct tty_struct * tty)
 {
-	return scr_readw(screenpos(currcons, offset, viewed));
+	respond_string(VT102ID, tty);
 }
 
-/* used by selection - convert a screen word to a glyph number */
-int scrw2glyph(unsigned short scr_word)
+void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry)
 {
-	return ( video_mode_512ch )
-		? ((scr_word & 0x0800) >> 3) + (scr_word & 0x00ff)
-		: scr_word & 0x00ff;
-}
+	char buf[8];
 
-/* used by vcs - note the word offset */
-unsigned short *screen_pos(int currcons, int w_offset, int viewed)
-{
-	return screenpos(currcons, 2 * w_offset, viewed);
+	sprintf(buf, "\033[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
+		(char)('!' + mry));
+	respond_string(buf, tty);
 }
 
-void getconsxy(int currcons, char *p)
+/* invoked via ioctl(TIOCLINUX) and through set_selection */
+int mouse_reporting(void)
 {
-	p[0] = x;
-	p[1] = y;
-}
+	int currcons = fg_console;
 
-void putconsxy(int currcons, char *p)
-{
-	gotoxy(currcons, p[0], p[1]);
-	set_cursor(currcons);
+	return report_mouse;
 }
 
 static void set_mode(int currcons, int on_off)
@@ -1269,6 +1150,7 @@
 				bell_duration = DEFAULT_BELL_DURATION;
 			break;
 		case 12: /* bring specified console to the front */
+			/* FIXME: Use console_bh for switching! */
 			if (par[1] >= 1 && vc_cons_allocated(par[1]-1))
 				update_screen(par[1]-1);
 			break;
@@ -1281,56 +1163,12 @@
 	}
 }
 
-static void insert_char(int currcons, unsigned int nr)
-{
-	unsigned short *p, *q = (unsigned short *) pos;
-
-	p = q + video_num_columns - nr - x;
-	while (--p >= q)
-		scr_writew(scr_readw(p), p + nr);
-	memsetw(q, video_erase_char, nr*2);
-	need_wrap = 0;
-	if (currcons == fg_console) {
-		unsigned short oldattr = attr;
-		sw->con_bmove(vc_cons[currcons].d,y,x,y,x+nr,1,
-			      video_num_columns-x-nr);
-		attr = video_erase_char >> 8;
-		while (nr--)
-			sw->con_putc(vc_cons[currcons].d,
-				     (video_erase_char & 0x00ff),y,x+nr);
-		attr = oldattr;
-	}
-}
-
 static void insert_line(int currcons, unsigned int nr)
 {
 	scrdown(currcons,y,bottom,nr);
 	need_wrap = 0;
 }
 
-static void delete_char(int currcons, unsigned int nr)
-{
-	unsigned int i = x;
-	unsigned short *p = (unsigned short *) pos;
-
-	while (++i <= video_num_columns - nr) {
-		scr_writew(scr_readw(p+nr), p);
-		p++;
-	}
-	memsetw(p, video_erase_char, nr*2);
-	need_wrap = 0;
-	if (currcons == fg_console) {
-		unsigned short oldattr = attr;
-		sw->con_bmove(vc_cons[currcons].d, y, x+nr, y, x, 1,
-			      video_num_columns-x-nr);
-		attr = video_erase_char >> 8;
-		while (nr--)
-			sw->con_putc(vc_cons[currcons].d,
-				     (video_erase_char & 0x00ff), y,
-				     video_num_columns-1-nr);
-		attr = oldattr;
-	}
-}
 
 static void delete_line(int currcons, unsigned int nr)
 {
@@ -1462,55 +1300,380 @@
 	    csi_J(currcons,2);
 }
 
-/*
- * Turn the Scroll-Lock LED on when the tty is stopped
- */
-static void con_stop(struct tty_struct *tty)
+static void do_con_trol(struct tty_struct *tty, unsigned int currcons, int c)
 {
-	int console_num;
-	if (!tty)
+	/*
+	 *  Control characters can be used in the _middle_
+	 *  of an escape sequence.
+	 */
+	switch (c) {
+	case 0:
+		return;
+	case 7:
+		if (bell_duration)
+			kd_mksound(bell_pitch, bell_duration);
+		return;
+	case 8:
+		bs(currcons);
+		return;
+	case 9:
+		pos -= (x << 1);
+		while (x < video_num_columns - 1) {
+			x++;
+			if (tab_stop[x >> 5] & (1 << (x & 31)))
+				break;
+		}
+		pos += (x << 1);
+		return;
+	case 10: case 11: case 12:
+		lf(currcons);
+		if (!is_kbd(lnm))
+			return;
+	case 13:
+		cr(currcons);
+		return;
+	case 14:
+		charset = 1;
+		translate = set_translate(G1_charset);
+		disp_ctrl = 1;
+		return;
+	case 15:
+		charset = 0;
+		translate = set_translate(G0_charset);
+		disp_ctrl = 0;
+		return;
+	case 24: case 26:
+		vc_state = ESnormal;
+		return;
+	case 27:
+		vc_state = ESesc;
+		return;
+	case 127:
+		del(currcons);
+		return;
+	case 128+27:
+		vc_state = ESsquare;
+		return;
+	}
+	switch(vc_state) {
+	case ESesc:
+		vc_state = ESnormal;
+		switch (c) {
+		case '[':
+			vc_state = ESsquare;
+			return;
+		case ']':
+			vc_state = ESnonstd;
+			return;
+		case '%':
+			vc_state = ESpercent;
+			return;
+		case 'E':
+			cr(currcons);
+			lf(currcons);
+			return;
+		case 'M':
+			ri(currcons);
+			return;
+		case 'D':
+			lf(currcons);
+			return;
+		case 'H':
+			tab_stop[x >> 5] |= (1 << (x & 31));
+			return;
+		case 'Z':
+			respond_ID(tty);
+			return;
+		case '7':
+			save_cur(currcons);
+			return;
+		case '8':
+			restore_cur(currcons);
+			return;
+		case '(':
+			vc_state = ESsetG0;
+			return;
+		case ')':
+			vc_state = ESsetG1;
+			return;
+		case '#':
+			vc_state = EShash;
+			return;
+		case 'c':
+			reset_terminal(currcons,1);
+			return;
+		case '>':  /* Numeric keypad */
+			clr_kbd(kbdapplic);
+			return;
+		case '=':  /* Appl. keypad */
+			set_kbd(kbdapplic);
+			return;
+		}
+		return;
+	case ESnonstd:
+		if (c=='P') {   /* palette escape sequence */
+			for (npar=0; npar<NPAR; npar++)
+				par[npar] = 0 ;
+			npar = 0 ;
+			vc_state = ESpalette;
+			return;
+		} else if (c=='R') {   /* reset palette */
+			reset_palette (currcons);
+			vc_state = ESnormal;
+		} else
+			vc_state = ESnormal;
+		return;
+	case ESpalette:
+		if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) {
+			par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ;
+			if (npar==7) {
+				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];
+				set_palette() ;
+				vc_state = ESnormal;
+			}
+		} else
+			vc_state = ESnormal;
+		return;
+	case ESsquare:
+		for(npar = 0 ; npar < NPAR ; npar++)
+			par[npar] = 0;
+		npar = 0;
+		vc_state = ESgetpars;
+		if (c == '[') { /* Function key */
+			vc_state=ESfunckey;
+			return;
+		}
+		ques = (c=='?');
+		if (ques)
+			return;
+	case ESgetpars:
+		if (c==';' && npar<NPAR-1) {
+			npar++;
+			return;
+		} else if (c>='0' && c<='9') {
+			par[npar] *= 10;
+			par[npar] += c-'0';
+			return;
+		} else vc_state=ESgotpars;
+	case ESgotpars:
+		vc_state = ESnormal;
+		switch(c) {
+		case 'h':
+			set_mode(currcons,1);
+			return;
+		case 'l':
+			set_mode(currcons,0);
+			return;
+		case 'c':
+			if (par[0])
+				cursor_type = par[0] | (par[1]<<8) | (par[2]<<16);
+			else
+				cursor_type = CUR_DEFAULT;
+			set_cursor(currcons);
+			return;
+		case 'n':
+			if (!ques) {
+				if (par[0] == 5)
+					status_report(tty);
+				else if (par[0] == 6)
+					cursor_report(currcons,tty);
+			}
+			return;
+		}
+		if (ques) {
+			ques = 0;
+			return;
+		}
+		switch(c) {
+		case 'G': case '`':
+			if (par[0]) par[0]--;
+			gotoxy(currcons,par[0],y);
+			return;
+		case 'A':
+			if (!par[0]) par[0]++;
+			gotoxy(currcons,x,y-par[0]);
+			return;
+		case 'B': case 'e':
+			if (!par[0]) par[0]++;
+			gotoxy(currcons,x,y+par[0]);
+			return;
+		case 'C': case 'a':
+			if (!par[0]) par[0]++;
+			gotoxy(currcons,x+par[0],y);
+			return;
+		case 'D':
+			if (!par[0]) par[0]++;
+			gotoxy(currcons,x-par[0],y);
+			return;
+		case 'E':
+			if (!par[0]) par[0]++;
+			gotoxy(currcons,0,y+par[0]);
+			return;
+		case 'F':
+			if (!par[0]) par[0]++;
+			gotoxy(currcons,0,y-par[0]);
+			return;
+		case 'd':
+			if (par[0]) par[0]--;
+			gotoxay(currcons,x,par[0]);
+			return;
+		case 'H': case 'f':
+			if (par[0]) par[0]--;
+			if (par[1]) par[1]--;
+			gotoxay(currcons,par[1],par[0]);
+			return;
+		case 'J':
+			csi_J(currcons,par[0]);
+			return;
+		case 'K':
+			csi_K(currcons,par[0]);
+			return;
+		case 'L':
+			csi_L(currcons,par[0]);
+			return;
+		case 'M':
+			csi_M(currcons,par[0]);
+			return;
+		case 'P':
+			csi_P(currcons,par[0]);
+			return;
+		case 'c':
+			if (!par[0])
+				respond_ID(tty);
+			return;
+		case 'g':
+			if (!par[0])
+				tab_stop[x >> 5] &= ~(1 << (x & 31));
+			else if (par[0] == 3) {
+				tab_stop[0] =
+					tab_stop[1] =
+					tab_stop[2] =
+					tab_stop[3] =
+					tab_stop[4] = 0;
+			}
+			return;
+		case 'm':
+			csi_m(currcons);
+			return;
+		case 'q': /* DECLL - but only 3 leds */
+			/* map 0,1,2,3 to 0,1,2,4 */
+			if (par[0] < 4)
+				setledstate(kbd_table + currcons,
+					    (par[0] < 3) ? par[0] : 4);
+			return;
+		case 'r':
+			if (!par[0])
+				par[0]++;
+			if (!par[1])
+				par[1] = video_num_lines;
+			/* Minimum allowed region is 2 lines */
+			if (par[0] < par[1] &&
+			    par[1] <= video_num_lines) {
+				top=par[0]-1;
+				bottom=par[1];
+				gotoxay(currcons,0,0);
+			}
+			return;
+		case 's':
+			save_cur(currcons);
+			return;
+		case 'u':
+			restore_cur(currcons);
+			return;
+		case 'X':
+			csi_X(currcons, par[0]);
+			return;
+		case '@':
+			csi_at(currcons,par[0]);
+			return;
+		case ']': /* setterm functions */
+			setterm_command(currcons);
+			return;
+		}
 		return;
-	console_num = MINOR(tty->device) - (tty->driver.minor_start);
-	if (!vc_cons_allocated(console_num))
+	case ESpercent:
+		vc_state = ESnormal;
+		switch (c) {
+		case '@':  /* defined in ISO 2022 */
+			utf = 0;
+			return;
+		case 'G':  /* prelim official escape code */
+		case '8':  /* retained for compatibility */
+			utf = 1;
+			return;
+		}
 		return;
-#if !CONFIG_AP1000
-	set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
-	set_leds();
-#endif
-}
-
-/*
- * Turn the Scroll-Lock LED off when the console is started
- */
-static void con_start(struct tty_struct *tty)
-{
-	int console_num;
-	if (!tty)
+	case ESfunckey:
+		vc_state = ESnormal;
 		return;
-	console_num = MINOR(tty->device) - (tty->driver.minor_start);
-	if (!vc_cons_allocated(console_num))
+	case EShash:
+		vc_state = ESnormal;
+		if (c == '8') {
+			/* DEC screen alignment test. kludge :-) */
+			video_erase_char =
+				(video_erase_char & 0xff00) | 'E';
+			/* Arno:
+			 * Doesn't work, because csi_J(c,2)
+			 * calls con_clear and doesn't print
+			 * the erase char..	FIXME
+			 */
+			csi_J(currcons, 2);
+			video_erase_char =
+				(video_erase_char & 0xff00) | ' ';
+		}
 		return;
-#if !CONFIG_AP1000
-	clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
-	set_leds();
-#endif
-}
-
-static void con_flush_chars(struct tty_struct *tty)
-{
-	unsigned int currcons;
-	struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
-
-	currcons = vt->vc_num;
-	if (vcmode != KD_GRAPHICS)
-		set_cursor(currcons);
+	case ESsetG0:
+		if (c == '0')
+			G0_charset = GRAF_MAP;
+		else if (c == 'B')
+			G0_charset = LAT1_MAP;
+		else if (c == 'U')
+			G0_charset = IBMPC_MAP;
+		else if (c == 'K')
+			G0_charset = USER_MAP;
+		if (charset == 0)
+			translate = set_translate(G0_charset);
+		vc_state = ESnormal;
+		return;
+	case ESsetG1:
+		if (c == '0')
+			G1_charset = GRAF_MAP;
+		else if (c == 'B')
+			G1_charset = LAT1_MAP;
+		else if (c == 'U')
+			G1_charset = IBMPC_MAP;
+		else if (c == 'K')
+			G1_charset = USER_MAP;
+		if (charset == 1)
+			translate = set_translate(G1_charset);
+		vc_state = ESnormal;
+		return;
+	default:
+		vc_state = ESnormal;
+	}
 }
 
 static int do_con_write(struct tty_struct * tty, int from_user,
 			const unsigned char *buf, int count)
 {
-	int c, tc, ok, n = 0;
+#ifdef VT_BUF_VRAM_ONLY
+#define FLUSH do { } while(0);
+#else
+#define FLUSH if (draw_x >= 0) { \
+	sw->con_putcs(vc_cons[currcons].d, (u16 *)draw_from, (u16 *)draw_to-(u16 *)draw_from, y, draw_x); \
+	draw_x = -1; \
+	}
+#endif
+
+	int c, tc, ok, n = 0, draw_x = -1;
 	unsigned int currcons;
+	unsigned long draw_from = 0, draw_to = 0;
 	struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
 
 #if CONFIG_AP1000
@@ -1530,7 +1693,7 @@
 	}
 
 	/* undraw cursor first */
-	if (currcons == fg_console)
+	if (DO_UPDATE)
 		hide_cursor(currcons);
 
 	/* clear the selection */
@@ -1627,516 +1790,80 @@
 			if (tc & ~console_charmask)
                                 continue; /* Conversion failed */
 
+			if (need_wrap || decim)
+				FLUSH
 			if (need_wrap) {
 				cr(currcons);
 				lf(currcons);
 			}
-
-                        /* DPC: 1994-04-12
-                         *   Speed up overstrike mode, using new putcs.
-                         *
-                         * P.S. I hate 8 spaces per tab! Use Emacs!
-			 */
-
-			/* Only use this for the foreground console,
-                           where we really draw the chars */
-
-                        if (count > 2 &&
-			    !decim && !utf && currcons == fg_console) {
-				char   *p     = putcs_buf;
-				int putcs_count  = 1;
-				ushort nextx  = x + 1;
-
-				*p++ = tc;
-				scr_writew((attr << 8) + tc,
-					   (unsigned short *)pos);
-				pos+=2;
-
-				if (nextx == video_num_columns) {
-					sw->con_putc(vc_cons[currcons].d,
-						     *putcs_buf, y, x);
-					pos-=2;
-					need_wrap = decawm;
-					continue;
-				}
-
-				/* TAB TAB TAB - Arghh!!!! */
-
-				while (count) {
-					enable_bh(CONSOLE_BH);
-					if (from_user)
-						get_user(c, buf);
-					else
-						c = *buf;
-					disable_bh(CONSOLE_BH);
-					tc = translate[toggle_meta ? (c|0x80) : c];
-					if (!tc ||
-					    !(c >= 32
-					      || !(((disp_ctrl ? CTRL_ALWAYS
-						   : CTRL_ACTION) >> c) & 1)))
-					  break;
-					tc = conv_uni_to_pc(tc);
-					if (tc == -4)
-					  tc = conv_uni_to_pc(0xfffd);
-					else if (tc == -3)
-					  tc = c;
-
-					buf++; n++; count--;
-					if (tc & ~console_charmask)
-					  continue; /* Conversion failed */
-
-					*p++ = tc;
-					scr_writew((attr << 8) + tc,
-						   (unsigned short *)pos);
-					pos+=2;
-					++putcs_count;
-					++nextx;
-					if (nextx == video_num_columns ||
-					    putcs_count == sizeof (putcs_buf))
-						break;
-				}
-
-				sw->con_putcs(vc_cons[currcons].d,
-					      putcs_buf, putcs_count, y, x);
-				if (nextx == video_num_columns) {
-					pos-=2;
-					x         = video_num_columns-1;
-					need_wrap = decawm;
-				} else
-					x += putcs_count;
-				continue;
-                        }
-
-                        /* DPC: End of putcs support */
-
 			if (decim)
 				insert_char(currcons, 1);
-			scr_writew( video_mode_512ch ?
+			scr_writew(c = video_mode_512ch ?
 			   ((attr & 0xf7) << 8) + ((tc & 0x100) << 3) +
 			   (tc & 0x0ff) : (attr << 8) + tc,
 			   (unsigned short *) pos);
-			if (currcons == fg_console)
-				sw->con_putc(vc_cons[currcons].d, tc, y, x);
 
-			if (x == video_num_columns - 1)
+			if (DO_UPDATE && draw_x < 0) {
+				draw_x = x;
+				draw_from = pos;
+			}
+			if (x == video_num_columns - 1) {
 				need_wrap = decawm;
-			else {
+				draw_to = pos+2;
+			} else {
 				x++;
-				pos+=2;
+				draw_to = (pos+=2);
 			}
 			continue;
 		}
-
-		/*
-		 *  Control characters can be used in the _middle_
-		 *  of an escape sequence.
-		 */
-		switch (c) {
-			case 0:
-				continue;
-			case 7:
-				if (bell_duration)
-					kd_mksound(bell_pitch, bell_duration);
-				continue;
-			case 8:
-				bs(currcons);
-				continue;
-			case 9:
-				pos -= (x << 1);
-				while (x < video_num_columns - 1) {
-					x++;
-					if (tab_stop[x >> 5] & (1 << (x & 31)))
-						break;
-				}
-				pos += (x << 1);
-				continue;
-			case 10: case 11: case 12:
-				lf(currcons);
-				if (!is_kbd(lnm))
-					continue;
-			case 13:
-				cr(currcons);
-				continue;
-			case 14:
-				charset = 1;
-				translate = set_translate(G1_charset);
-				disp_ctrl = 1;
-				continue;
-			case 15:
-				charset = 0;
-				translate = set_translate(G0_charset);
-				disp_ctrl = 0;
-				continue;
-			case 24: case 26:
-				vc_state = ESnormal;
-				continue;
-			case 27:
-				vc_state = ESesc;
-				continue;
-			case 127:
-				del(currcons);
-				continue;
-			case 128+27:
-				vc_state = ESsquare;
-				continue;
-		}
-		switch(vc_state) {
-			case ESesc:
-				vc_state = ESnormal;
-				switch (c) {
-				  case '[':
-					vc_state = ESsquare;
-					continue;
-				  case ']':
-					vc_state = ESnonstd;
-					continue;
-				  case '%':
-					vc_state = ESpercent;
-					continue;
-				  case 'E':
-					cr(currcons);
-					lf(currcons);
-					continue;
-				  case 'M':
-					ri(currcons);
-					continue;
-				  case 'D':
-					lf(currcons);
-					continue;
-				  case 'H':
-					tab_stop[x >> 5] |= (1 << (x & 31));
-					continue;
-				  case 'Z':
-					respond_ID(tty);
-					continue;
-				  case '7':
-					save_cur(currcons);
-					continue;
-				  case '8':
-					restore_cur(currcons);
-					continue;
-				  case '(':
-					vc_state = ESsetG0;
-					continue;
-				  case ')':
-					vc_state = ESsetG1;
-					continue;
-				  case '#':
-					vc_state = EShash;
-					continue;
-				  case 'c':
-					reset_terminal(currcons,1);
-					continue;
-				  case '>':  /* Numeric keypad */
-					clr_kbd(kbdapplic);
-					continue;
-				  case '=':  /* Appl. keypad */
-					set_kbd(kbdapplic);
-				 	continue;
-				}
-				continue;
-			case ESnonstd:
-				if (c=='P') {   /* palette escape sequence */
-					for (npar=0; npar<NPAR; npar++)
-						par[npar] = 0 ;
-					npar = 0 ;
-					vc_state = ESpalette;
-					continue;
-				} else if (c=='R') {   /* reset palette */
-					reset_palette (currcons);
-					vc_state = ESnormal;
-				} else
-					vc_state = ESnormal;
-				continue;
-			case ESpalette:
-				if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) {
-					par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ;
-					if (npar==7) {
-						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];
-						set_palette() ;
-						vc_state = ESnormal;
-					}
-				} else
-					vc_state = ESnormal;
-				continue;
-			case ESsquare:
-				for(npar = 0 ; npar < NPAR ; npar++)
-					par[npar] = 0;
-				npar = 0;
-				vc_state = ESgetpars;
-				if (c == '[') { /* Function key */
-					vc_state=ESfunckey;
-					continue;
-				}
-				ques = (c=='?');
-				if (ques)
-					continue;
-			case ESgetpars:
-				if (c==';' && npar<NPAR-1) {
-					npar++;
-					continue;
-				} else if (c>='0' && c<='9') {
-					par[npar] *= 10;
-					par[npar] += c-'0';
-					continue;
-				} else vc_state=ESgotpars;
-			case ESgotpars:
-				vc_state = ESnormal;
-				switch(c) {
-					case 'h':
-						set_mode(currcons,1);
-						continue;
-					case 'l':
-						set_mode(currcons,0);
-						continue;
-					case 'c':
-						if (par[0])
-							cursor_type = par[0] | (par[1]<<8) | (par[2]<<16);
-						else
-							cursor_type = CUR_DEFAULT;
-						set_cursor(currcons);
-						continue;
-					case 'n':
-						if (!ques) {
-							if (par[0] == 5)
-								status_report(tty);
-							else if (par[0] == 6)
-								cursor_report(currcons,tty);
-						}
-						continue;
-				}
-				if (ques) {
-					ques = 0;
-					continue;
-				}
-				switch(c) {
-					case 'G': case '`':
-						if (par[0]) par[0]--;
-						gotoxy(currcons,par[0],y);
-						continue;
-					case 'A':
-						if (!par[0]) par[0]++;
-						gotoxy(currcons,x,y-par[0]);
-						continue;
-					case 'B': case 'e':
-						if (!par[0]) par[0]++;
-						gotoxy(currcons,x,y+par[0]);
-						continue;
-					case 'C': case 'a':
-						if (!par[0]) par[0]++;
-						gotoxy(currcons,x+par[0],y);
-						continue;
-					case 'D':
-						if (!par[0]) par[0]++;
-						gotoxy(currcons,x-par[0],y);
-						continue;
-					case 'E':
-						if (!par[0]) par[0]++;
-						gotoxy(currcons,0,y+par[0]);
-						continue;
-					case 'F':
-						if (!par[0]) par[0]++;
-						gotoxy(currcons,0,y-par[0]);
-						continue;
-					case 'd':
-						if (par[0]) par[0]--;
-						gotoxay(currcons,x,par[0]);
-						continue;
-					case 'H': case 'f':
-						if (par[0]) par[0]--;
-						if (par[1]) par[1]--;
-						gotoxay(currcons,par[1],par[0]);
-						continue;
-					case 'J':
-						csi_J(currcons,par[0]);
-						continue;
-					case 'K':
-						csi_K(currcons,par[0]);
-						continue;
-					case 'L':
-						csi_L(currcons,par[0]);
-						continue;
-					case 'M':
-						csi_M(currcons,par[0]);
-						continue;
-					case 'P':
-						csi_P(currcons,par[0]);
-						continue;
-					case 'c':
-						if (!par[0])
-							respond_ID(tty);
-						continue;
-					case 'g':
-						if (!par[0])
-							tab_stop[x >> 5] &= ~(1 << (x & 31));
-						else if (par[0] == 3) {
-							tab_stop[0] =
-							tab_stop[1] =
-							tab_stop[2] =
-							tab_stop[3] =
-							tab_stop[4] = 0;
-						}
-						continue;
-					case 'm':
-						csi_m(currcons);
-						continue;
-					case 'q': /* DECLL - but only 3 leds */
-						/* map 0,1,2,3 to 0,1,2,4 */
-						if (par[0] < 4)
-						  setledstate(kbd_table + currcons,
-							      (par[0] < 3) ? par[0] : 4);
-						continue;
-					case 'r':
-						if (!par[0])
-							par[0]++;
-						if (!par[1])
-							par[1] = video_num_lines;
-						/* Minimum allowed region is 2 lines */
-						if (par[0] < par[1] &&
-						    par[1] <= video_num_lines) {
-							top=par[0]-1;
-							bottom=par[1];
-							gotoxay(currcons,0,0);
-						}
-						continue;
-					case 's':
-						save_cur(currcons);
-						continue;
-					case 'u':
-						restore_cur(currcons);
-						continue;
-					case 'X':
-						csi_X(currcons, par[0]);
-						continue;
-					case '@':
-						csi_at(currcons,par[0]);
-						continue;
-					case ']': /* setterm functions */
-						setterm_command(currcons);
-						continue;
-				}
-				continue;
-			case ESpercent:
-				vc_state = ESnormal;
-				switch (c) {
-				  case '@':  /* defined in ISO 2022 */
-					utf = 0;
-					continue;
-				  case 'G':  /* prelim official escape code */
-				  case '8':  /* retained for compatibility */
-					utf = 1;
-					continue;
-				}
-				continue;
-			case ESfunckey:
-				vc_state = ESnormal;
-				continue;
-			case EShash:
-				vc_state = ESnormal;
-				if (c == '8') {
-					/* DEC screen alignment test. kludge :-) */
-					video_erase_char =
-						(video_erase_char & 0xff00) | 'E';
-					/* Arno:
-					* Doesn't work, because csi_J(c,2)
-					* calls con_clear and doesn't print
-					* the erase char..
-					*/
-					csi_J(currcons, 2);
-					video_erase_char =
-						(video_erase_char & 0xff00) | ' ';
-				}
-				continue;
-			case ESsetG0:
-				if (c == '0')
-					G0_charset = GRAF_MAP;
-				else if (c == 'B')
-					G0_charset = LAT1_MAP;
-				else if (c == 'U')
-					G0_charset = IBMPC_MAP;
-				else if (c == 'K')
-					G0_charset = USER_MAP;
-				if (charset == 0)
-					translate = set_translate(G0_charset);
-				vc_state = ESnormal;
-				continue;
-			case ESsetG1:
-				if (c == '0')
-					G1_charset = GRAF_MAP;
-				else if (c == 'B')
-					G1_charset = LAT1_MAP;
-				else if (c == 'U')
-					G1_charset = IBMPC_MAP;
-				else if (c == 'K')
-					G1_charset = USER_MAP;
-				if (charset == 1)
-					translate = set_translate(G1_charset);
-				vc_state = ESnormal;
-				continue;
-			default:
-				vc_state = ESnormal;
-		}
+		/* FIXME: Handle tabs in a special way and use putcs for them as well */
+		FLUSH
+		do_con_trol(tty, currcons, c);
 	}
+	FLUSH
 	enable_bh(CONSOLE_BH);
 	return n;
+#undef FLUSH
 }
 
-static int con_write(struct tty_struct * tty, int from_user,
-		     const unsigned char *buf, int count)
-{
-	int	retval;
-
-	retval = do_con_write(tty, from_user, buf, count);
-	con_flush_chars(tty);
-
-	return retval;
-}
-
-static void con_put_char(struct tty_struct *tty, unsigned char ch)
-{
-	do_con_write(tty, 0, &ch, 1);
-}
-
-static int con_write_room(struct tty_struct *tty)
-{
-	if (tty->stopped)
-		return 0;
-	return 4096;		/* No limit, really; we're not buffering */
-}
-
-static int con_chars_in_buffer(struct tty_struct *tty)
-{
-	return 0;		/* we're not buffering */
-}
-
-void poke_blanked_console(void)
+/*
+ * This is the console switching bottom half handler.
+ *
+ * Doing console switching in a bottom half handler allows
+ * us to do the switches asynchronously (needed when we want
+ * to switch due to a keyboard interrupt), while still giving
+ * us the option to easily disable it to avoid races when we
+ * need to write to the console.
+ */
+static void console_bh(void)
 {
-	timer_active &= ~(1<<BLANK_TIMER);
-	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
-		return;
-	if (console_blanked) {
-		timer_table[BLANK_TIMER].fn = unblank_screen;
-		timer_table[BLANK_TIMER].expires = 0;
-		timer_active |= 1<<BLANK_TIMER;
-	} else if (blankinterval) {
-		timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
-		timer_active |= 1<<BLANK_TIMER;
+	if (want_console >= 0) {
+		if (want_console != fg_console) {
+			save_screen();
+			change_console(want_console);
+			/* we only changed when the console had already
+			   been allocated - a new console is not created
+			   in an interrupt routine */
+		}
+		want_console = -1;
+	}
+	if (do_poke_blanked_console) { /* do not unblank for a LED change */
+		do_poke_blanked_console = 0;
+		poke_blanked_console();
 	}
 }
 
+/*
+ *	Console on virtual terminal
+ */
+
 #ifdef CONFIG_VT_CONSOLE
 void vt_console_print(struct console *co, const char * b, unsigned count)
 {
 	int currcons = fg_console;
 	unsigned char c;
 	static int printing = 0;
-	const char *start = b;
+	const ushort *start;
 	ushort cnt = 0;
 	ushort myx = x;
 
@@ -2159,36 +1886,37 @@
 
 	/* undraw cursor first */
 	hide_cursor(currcons);
+	
+	start = (ushort *)pos;
 
 	/* Contrived structure to try to emulate original need_wrap behaviour
 	 * Problems caused when we have need_wrap set on '\n' character */
 	while (count--) {
 		c = *b++;
 		if (c == 10 || c == 13 || c == 8 || need_wrap) {
-			if ((cnt = b - start - 1) > 0) {
+			if (cnt > 0) {
 				sw->con_putcs(vc_cons[currcons].d, start, cnt, y, x);
 				x += cnt;
 				if (need_wrap)
 					x--;
+				cnt = 0;
 			}
 			if (c == 8) {		/* backspace */
 				bs(currcons);
-				start = b;
+				start = (ushort *)pos;
 				myx = x;
 				continue;
 			}
 			if (c != 13)
 				lf(currcons);
 			cr(currcons);
-			if (c == 10 || c == 13) {
-				start = b;
-				myx = x;
-				continue;
-			}
-			start = b-1;
+			start = (ushort *)pos;
 			myx = x;
+			if (c == 10 || c == 13)
+				continue;
 		}
 		scr_writew((attr << 8) + c, (unsigned short *) pos);
+		cnt++;
 		if (myx == video_num_columns - 1) {
 			need_wrap = 1;
 			continue;
@@ -2196,7 +1924,7 @@
 		pos+=2;
 		myx++;
 	}
-	if ((cnt = b - start) > 0) {
+	if (cnt > 0) {
 		sw->con_putcs(vc_cons[currcons].d, start, cnt, y, x);
 		x += cnt;
 		if (x == video_num_columns) {
@@ -2204,32 +1932,116 @@
 			need_wrap = 1;
 		}
 	}
-	set_cursor(currcons);
-	poke_blanked_console();
-	printing = 0;
+	set_cursor(currcons);
+	poke_blanked_console();
+	printing = 0;
+}
+
+static kdev_t vt_console_device(struct console *c)
+{
+	return MKDEV(TTY_MAJOR, c->index ? c->index : fg_console + 1);
+}
+
+struct console vt_console_driver = {
+	"tty",
+	vt_console_print,
+	NULL,
+	vt_console_device,
+	keyboard_wait_for_keypress,
+	do_unblank_screen,
+	NULL,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+#endif
+
+/*
+ *	Handling of Linux-specific VC ioctls
+ */
+
+int tioclinux(struct tty_struct *tty, unsigned long arg)
+{
+	char type, data;
+
+	if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
+		return -EINVAL;
+	if (current->tty != tty && !suser())
+		return -EPERM;
+	if (get_user(type, (char *)arg))
+		return -EFAULT;
+	switch (type)
+	{
+		case 2:
+			return set_selection(arg, tty, 1);
+		case 3:
+			return paste_selection(tty);
+		case 4:
+			do_unblank_screen();
+			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.
+	 */
+	 		data = shift_state;
+			return __put_user(data, (char *) arg);
+		case 7:
+			data = mouse_reporting();
+			return __put_user(data, (char *) arg);
+		case 10:
+			set_vesa_blanking(arg);
+			return 0;
+		case 11:	/* set kmsg redirect */
+			if (!suser())
+				return -EPERM;
+			if (get_user(data, (char *)arg+1))
+					return -EFAULT;
+			kmsg_redirect = data;
+			return 0;
+		case 12:	/* get fg_console */
+			return fg_console;
+	}
+	return -EINVAL;
 }
 
-static kdev_t vt_console_device(struct console *c)
+/*
+ *	/dev/ttyN handling
+ */
+
+static int con_write(struct tty_struct * tty, int from_user,
+		     const unsigned char *buf, int count)
 {
-	return MKDEV(TTY_MAJOR, c->index ? c->index : fg_console + 1);
+	int	retval;
+
+	retval = do_con_write(tty, from_user, buf, count);
+	con_flush_chars(tty);
+
+	return retval;
 }
 
-extern int keyboard_wait_for_keypress(struct console *);
+static void con_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	do_con_write(tty, 0, &ch, 1);
+}
 
-struct console vt_console_driver = {
-	"tty",
-	vt_console_print,
-	NULL,
-	vt_console_device,
-	keyboard_wait_for_keypress,
-	do_unblank_screen,
-	NULL,
-	CON_PRINTBUFFER,
-	-1,
-	0,
-	NULL
-};
-#endif
+static int con_write_room(struct tty_struct *tty)
+{
+	if (tty->stopped)
+		return 0;
+	return 4096;		/* No limit, really; we're not buffering */
+}
+
+static int con_chars_in_buffer(struct tty_struct *tty)
+{
+	return 0;		/* we're not buffering */
+}
 
 /*
  * con_throttle and con_unthrottle are only used for
@@ -2247,9 +2059,76 @@
 	wake_up_interruptible(&vt->paste_wait);
 }
 
-static void vc_init(unsigned int currcons, unsigned long rows, unsigned long cols, int do_clear)
+/*
+ * Turn the Scroll-Lock LED on when the tty is stopped
+ */
+static void con_stop(struct tty_struct *tty)
+{
+	int console_num;
+	if (!tty)
+		return;
+	console_num = MINOR(tty->device) - (tty->driver.minor_start);
+	if (!vc_cons_allocated(console_num))
+		return;
+#if !CONFIG_AP1000
+	set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
+	set_leds();
+#endif
+}
+
+/*
+ * Turn the Scroll-Lock LED off when the console is started
+ */
+static void con_start(struct tty_struct *tty)
+{
+	int console_num;
+	if (!tty)
+		return;
+	console_num = MINOR(tty->device) - (tty->driver.minor_start);
+	if (!vc_cons_allocated(console_num))
+		return;
+#if !CONFIG_AP1000
+	clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK);
+	set_leds();
+#endif
+}
+
+static void con_flush_chars(struct tty_struct *tty)
+{
+	unsigned int currcons;
+	struct vt_struct *vt = (struct vt_struct *)tty->driver_data;
+
+	currcons = vt->vc_num;
+	if (vcmode != KD_GRAPHICS)
+		set_cursor(currcons);
+}
+
+/*
+ * Allocate the console screen memory.
+ */
+static int con_open(struct tty_struct *tty, struct file * filp)
+{
+	unsigned int	currcons;
+	int i;
+
+	currcons = MINOR(tty->device) - tty->driver.minor_start;
+
+	i = vc_allocate(currcons, 0);
+	if (i)
+		return i;
+
+	vt_cons[currcons]->vc_num = currcons;
+	tty->driver_data = vt_cons[currcons];
+
+	if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
+		tty->winsize.ws_row = video_num_lines;
+		tty->winsize.ws_col = video_num_columns;
+	}
+	return 0;
+}
+
+static void vc_init(unsigned int currcons, unsigned int rows, unsigned int cols, int do_clear)
 {
-	long base = (long) vc_scrbuf[currcons];
 	int j, k ;
 
 	video_num_columns = cols;
@@ -2257,9 +2136,8 @@
 	video_size_row = cols<<1;
 	video_screen_size = video_num_lines * video_size_row;
 
-	pos = origin = video_mem_start = base;
-	scr_end = base + video_screen_size;
-	video_mem_end = base + video_screen_size;
+	set_origin(currcons);
+	pos = origin;
 	reset_vc(currcons);
 	for (j=k=0; j<16; j++) {
 		vc_cons[currcons].d->vc_palette[k++] = default_red[j] ;
@@ -2274,53 +2152,23 @@
 }
 
 /*
- * This is the console switching bottom half handler.
- *
- * Doing console switching in a bottom half handler allows
- * us to do the switches asynchronously (needed when we want
- * to switch due to a keyboard interrupt), while still giving
- * us the option to easily disable it to avoid races when we
- * need to write to the console.
- */
-static void console_bh(void)
-{
-	if (want_console >= 0) {
-		if (want_console != fg_console) {
-			change_console(want_console);
-			/* we only changed when the console had already
-			   been allocated - a new console is not created
-			   in an interrupt routine */
-		}
-		want_console = -1;
-	}
-	if (do_poke_blanked_console) { /* do not unblank for a LED change */
-		do_poke_blanked_console = 0;
-		poke_blanked_console();
-	}
-}
-
-/*
- *  unsigned long con_init(unsigned long);
- *
  * This routine initializes console interrupts, and does nothing
  * else. If you want the screen to clear, call tty_write with
  * the appropriate escape-sequence.
  *
- * Reads the information preserved by setup.s to determine the current display
- * type and sets everything accordingly.
- *
  * FIXME: return early if we don't _have_ a video card installed.
- *
  */
+
+struct tty_driver console_driver;
+static int console_refcount;
+
 __initfunc(unsigned long con_init(unsigned long kmem_start))
 {
 	const char *display_desc = NULL;
 	unsigned int currcons = 0;
-	char q[2] = { 0, 1 };
 
 	if (conswitchp)
-		kmem_start = conswitchp->con_startup(kmem_start,
-						     &display_desc);
+		display_desc = conswitchp->con_startup();
 	if (!display_desc) {
 		fg_console = 0;
 		return kmem_start;
@@ -2357,7 +2205,7 @@
 		panic("Couldn't register console driver\n");
 
 #if CONFIG_AP1000
-        return(kmem_start);
+	return kmem_start;
 #endif
 
 	timer_table[BLANK_TIMER].fn = blank_screen;
@@ -2367,6 +2215,7 @@
 		timer_active |= 1<<BLANK_TIMER;
 	}
 
+	/* Unfortunately, kmalloc is not running yet */
 	/* Due to kmalloc roundup allocating statically is more efficient -
 	   so provide MIN_NR_CONSOLES for people with very little memory */
 	for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {
@@ -2377,34 +2226,25 @@
 		vt_cons[currcons] = (struct vt_struct *) kmem_start;
 		kmem_start += sizeof(struct vt_struct);
 		visual_init(currcons);
-		vc_scrbuf[currcons] = (unsigned short *) kmem_start;
+		screenbuf = (unsigned short *) kmem_start;
 		kmem_start += video_screen_size;
 		kmalloced = 0;
 		screenbuf_size = video_screen_size;
-       		vc_init(currcons, video_num_lines, video_num_columns, currcons);
+		vc_init(currcons, video_num_lines, video_num_columns, 
+			currcons || !sw->con_save_screen);
 		for (j=k=0; j<16; j++) {
 			vc_cons[currcons].d->vc_palette[k++] = default_red[j] ;
 			vc_cons[currcons].d->vc_palette[k++] = default_grn[j] ;
 			vc_cons[currcons].d->vc_palette[k++] = default_blu[j] ;
 		}
 	}
-
 	currcons = fg_console = 0;
-
-	gotoxy(currcons,0,0);
+	save_screen();
+	gotoxy(currcons,x,y);
 	csi_J(currcons, 0);
 	update_screen(fg_console);
-
-#if 0
-/* The logo is too ugly to live */
-	if (console_show_logo)
-	    q[1] += console_show_logo();
-	conswitchp->con_putcs(vc_cons[fg_console].d, linux_logo_banner,
-			      sizeof(linux_logo_banner)-1, q[1]-1, q[0]);
-	putconsxy(0, q);
-#endif
-	sw->con_cursor(vc_cons[currcons].d, CM_DRAW);
-	printk("Console: %s %s %ldx%ld",
+	set_cursor(currcons);
+	printk("Console: %s %s %dx%d",
 		can_do_color ? "colour" : "mono",
 		display_desc, video_num_columns, video_num_lines);
 	printable = 1;
@@ -2415,9 +2255,14 @@
 #endif
 
 	init_bh(CONSOLE_BH, console_bh);
+	
 	return kmem_start;
 }
 
+/*
+ *	Screen blanking
+ */
+
 void set_vesa_blanking(unsigned long arg)
 {
     char *argp = (char *)arg + 1;
@@ -2480,9 +2325,8 @@
 		timer_table[BLANK_TIMER].fn = unblank_screen;
 	}
 
-	/* try not to lose information by blanking, and not to waste memory */
 	currcons = fg_console;
-	has_scrolled = 0;
+	save_screen();
 	sw->con_blank(1);
 	console_blanked = fg_console + 1;
 
@@ -2534,84 +2378,24 @@
 	do_unblank_screen();
 }
 
-void update_screen(int new_console)
+void poke_blanked_console(void)
 {
-	int currcons = fg_console;
-	int xx, yy, startx, attr_save;
-	char *bufp;
-	unsigned short *p;
-	static int lock = 0;
-
-	if (lock)
-		return;
-	if (!vc_cons_allocated(new_console)) {
-		/* strange ... */
-		printk("update_screen: tty %d not allocated ??\n", new_console+1);
+	timer_active &= ~(1<<BLANK_TIMER);
+	if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
 		return;
+	if (console_blanked) {
+		timer_table[BLANK_TIMER].fn = unblank_screen;
+		timer_table[BLANK_TIMER].expires = 0;
+		timer_active |= 1<<BLANK_TIMER;
+	} else if (blankinterval) {
+		timer_table[BLANK_TIMER].expires = jiffies + blankinterval;
+		timer_active |= 1<<BLANK_TIMER;
 	}
-	lock = 1;
-
-	clear_selection();
-
-	currcons = fg_console = new_console;
-	sw->con_cursor (vc_cons[currcons].d, CM_ERASE);
-	sw->con_switch (vc_cons[new_console].d);
-	/* Update the screen contents */
-	p = (unsigned short *)video_mem_start;
-	attr_save = attr;
-	for (yy = 0; yy < video_num_lines; yy++) {
-	    bufp = putcs_buf;
-	    for (startx = xx = 0; xx < video_num_columns; xx++) {
-		if (attr != ((scr_readw(p) >> 8) & 0xff)) {
-		    if (bufp > putcs_buf)
-		      sw->con_putcs (vc_cons[currcons].d, putcs_buf,
-				     bufp - putcs_buf, yy, startx);
-		    startx = xx;
-		    bufp = putcs_buf;
-		    attr = (scr_readw(p) >> 8) & 0xff;
-		}
-		*bufp++ = scr_readw(p++);
-		if (bufp == putcs_buf + sizeof (putcs_buf)) {
-		    sw->con_putcs (vc_cons[currcons].d, putcs_buf,
-				   bufp - putcs_buf, yy, startx);
-		    startx = xx + 1;
-		    bufp = putcs_buf;
-		}
-	    }
-	    if (bufp > putcs_buf)
-		sw->con_putcs (vc_cons[currcons].d, putcs_buf,
-			       bufp - putcs_buf, yy, startx);
-	}
-	set_cursor (currcons);
-	attr = attr_save;
-	set_leds();
-	compute_shiftstate();
-	lock = 0;
 }
 
 /*
- * Allocate the console screen memory.
+ *	Palettes
  */
-static int con_open(struct tty_struct *tty, struct file * filp)
-{
-	unsigned int	currcons;
-	int i;
-
-	currcons = MINOR(tty->device) - tty->driver.minor_start;
-
-	i = vc_allocate(currcons);
-	if (i)
-		return i;
-
-	vt_cons[currcons]->vc_num = currcons;
-	tty->driver_data = vt_cons[currcons];
-
-	if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
-		tty->winsize.ws_row = video_num_lines;
-		tty->winsize.ws_col = video_num_columns;
-	}
-	return 0;
-}
 
 void set_palette(void)
 {
@@ -2673,7 +2457,7 @@
 }
 
 /*
- *  PIO_FONT support.
+ *  Font switching
  *
  *  Currently we only support 8 pixels wide fonts, at a maximum height
  *  of 32 pixels. Userspace fontdata is stored with 32 bytes reserved
@@ -2687,63 +2471,111 @@
 
 #define cmapsz 8192
 
-int set_get_font(unsigned char * arg, int set, int ch512)
+int con_set_font (char *arg, int w, int h, int chars)
 {
-    int i, unit, size;
-    char *charmap;
-
-    if (!arg)
-	return -EINVAL;
-
-
-    size = ch512 ? 2*cmapsz : cmapsz;
-
-    charmap = (char *)kmalloc(size, GFP_USER);
+	int ch512;
+	int i = -EINVAL;
+	int s = cmapsz;
+	char *charmap;
+
+	if (vt_cons[fg_console]->vc_mode != KD_TEXT)
+		goto quit;
+	if (w != 8 || h < 0 || h >= 32)
+		goto quit;
+	if (chars == 256)
+		ch512 = 0;
+	else if (chars == 512) {
+		ch512 = 1;
+		s += s;
+	} else
+		goto quit;
+	s = ch512 ? 2*cmapsz : cmapsz;
+	charmap = kmalloc(s, GFP_USER);
+	if (!charmap)
+		return -ENOMEM;
+	i = -EFAULT;
+	if (copy_from_user(charmap, arg, s))
+		goto done;
+	if (!h) {			/* No height given, try guessing */
+		for (h = 32; h > 0; h--)
+			for (i = 0; i < chars; i++)
+				if (charmap[32*i+h-1])
+					goto nonzero;
+		return -EINVAL;		/* Empty fonts not allowed */
+	}
+	nonzero:
+	i = conswitchp->con_set_font(vc_cons[fg_console].d, w, h, charmap);
+	if ( !i ) {
+		hashtable_contents_valid = 0;
+		video_mode_512ch = ch512;
+		console_charmask = ch512 ? 0x1ff : 0x0ff;
+	}
+done:
+	kfree(charmap);
+quit:
+	return i;
+}
 
-    if (set) {
-	if (copy_from_user(charmap, arg, size)) {
-	    kfree(charmap);
-	    return -EFAULT;
-	}
-
-	for (unit = 32; unit > 0; unit--)
-	    for (i = 0; i < (ch512 ? 512 : 256); i++)
-		if (charmap[32*i+unit-1])
-		    goto nonzero;
-    nonzero:
-	i = conswitchp->con_set_font(vc_cons[fg_console].d, 8, unit, charmap);
-    } else {
-	memset(charmap, 0, size);
-	i = conswitchp->con_get_font(vc_cons[fg_console].d, &unit, &unit,
-				     charmap);
-	if (i == 0 && copy_to_user(arg, charmap, size))
-	    i = -EFAULT;
-    }
-    kfree(charmap);
+int con_get_font (char *arg, int *w, int *h, int *chars)
+{
+	int num = video_mode_512ch ? 512 : 256;
+	char *charmap;
+	int s = video_mode_512ch ? 2*cmapsz : cmapsz;
+	int i;
 
-    return i;
+	if (num < *chars)
+		return -ENOSPC;
+	if (vt_cons[fg_console]->vc_mode != KD_TEXT)
+		return -EINVAL;
+	*chars = num;
+	*w = 8;
+	charmap = kmalloc(s, GFP_USER);
+	if (!charmap)
+		return -ENOMEM;
+	memset(charmap, 0, s);
+	i = conswitchp->con_get_font(vc_cons[fg_console].d, w, h, charmap);
+	if (!i && copy_to_user(arg, charmap, s))
+		i = -EINVAL;
+	kfree(charmap);
+	return i;
 }
 
 /*
- * Load font into the EGA/VGA character generator. arg points to a 8192
- * byte map, 32 bytes per character. Only first H of them are used for
- * 8xH fonts (0 < H <= 32).
+ *	Interface exported to selection and vcs.
  */
 
-int con_set_font (char *arg, int ch512)
+/* used by selection */
+unsigned short screen_word(int currcons, int offset, int viewed)
 {
-	int i;
+	return scr_readw(screenpos(currcons, offset, viewed));
+}
 
-	i = set_get_font (arg,1,ch512);
-  	if ( !i ) {
-		hashtable_contents_valid = 0;
-      		video_mode_512ch = ch512;
-      		console_charmask = ch512 ? 0x1ff : 0x0ff;
-	}
-	return i;
+/* used by vcs - note the word offset */
+unsigned short *screen_pos(int currcons, int w_offset, int viewed)
+{
+	return screenpos(currcons, 2 * w_offset, viewed);
+}
+
+void getconsxy(int currcons, char *p)
+{
+	p[0] = x;
+	p[1] = y;
 }
 
-int con_get_font (char *arg)
+void putconsxy(int currcons, char *p)
 {
-	return set_get_font (arg,0,video_mode_512ch);
+	gotoxy(currcons, p[0], p[1]);
+	set_cursor(currcons);
 }
+
+
+/*
+ *	Visible symbols for modules
+ */
+
+EXPORT_SYMBOL(color_table);
+EXPORT_SYMBOL(default_red);
+EXPORT_SYMBOL(default_grn);
+EXPORT_SYMBOL(default_blu);
+EXPORT_SYMBOL(video_font_height);
+EXPORT_SYMBOL(video_scan_lines);
diff -u --recursive --new-file v2.1.108/linux/drivers/char/console_macros.h linux/drivers/char/console_macros.h
--- v2.1.108/linux/drivers/char/console_macros.h	Wed Jun 24 22:54:04 1998
+++ linux/drivers/char/console_macros.h	Fri Jul 10 15:18:31 1998
@@ -1,7 +1,10 @@
 #define cons_num	(vc_cons[currcons].d->vc_num)
 #define sw		(vc_cons[currcons].d->vc_sw)
+#define screenbuf	(vc_cons[currcons].d->vc_screenbuf)
 #define screenbuf_size	(vc_cons[currcons].d->vc_screenbuf_size)
 #define origin		(vc_cons[currcons].d->vc_origin)
+#define scr_top		(vc_cons[currcons].d->vc_scr_top)
+#define visible_origin  (vc_cons[currcons].d->vc_visible_origin)
 #define scr_end		(vc_cons[currcons].d->vc_scr_end)
 #define pos		(vc_cons[currcons].d->vc_pos)
 #define top		(vc_cons[currcons].d->vc_top)
@@ -35,7 +38,6 @@
 #define decim		(vc_cons[currcons].d->vc_decim)
 #define deccolm		(vc_cons[currcons].d->vc_deccolm)
 #define need_wrap	(vc_cons[currcons].d->vc_need_wrap)
-#define has_scrolled	(vc_cons[currcons].d->vc_has_scrolled)
 #define kmalloced	(vc_cons[currcons].d->vc_kmalloced)
 #define report_mouse	(vc_cons[currcons].d->vc_report_mouse)
 #define color		(vc_cons[currcons].d->vc_color)
diff -u --recursive --new-file v2.1.108/linux/drivers/char/fbmem.c linux/drivers/char/fbmem.c
--- v2.1.108/linux/drivers/char/fbmem.c	Wed Jul  1 19:38:53 1998
+++ linux/drivers/char/fbmem.c	Fri Jul 10 15:18:30 1998
@@ -19,6 +19,8 @@
 #include <linux/malloc.h>
 #include <linux/mman.h>
 #include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/console_struct.h>
 #include <linux/init.h>
 #ifdef CONFIG_PROC_FS
 #include <linux/proc_fs.h>
@@ -44,50 +46,57 @@
      *  Frame buffer device initialization and setup routines
      */
 
-extern unsigned long amifb_init(unsigned long mem_start);
+extern unsigned long acornfb_init(void);
+extern void acornfb_setup(char *options, int *ints);
+extern void amifb_init(void);
 extern void amifb_setup(char *options, int *ints);
-extern unsigned long atafb_init(unsigned long mem_start);
+extern void atafb_init(void);
 extern void atafb_setup(char *options, int *ints);
-extern unsigned long macfb_init(unsigned long mem_start);
+extern void macfb_init(void);
 extern void macfb_setup(char *options, int *ints);
-extern unsigned long cyberfb_init(unsigned long mem_start);
+extern void cyberfb_init(void);
 extern void cyberfb_setup(char *options, int *ints);
-extern unsigned long retz3fb_init(unsigned long mem_start);
+extern void retz3fb_init(void);
 extern void retz3fb_setup(char *options, int *ints);
-extern unsigned long clgenfb_init(unsigned long mem_start);
+extern void clgenfb_init(void);
 extern void clgenfb_setup(char *options, int *ints);
-extern unsigned long vfb_init(unsigned long mem_start);
+extern void vfb_init(void);
 extern void vfb_setup(char *options, int *ints);
-extern unsigned long offb_init(unsigned long mem_start);
+extern void offb_init(void);
 extern void offb_setup(char *options, int *ints);
-extern unsigned long atyfb_init(unsigned long mem_start);
+extern void atyfb_init(void);
 extern void atyfb_setup(char *options, int *ints);
-extern unsigned long dnfb_init(unsigned long mem_start);
-extern unsigned long tgafb_init(unsigned long mem_start);
-extern unsigned long virgefb_init(unsigned long mem_start);
+extern void dnfb_init(void);
+extern void tgafb_init(void);
+extern void virgefb_init(void);
 extern void virgefb_setup(char *options, int *ints);
 extern void resolver_video_setup(char *options, int *ints);
-extern unsigned long s3triofb_init(unsigned long mem_start);
+extern void s3triofb_init(void);
 extern void s3triofb_setup(char *options, int *ints);
-extern unsigned long vgafb_init(unsigned long mem_start);
+extern void vgafb_init(void);
 extern void vgafb_setup(char *options, int *ints);
-extern unsigned long vesafb_init(unsigned long mem_start);
+extern void vesafb_init(void);
 extern void vesafb_setup(char *options, int *ints);
-extern unsigned long mdafb_init(unsigned long mem_start);
+extern void mdafb_init(void);
 extern void mdafb_setup(char *options, int *ints);
-extern unsigned long hpfb_init(unsigned long mem_start);
+extern void hpfb_init(void);
 extern void hpfb_setup(char *options, int *ints);
-
-
+extern void sbusfb_init(void);
+extern void sbusfb_setup(char *options, int *ints);
+extern void promfb_init(void);
+extern void promfb_setup(char *options, int *ints);
 
 static struct {
 	const char *name;
-	unsigned long (*init)(unsigned long mem_start);
+	void (*init)(void);
 	void (*setup)(char *options, int *ints);
 } fb_drivers[] __initdata = {
 #ifdef CONFIG_FB_RETINAZ3
 	{ "retz3", retz3fb_init, retz3fb_setup },
 #endif
+#ifdef CONFIG_FB_ACORN
+	{ "acorn", acornfb_init, acornfb_setup },
+#endif
 #ifdef CONFIG_FB_AMIGA
 	{ "amifb", amifb_init, amifb_setup },
 #endif
@@ -103,9 +112,6 @@
 #ifdef CONFIG_FB_CLGEN
 	{ "clgen", clgenfb_init, clgenfb_setup },
 #endif
-#ifdef CONFIG_FB_VIRTUAL
-	{ "vfb", vfb_init, vfb_setup },
-#endif
 #ifdef CONFIG_FB_OF
 	{ "offb", offb_init, offb_setup },
 #endif
@@ -136,15 +142,25 @@
 #ifdef CONFIG_FB_HP300
 	{ "hpfb", hpfb_init, hpfb_setup },
 #endif 
+#ifdef CONFIG_FB_SBUS
+	{ "sbus", sbusfb_init, sbusfb_setup },
+#endif
+#ifdef CONFIG_FB_PROM
+	{ "prom", promfb_init, promfb_setup },
+#endif
 #ifdef CONFIG_GSP_RESOLVER
 	/* Not a real frame buffer device... */
 	{ "resolver", NULL, resolver_video_setup },
 #endif
+#ifdef CONFIG_FB_VIRTUAL
+	/* Must be last to avoid that vfb becomes your primary display */
+	{ "vfb", vfb_init, vfb_setup },
+#endif
 };
 
 #define NUM_FB_DRIVERS	(sizeof(fb_drivers)/sizeof(*fb_drivers))
 
-static fb_init_func *pref_init_funcs[FB_MAX];
+static void (*pref_init_funcs[FB_MAX])(void);
 static int num_pref_init_funcs __initdata = 0;
 
 
@@ -246,9 +262,9 @@
     if (newidx != con2fb_map[unit]) {
        oldfb = registered_fb[oldidx];
        newfb = registered_fb[newidx];
-       if (newfb->fbops->fb_open(newfb))
+       if (newfb->fbops->fb_open(newfb,0))
 	   return;
-       oldfb->fbops->fb_release(oldfb);
+       oldfb->fbops->fb_release(oldfb,0);
        conp = fb_display[unit].conp;
        con2fb_map[unit] = newidx;
        fb_display[unit] = *(newfb->disp);
@@ -270,7 +286,7 @@
 	sprintf(modname, "fb%d", fb);
 	request_module(modname);
 }
-#endif
+#endif /* CONFIG_KMOD */
 
 static int 
 fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
@@ -340,7 +356,7 @@
 #ifdef CONFIG_KMOD
 		if (!registered_fb[con2fb.framebuffer])
 		    try_to_load(con2fb.framebuffer);
-#endif
+#endif /* CONFIG_KMOD */
 		if (!registered_fb[con2fb.framebuffer])
 		    return -EINVAL;
 		if (con2fb.console != 0)
@@ -369,6 +385,8 @@
 
 	if (!fb)
 		return -ENODEV;
+	if (fb->fb_mmap)
+		return fb->fb_mmap(info, file, vma);
 	fb->fb_get_fix(&fix, PROC_CONSOLE(), info);
 
 	/* frame buffer memory */
@@ -402,6 +420,11 @@
 	}
 #elif defined(__powerpc__)
 	pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE|_PAGE_GUARDED;
+#elif defined(__alpha__)
+	/* Caching is off in the I/O space quadrant by design.  */
+#elif defined(__sparc__)
+	/* Should never get here, all fb drivers should have their own
+	   mmap routines */
 #else
 #warning What do we have to do here??
 #endif
@@ -422,10 +445,10 @@
 #ifdef CONFIG_KMOD
 	if (!(info = registered_fb[fbidx]))
 		try_to_load(fbidx);
-#endif
+#endif /* CONFIG_KMOD */
 	if (!(info = registered_fb[fbidx]))
 		return -ENODEV;
-	return info->fbops->fb_open(info);
+	return info->fbops->fb_open(info,1);
 }
 
 static int 
@@ -434,7 +457,7 @@
 	int fbidx = GET_FB_IDX(inode->i_rdev);
 	struct fb_info *info = registered_fb[fbidx];
 
-	info->fbops->fb_release(info);
+	info->fbops->fb_release(info,1);
 	return 0;
 }
 
@@ -451,11 +474,33 @@
 	NULL		/* fsync 	*/
 };
 
+static inline void take_over_console(struct consw *sw)
+{
+    int i;
+    extern void set_palette(void);
+
+    conswitchp = sw;
+    conswitchp->con_startup();
+
+    for (i = 0; i < MAX_NR_CONSOLES; i++) {
+	if (!vc_cons[i].d || !vc_cons[i].d->vc_sw)
+	    continue;
+	if (i == fg_console &&
+	    vc_cons[i].d->vc_sw->con_save_screen)
+		vc_cons[i].d->vc_sw->con_save_screen(vc_cons[i].d);
+	vc_cons[i].d->vc_sw->con_deinit(vc_cons[i].d);
+	vc_cons[i].d->vc_sw = conswitchp;
+	vc_cons[i].d->vc_sw->con_init(vc_cons[i].d, 0);
+    }
+    set_palette();
+}
+
 int
 register_framebuffer(struct fb_info *fb_info)
 {
 	int i, j;
 	static int fb_ever_opened[FB_MAX];
+	static int first = 1;
 
 	if (num_registered_fb == FB_MAX)
 		return -ENXIO;
@@ -472,9 +517,15 @@
 		 */
 		for (j = 0; j < MAX_NR_CONSOLES; j++)
 			if (con2fb_map[j] == i)
-				fb_info->fbops->fb_open(fb_info);
+				fb_info->fbops->fb_open(fb_info,0);
 		fb_ever_opened[i] = 1;
 	}
+
+	if (first) {
+		first = 0;
+		take_over_console(&fb_con);
+	}
+
 	return 0;
 }
 
@@ -501,6 +552,8 @@
 __initfunc(void
 fbmem_init(void))
 {
+	int i;
+
 #ifdef CONFIG_PROC_FS
 	proc_fbmem = create_proc_entry("fb", 0, 0);
 	if (proc_fbmem)
@@ -509,6 +562,16 @@
 
 	if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
 		printk("unable to get major %d for fb devs\n", FB_MAJOR);
+
+	/*
+	 *  Probe for all builtin frame buffer devices
+	 */
+	for (i = 0; i < num_pref_init_funcs; i++)
+		pref_init_funcs[i]();
+
+	for (i = 0; i < NUM_FB_DRIVERS; i++)
+		if (fb_drivers[i].init)
+			fb_drivers[i].init();
 }
 
 
@@ -545,25 +608,6 @@
 
 
     /*
-     *  Probe for all builtin frame buffer devices
-     */
-
-__initfunc(unsigned long probe_framebuffers(unsigned long kmem_start))
-{
-    int i;
-
-    for (i = 0; i < num_pref_init_funcs; i++)
-	    kmem_start = pref_init_funcs[i](kmem_start);
-
-    for (i = 0; i < NUM_FB_DRIVERS; i++)
-	    if (fb_drivers[i].init)
-		    kmem_start = fb_drivers[i].init(kmem_start);
-
-    return kmem_start;
-}
-
-
-    /*
      *  Command line options
      */
 
@@ -609,7 +653,7 @@
     /*
      * If we get here no fb was specified and we default to pass the
      * options to the first frame buffer that has an init and a setup
-     * fuction.
+     * function.
      */
     for (i = 0; i < NUM_FB_DRIVERS; i++) {
 	    if (fb_drivers[i].init && fb_drivers[i].setup) {
diff -u --recursive --new-file v2.1.108/linux/drivers/char/hfmodem/refclock.c linux/drivers/char/hfmodem/refclock.c
--- v2.1.108/linux/drivers/char/hfmodem/refclock.c	Fri May  8 23:14:47 1998
+++ linux/drivers/char/hfmodem/refclock.c	Fri Jul 10 14:01:13 1998
@@ -30,6 +30,7 @@
 #include <linux/sched.h>
 #include <linux/time.h>
 #include <linux/hfmodem.h>
+#include <asm/processor.h>
 
 /* --------------------------------------------------------------------- */
 
@@ -67,7 +68,7 @@
 #ifdef __i386__
 __initfunc(static void i386_capability(void))
 {
-	if (boot_cpu_data.x86_capability & 0x10)
+	if (boot_cpu_data.x86_capability & X86_FEATURE_TSC))
 		rdtsc_ok = 1;
 	else
 		printk(KERN_INFO "%s: cpu does not support the rdtsc instruction\n", hfmodem_drvname);
diff -u --recursive --new-file v2.1.108/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- v2.1.108/linux/drivers/char/keyboard.c	Fri Jan 30 11:28:07 1998
+++ linux/drivers/char/keyboard.c	Fri Jul 10 15:18:31 1998
@@ -58,9 +58,6 @@
 #endif
 
 extern void ctrl_alt_del(void);
-extern void reset_vc(unsigned int new_console);
-extern void scrollback(int);
-extern void scrollfront(int);
 
 struct wait_queue * keypress_wait = NULL;
 struct console;
@@ -199,7 +196,7 @@
 	mark_bh(CONSOLE_BH);
 	add_keyboard_randomness(scancode);
 
-	tty = ttytab[fg_console];
+	tty = ttytab? ttytab[fg_console]: NULL;
  	kbd = kbd_table + fg_console;
 	if ((raw_mode = (kbd->kbdmode == VC_RAW))) {
  		put_queue(scancode);
diff -u --recursive --new-file v2.1.108/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v2.1.108/linux/drivers/char/lp.c	Wed Jul  1 19:38:54 1998
+++ linux/drivers/char/lp.c	Fri Jul 10 11:25:07 1998
@@ -323,7 +323,7 @@
 		return -ENXIO;
 
 	lp_table[minor].last_error = 0;
-	lp_table[minor].irq_detected = 0;
+	lp_table[minor].irq_detected = 1;
 	lp_table[minor].irq_missed = 0;
 
 	w_ctr(minor, LP_PSELECP | LP_PINITP);
diff -u --recursive --new-file v2.1.108/linux/drivers/char/random.c linux/drivers/char/random.c
--- v2.1.108/linux/drivers/char/random.c	Wed Jun 24 22:54:05 1998
+++ linux/drivers/char/random.c	Fri Jul 10 14:01:13 1998
@@ -243,6 +243,7 @@
 #include <linux/poll.h>
 #include <linux/init.h>
 
+#include <asm/processor.h>
 #include <asm/uaccess.h>
 #include <asm/irq.h>
 #include <asm/io.h>
@@ -701,7 +702,7 @@
 	begin_benchmark(&timer_benchmark);
 #endif
 #if defined (__i386__)
-	if (boot_cpu_data.x86_capability & 16) {
+	if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) {
 		__u32 high;
 		__asm__(".byte 0x0f,0x31"
 			:"=a" (time), "=d" (high));
diff -u --recursive --new-file v2.1.108/linux/drivers/char/rocket.c linux/drivers/char/rocket.c
--- v2.1.108/linux/drivers/char/rocket.c	Thu May  7 22:51:49 1998
+++ linux/drivers/char/rocket.c	Fri Jul 10 10:48:37 1998
@@ -48,6 +48,9 @@
 
 #if (LINUX_VERSION_CODE > 66304)
 #define NEW_MODULES
+#ifdef LOCAL_ROCKET_H		/* We're building standalone */
+#define MODULE
+#endif
 #endif
 
 #ifdef NEW_MODULES
@@ -81,6 +84,9 @@
 #include <linux/ioport.h>
 #ifdef ENABLE_PCI
 #include <linux/pci.h>
+#if (LINUX_VERSION_CODE < 0x020163) /* 2.1.99 */
+#include <linux/bios32.h>
+#endif
 #endif
 #if (LINUX_VERSION_CODE >= 131343) /* 2.1.15 -- XX get correct version */
 #include <linux/init.h>
@@ -89,12 +95,17 @@
 #endif
 	
 #include "rocket_int.h"
+#ifdef LOCAL_ROCKET_H
+#include "rocket.h"
+#include "version.h"
+#else
 #include <linux/rocket.h>
-
-#define ROCKET_VERSION "1.14a"
-#define ROCKET_DATE "19-Jul-97"
+#define ROCKET_VERSION "1.14b"
+#define ROCKET_DATE "29-Jun-98"
+#endif /* LOCAL_ROCKET_H */
 
 #define ROCKET_PARANOIA_CHECK
+#define ROCKET_SOFT_FLOW
 
 #undef ROCKET_DEBUG_OPEN
 #undef ROCKET_DEBUG_INTR
@@ -177,9 +188,30 @@
 static unsigned long time_counter = 0;
 #endif
 
+#if ((LINUX_VERSION_CODE > 0x020111) && defined(MODULE))
+MODULE_AUTHOR("Theodore Ts'o");
+MODULE_DESCRIPTION("Comtrol Rocketport driver");
+MODULE_PARM(board1,     "i");
+MODULE_PARM_DESC(board1, "I/O port for (ISA) board #1");
+MODULE_PARM(board2,     "i");
+MODULE_PARM_DESC(board2, "I/O port for (ISA) board #2");
+MODULE_PARM(board3,     "i");
+MODULE_PARM_DESC(board3, "I/O port for (ISA) board #3");
+MODULE_PARM(board4,     "i");
+MODULE_PARM_DESC(board4, "I/O port for (ISA) board #4");
+MODULE_PARM(controller, "i");
+MODULE_PARM_DESC(controller, "I/O port for (ISA) rocketport controller");
+MODULE_PARM(support_low_speed, "i");
+MODULE_PARM_DESC(support_low_speed, "0 means support 50 baud, 1 means support 460400 baud");	
+#endif
+
 /*
  * Provide backwards compatibility for kernels prior to 2.1.8.
  */
+#if (LINUX_VERSION_CODE < 0x20000)
+typedef dev_t kdev_t;
+#endif
+
 #if (LINUX_VERSION_CODE < 131336)
 int copy_from_user(void *to, const void *from_user, unsigned long len)
 {
@@ -202,6 +234,12 @@
 	memcpy_tofs(to_user, from, len);
 	return 0;
 }
+
+static inline int signal_pending(struct task_struct *p)
+{
+	return (p->signal & (~p->blocked != 0));
+}
+
 #else
 #include <asm/uaccess.h>
 #endif
@@ -720,6 +758,27 @@
 		info->intmask |= DELTA_CD;
 		restore_flags(flags);
 	}
+
+	/*
+	 * Handle software flow control in the board
+	 */
+#ifdef ROCKET_SOFT_FLOW
+	if (I_IXON(info->tty)) {
+		sEnTxSoftFlowCtl(cp);
+		if (I_IXANY(info->tty)) {
+			sEnIXANY(cp);
+		} else {
+			sDisIXANY(cp);
+		}
+		sSetTxXONChar(cp, START_CHAR(info->tty));
+		sSetTxXOFFChar(cp, STOP_CHAR(info->tty));
+	} else {
+		sDisTxSoftFlowCtl(cp);
+		sDisIXANY(cp);
+		sClrTxXOFF(cp);
+	}
+#endif
+	
 	/*
 	 * Set up ignore/read mask words
 	 */
@@ -728,7 +787,7 @@
 		info->read_status_mask |= STMFRAMEH | STMPARITYH;
 	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
 		info->read_status_mask |= STMBREAKH;
-	
+
 	/*
 	 * Characters to ignore
 	 */
@@ -751,7 +810,7 @@
 {
 	struct wait_queue wait = { current, NULL };
 	int		retval;
-	int		do_clocal = 0;
+	int		do_clocal = 0, extra_count = 0;
 	unsigned long	flags;
 
 	/*
@@ -820,8 +879,10 @@
 	       info->line, info->count);
 #endif
 	save_flags(flags); cli();
-	if (!tty_hung_up_p(filp))
+	if (!tty_hung_up_p(filp)) {
+		extra_count = 1;
 		info->count--;
+	}
 	restore_flags(flags);
 	info->blocked_open++;
 	while (1) {
@@ -857,7 +918,7 @@
 	current->state = TASK_RUNNING;
 	remove_wait_queue(&info->open_wait, &wait);
 	cli();
-	if (!tty_hung_up_p(filp))
+	if (extra_count)
 		info->count++;
 	restore_flags(flags);
 	info->blocked_open--;
@@ -1315,7 +1376,12 @@
 	if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
 		return -EFAULT;
 
-	if (!capable(CAP_SYS_ADMIN)) {
+#ifdef CAP_SYS_ADMIN
+	if (!capable(CAP_SYS_ADMIN))
+#else
+	if (!suser())
+#endif
+	{
 		if ((new_serial.flags & ~ROCKET_USR_MASK) !=
 		    (info->flags & ~ROCKET_USR_MASK))
 			return -EPERM;
@@ -1754,7 +1820,6 @@
 	
 	save_flags(flags);
 	while (1) {
-		cli();
 		if (info->tty == 0) {
 			restore_flags(flags);
 			goto end;
@@ -1778,10 +1843,10 @@
 			/* In case we got pre-empted */
 			if (info->tty == 0)
 				goto end_intr;
-			c = MIN(c, MIN(XMIT_BUF_SIZE - info->xmit_cnt - 1,
-				       XMIT_BUF_SIZE - info->xmit_head));
-				
 		}
+		cli();
+		c = MIN(c, MIN(XMIT_BUF_SIZE - info->xmit_cnt - 1,
+			       XMIT_BUF_SIZE - info->xmit_head));
 		memcpy(info->xmit_buf + info->xmit_head, b, c);
 		info->xmit_head = (info->xmit_head + c) & (XMIT_BUF_SIZE-1);
 		info->xmit_cnt += c;
@@ -1868,6 +1933,35 @@
 }
 
 #ifdef ENABLE_PCI
+#if (LINUX_VERSION_CODE < 0x020163) /* 2.1.99 */
+/* For compatibility */
+static struct pci_dev *pci_find_slot(char bus, char device_fn)
+{
+	unsigned short		vendor_id, device_id;
+	int			ret, error;
+	static struct pci_dev	ret_struct;
+	
+	error = pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID,
+		&vendor_id);
+	ret = pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID,
+		&device_id);
+	if (error == 0)
+		error = ret;
+
+	if (error) {
+		printk("PCI RocketPort error: %s not initializing due to error"
+		       "reading configuration space\n",
+		       pcibios_strerror(error));
+		return(0);
+	}
+
+	memset(&ret_struct, 0, sizeof(ret_struct));
+	ret_struct.device = device_id;
+
+	return &ret_struct;
+}
+#endif
+     
 __initfunc(int register_PCI(int i, char bus, char device_fn))
 {
 	int	num_aiops, aiop, max_num_aiops, num_chan, chan;
@@ -1875,8 +1969,23 @@
 	char *str;
 	CONTROLLER_t	*ctlp;
 	struct pci_dev *dev = pci_find_slot(bus, device_fn);
+#if (LINUX_VERSION_CODE < 0x020163) /* 2.1.99 */
+	int	ret;
+	unsigned int port;
+#endif
 
+	if (!dev)
+		return 0;
+
+#if (LINUX_VERSION_CODE >= 0x020163) /* 2.1.99 */
 	rcktpt_io_addr[i] = dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+#else
+	ret = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0,
+		&port);
+	if (ret)
+		return 0;
+	rcktpt_io_addr[i] = port & PCI_BASE_ADDRESS_IO_MASK;
+#endif	
 	switch(dev->device) {
 	case PCI_DEVICE_ID_RP4QUAD:
 		str = "Quadcable";
@@ -1902,6 +2011,18 @@
 		str = "32";
 		max_num_aiops = 4;
 		break;
+	case PCI_DEVICE_ID_RPP4:
+		str = "Plus Quadcable";
+		max_num_aiops = 1;
+		break;
+	case PCI_DEVICE_ID_RPP8:
+		str = "Plus Octacable";
+		max_num_aiops = 1;
+		break;
+	case PCI_DEVICE_ID_RP8M:
+		str = "8-port Modem";
+		max_num_aiops = 1;
+		break;
 	default:
 		str = "(unknown/unsupported)";
 		max_num_aiops = 0;
@@ -1936,20 +2057,48 @@
 	int	i, count = 0;
 
 	for(i=0; i < (NUM_BOARDS - boards_found); i++) {
-		if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8OCTA,
-			i, &bus, &device_fn)) 
+		if (!pcibios_find_device(PCI_VENDOR_ID_RP,
+			PCI_DEVICE_ID_RP4QUAD, i, &bus, &device_fn)) 
+			if (register_PCI(count+boards_found, bus, device_fn))
+				count++;
+		if (!pcibios_find_device(PCI_VENDOR_ID_RP,
+			PCI_DEVICE_ID_RP8J, i, &bus, &device_fn)) 
+			if (register_PCI(count+boards_found, bus, device_fn))
+				count++;
+		if(!pcibios_find_device(PCI_VENDOR_ID_RP,
+			PCI_DEVICE_ID_RP8OCTA, i, &bus, &device_fn)) 
+			if(register_PCI(count+boards_found, bus, device_fn))
+				count++;
+		if(!pcibios_find_device(PCI_VENDOR_ID_RP,
+			PCI_DEVICE_ID_RP8INTF, i, &bus, &device_fn)) 
+			if(register_PCI(count+boards_found, bus, device_fn))
+				count++;
+		if(!pcibios_find_device(PCI_VENDOR_ID_RP,
+			PCI_DEVICE_ID_RP16INTF, i, &bus, &device_fn)) 
+			if(register_PCI(count+boards_found, bus, device_fn))
+				count++;
+		if(!pcibios_find_device(PCI_VENDOR_ID_RP,
+			PCI_DEVICE_ID_RP32INTF, i, &bus, &device_fn)) 
+			if(register_PCI(count+boards_found, bus, device_fn))
+				count++;
+		if(!pcibios_find_device(PCI_VENDOR_ID_RP,
+			PCI_DEVICE_ID_RP4QUAD, i, &bus, &device_fn)) 
+			if(register_PCI(count+boards_found, bus, device_fn))
+				count++;
+		if(!pcibios_find_device(PCI_VENDOR_ID_RP,
+			PCI_DEVICE_ID_RP8J, i, &bus, &device_fn)) 
 			if(register_PCI(count+boards_found, bus, device_fn))
 				count++;
-		if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8INTF,
-			i, &bus, &device_fn)) 
+		if(!pcibios_find_device(PCI_VENDOR_ID_RP,
+			PCI_DEVICE_ID_RPP4, i, &bus, &device_fn)) 
 			if(register_PCI(count+boards_found, bus, device_fn))
 				count++;
-		if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP16INTF,
-			i, &bus, &device_fn)) 
+		if(!pcibios_find_device(PCI_VENDOR_ID_RP,
+			PCI_DEVICE_ID_RPP8, i, &bus, &device_fn)) 
 			if(register_PCI(count+boards_found, bus, device_fn))
 				count++;
-		if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP32INTF,
-			i, &bus, &device_fn)) 
+		if(!pcibios_find_device(PCI_VENDOR_ID_RP,
+			PCI_DEVICE_ID_RP8M, i, &bus, &device_fn)) 
 			if(register_PCI(count+boards_found, bus, device_fn))
 				count++;
 	}
@@ -2073,7 +2222,7 @@
 			isa_boards_found++;
 	}
 #ifdef ENABLE_PCI
-	if (pci_present()) {
+	if (pcibios_present()) {
 		if(isa_boards_found < NUM_BOARDS)
 			pci_boards_found = init_PCI(isa_boards_found);
 	} else {
diff -u --recursive --new-file v2.1.108/linux/drivers/char/rocket_int.h linux/drivers/char/rocket_int.h
--- v2.1.108/linux/drivers/char/rocket_int.h	Mon Jul 21 12:41:21 1997
+++ linux/drivers/char/rocket_int.h	Fri Jul 10 10:48:37 1998
@@ -483,6 +483,18 @@
 }
 
 /***************************************************************************
+Function: sDisIXANY
+Purpose:  Disable IXANY Software Flow Control
+Call:     sDisIXANY(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sDisIXANY(ChP) \
+{ \
+   (ChP)->R[0x0e] = 0x86; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x0c]); \
+}
+
+/***************************************************************************
 Function: DisParity
 Purpose:  Disable parity
 Call:     sDisParity(ChP)
@@ -573,6 +585,18 @@
 }
 
 /***************************************************************************
+Function: sEnIXANY
+Purpose:  Enable IXANY Software Flow Control
+Call:     sEnIXANY(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sEnIXANY(ChP) \
+{ \
+   (ChP)->R[0x0e] = 0x21; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x0c]); \
+}
+
+/***************************************************************************
 Function: EnParity
 Purpose:  Enable parity
 Call:     sEnParity(ChP)
@@ -647,6 +671,18 @@
 }
 
 /***************************************************************************
+Function: sEnTxSoftFlowCtl
+Purpose:  Enable Tx Software Flow Control
+Call:     sEnTxSoftFlowCtl(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sEnTxSoftFlowCtl(ChP) \
+{ \
+   (ChP)->R[0x06] = 0xc5; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x04]); \
+}
+
+/***************************************************************************
 Function: sGetAiopIntStatus
 Purpose:  Get the AIOP interrupt status
 Call:     sGetAiopIntStatus(CtlP,AiopNum)
@@ -985,6 +1021,32 @@
 }
 
 /***************************************************************************
+Function: sSetTxXOFFChar
+Purpose:  Set the Tx XOFF flow control character
+Call:     sSetTxXOFFChar(ChP,Ch)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Byte_t Ch; The value to set the Tx XOFF character to
+*/
+#define sSetTxXOFFChar(ChP,CH) \
+{ \
+   (ChP)->R[0x07] = (CH); \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x04]); \
+}
+
+/***************************************************************************
+Function: sSetTxXONChar
+Purpose:  Set the Tx XON flow control character
+Call:     sSetTxXONChar(ChP,Ch)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Byte_t Ch; The value to set the Tx XON character to
+*/
+#define sSetTxXONChar(ChP,CH) \
+{ \
+   (ChP)->R[0x0b] = (CH); \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x08]); \
+}
+
+/***************************************************************************
 Function: sStartRxProcessor
 Purpose:  Start a channel's receive processor
 Call:     sStartRxProcessor(ChP)
@@ -1133,17 +1195,25 @@
 #undef PCI_DEVICE_ID_RP32INTF
 #endif
 
-#define PCI_VENDOR_ID_RP               0x11fe
-#define PCI_DEVICE_ID_RP32INTF         0x0001
-#define PCI_DEVICE_ID_RP8INTF          0x0002
-#define PCI_DEVICE_ID_RP16INTF         0x0003
-#define PCI_DEVICE_ID_RP8OCTA          0x0005
+#define PCI_VENDOR_ID_RP		0x11fe
+#define PCI_DEVICE_ID_RP32INTF		0x0001
+#define PCI_DEVICE_ID_RP8INTF		0x0002
+#define PCI_DEVICE_ID_RP16INTF		0x0003
+#define PCI_DEVICE_ID_RP8OCTA		0x0005
 
-#ifndef RP4QUAD
-#define PCI_DEVICE_ID_RP4QUAD	       0x0004
+#ifndef PCI_DEVICE_ID_RP4QUAD
+#define PCI_DEVICE_ID_RP4QUAD		0x0004
 #endif
-#ifndef RP8J
-#define PCI_DEVICE_ID_RP8J	       0x0006
+#ifndef PCI_DEVICE_ID_RP8J
+#define PCI_DEVICE_ID_RP8J		0x0006
 #endif
-
+#ifndef PCI_DEVICE_ID_RPP4
+#define PCI_DEVICE_ID_RPP4		0x000A
+#endif
+#ifndef PCI_DEVICE_ID_RPP8
+#define PCI_DEVICE_ID_RPP8		0x000B
+#endif
+#ifndef PCI_DEVICE_ID_RP8M
+#define PCI_DEVICE_ID_RP8M		0x000C
+#endif	
 
diff -u --recursive --new-file v2.1.108/linux/drivers/char/selection.c linux/drivers/char/selection.c
--- v2.1.108/linux/drivers/char/selection.c	Wed Jun 24 22:54:05 1998
+++ linux/drivers/char/selection.c	Fri Jul 10 15:18:31 1998
@@ -92,12 +92,11 @@
 /* set inwordLut contents. Invoked by ioctl(). */
 int sel_loadlut(const unsigned long arg)
 {
-	int err;
+	int err = -EFAULT;
 
-	err = copy_from_user(inwordLut, (u32 *)(arg+4), 32);
-	if (err)
-		return -EFAULT;
-	return 0;
+	if (!copy_from_user(inwordLut, (u32 *)(arg+4), 32))
+		err = 0;
+	return err;
 }
 
 /* does screen address p correspond to character at LH/RH edge of screen? */
@@ -109,10 +108,7 @@
 /* constrain v such that v <= u */
 static inline unsigned short limit(const unsigned short v, const unsigned short u)
 {
-/* gcc miscompiles the ?: operator, so don't use it.. */
-	if (v > u)
-		return u;
-	return v;
+	return (v > u) ? u : v;
 }
 
 /* set the current selection. Invoked by ioctl() or by kernel code. */
@@ -258,16 +254,18 @@
 	sel_start = new_sel_start;
 	sel_end = new_sel_end;
 
-	if (sel_buffer)
-		kfree(sel_buffer);
-	sel_buffer = kmalloc((sel_end-sel_start)/2+1, GFP_KERNEL);
-	if (!sel_buffer) {
-		printk("selection: kmalloc() failed\n");
+	/* Allocate a new buffer before freeing the old one ... */
+	bp = kmalloc((sel_end-sel_start)/2+1, GFP_KERNEL);
+	if (!bp) {
+		printk(KERN_WARNING "selection: kmalloc() failed\n");
 		clear_selection();
 		return -ENOMEM;
 	}
+	if (sel_buffer)
+		kfree(sel_buffer);
+	sel_buffer = bp;
 
-	obp = bp = sel_buffer;
+	obp = bp;
 	for (i = sel_start; i <= sel_end; i += 2) {
 		*bp = sel_pos(i);
 		if (!isspace(*bp++))
@@ -286,31 +284,29 @@
 	return 0;
 }
 
-/* Insert the contents of the selection buffer into the queue of the
-   tty associated with the current console. Invoked by ioctl(). */
+/* 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 };
-	char	*bp = sel_buffer;
-	int	c = sel_buffer_lth;
-	int	l;
 	struct vt_struct *vt = (struct vt_struct *) tty->driver_data;
+	int	pasted = 0, count;
+	struct wait_queue wait = { current, NULL };
 
-	if (!bp || !c)
-		return 0;
 	poke_blanked_console();
 	add_wait_queue(&vt->paste_wait, &wait);
-	do {
+	while (sel_buffer && sel_buffer_lth > pasted) {
 		current->state = TASK_INTERRUPTIBLE;
 		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;
-	} while (c);
+		count = sel_buffer_lth - pasted;
+		count = MIN(count, tty->ldisc.receive_room(tty));
+		tty->ldisc.receive_buf(tty, sel_buffer + pasted, 0, count);
+		pasted += count;
+	}
 	remove_wait_queue(&vt->paste_wait, &wait);
 	current->state = TASK_RUNNING;
 	return 0;
diff -u --recursive --new-file v2.1.108/linux/drivers/char/vt.c linux/drivers/char/vt.c
--- v2.1.108/linux/drivers/char/vt.c	Wed Jun 24 22:54:05 1998
+++ linux/drivers/char/vt.c	Fri Jul 10 15:18:31 1998
@@ -22,6 +22,7 @@
 #include <linux/malloc.h>
 #include <linux/major.h>
 #include <linux/fs.h>
+#include <linux/config.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -56,39 +57,10 @@
 asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on);
 #endif
 
-extern int getkeycode(unsigned int scancode);
-extern int setkeycode(unsigned int scancode, unsigned int keycode);
-extern void compute_shiftstate(void);
-extern void complete_change_console(unsigned int new_console);
-extern int vt_waitactive(int vt);
-extern void do_blank_screen(int nopowersave);
-
-extern unsigned int keymap_count;
-
-/*
- * 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, int ch512);
-extern int con_get_font(char * fontmap);
-extern int con_set_cmap(unsigned char *cmap);
-extern int con_get_cmap(unsigned char *cmap);
-extern void reset_palette(int currcons);
-extern void set_palette(void) ;
-extern int con_adjust_height(unsigned long fontheight);
-
-extern int video_mode_512ch;
-extern unsigned long video_font_height;
-extern unsigned long default_font_height;
-extern unsigned long video_scan_lines;
+unsigned int video_mode_512ch;
+unsigned int video_font_height;
+unsigned int default_font_height;
+unsigned int video_scan_lines;
 
 /*
  * these are the valid i/o ports we're allowed to change. they map all the
@@ -152,6 +124,7 @@
  * We also return immediately, which is what was implied within the X
  * comments - KDMKTONE doesn't put the process to sleep.
  */
+/* FIXME: This should go to arch-dependent code */
 static void
 kd_nosound(unsigned long ignored)
 {
@@ -396,9 +369,8 @@
 static inline int 
 do_fontx_ioctl(int cmd, struct consolefontdesc *user_cfd, int perm)
 {
-	int nchar;
 	struct consolefontdesc cfdarg;
-	int i = 0;
+	int i;
 
 	if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc))) 
 		return -EFAULT;
@@ -409,35 +381,21 @@
 	case PIO_FONTX:
 		if (!perm)
 			return -EPERM;
-		if ( cfdarg.charcount == 256 ||
-		     cfdarg.charcount == 512 ) {
-			i = con_set_font(cfdarg.chardata,
-				cfdarg.charcount == 512);
-			if (i)
-				return i;
-			i = con_adjust_height(cfdarg.charheight);
-			/*
-			 *  ++Geert: vc_resize_con() will take note of the
-			 *	     changed screen size, if necessary
-			 */
-			return (i <= 0) ? i : 0;
-		} else
-			return -EINVAL;
-	case GIO_FONTX:
-		i = cfdarg.charcount;
-		cfdarg.charcount = nchar = video_mode_512ch ? 512 : 256;
-		cfdarg.charheight = video_font_height;
-		__copy_to_user(user_cfd, &cfdarg,
-			    sizeof(struct consolefontdesc)); 
-		if ( cfdarg.chardata )
-		{
-			if ( i < nchar )
-				return -ENOMEM;
-			return con_get_font(cfdarg.chardata);
-		} else
+		return con_set_font(cfdarg.chardata, 8, cfdarg.charheight, cfdarg.charcount);
+	case GIO_FONTX: {
+		int w, h;
+		int c= cfdarg.charcount;
+		if (!cfdarg.chardata)
 			return 0;
+		i = con_get_font(cfdarg.chardata, &w, &h, &c);
+		if (i)
+			return i;
+		if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc)))
+			return -EFAULT;
+		return 0;
+		}
 	}
-	return 0;
+	return -EINVAL;
 }
 
 static inline int 
@@ -802,7 +760,7 @@
 		if (arg == 0 || arg > MAX_NR_CONSOLES)
 			return -ENXIO;
 		arg--;
-		i = vc_allocate(arg);
+		i = vc_allocate(arg, 0);
 		if (i)
 			return i;
 		set_console(arg);
@@ -854,7 +812,7 @@
 				 */
 				int newvt = vt_cons[console]->vt_newvt;
 				vt_cons[console]->vt_newvt = -1;
-				i = vc_allocate(newvt);
+				i = vc_allocate(newvt, 0);
 				if (i)
 					return i;
 				/*
@@ -914,7 +872,7 @@
 			return i;
 		__get_user(ll, &vtsizes->v_rows);
 		__get_user(cc, &vtsizes->v_cols);
-		i = vc_resize(ll, cc);
+		i = vc_resize_all(ll, cc);
 		return i ? i : 	kd_size_changed(ll, cc);
 	}
 
@@ -963,7 +921,7 @@
 		if ( clin )
 		  video_font_height = clin;
 		
-		i = vc_resize(ll, cc);
+		i = vc_resize_all(ll, cc);
 		if (i)
 			return i;
 
@@ -974,27 +932,20 @@
 	case PIO_FONT:
 		if (!perm)
 			return -EPERM;
-		if (vt_cons[fg_console]->vc_mode != KD_TEXT)
-			return -EINVAL;
-		return con_set_font((char *)arg, 0);
-		/* con_set_font() defined in console.c */
+		return con_set_font((char *)arg, 8, 0, 256);
 
-	case GIO_FONT:
-		if (vt_cons[fg_console]->vc_mode != KD_TEXT ||
-		    video_mode_512ch)
-			return -EINVAL;
-		return con_get_font((char *)arg);
-		/* con_get_font() defined in console.c */
+	case GIO_FONT: {
+		int w, h, s=256;
+		return con_get_font((char *)arg, &w, &h, &s);
+	}
 
 	case PIO_CMAP:
                 if (!perm)
 			return -EPERM;
                 return con_set_cmap((char *)arg);
-                /* con_set_cmap() defined in console.c */
 
 	case GIO_CMAP:
                 return con_get_cmap((char *)arg);
-                /* con_get_cmap() defined in console.c */
 
 	case PIO_FONTX:
 	case GIO_FONTX:
@@ -1004,8 +955,6 @@
 	{
 		if (!perm)
 			return -EPERM;
-		if (vt_cons[fg_console]->vc_mode != KD_TEXT)
-			return -EINVAL;
 
 #ifdef BROKEN_GRAPHICS_PROGRAMS
 		/* With BROKEN_GRAPHICS_PROGRAMS defined, the default
@@ -1013,16 +962,9 @@
 		return -ENOSYS;
 #else
 
-		i = con_set_font(NULL, 0);	/* Set font to default */
+		i = con_set_font(NULL, 0, 0, 0);	/* Set font to default */
 		if (i) return i;
-
-		i = con_adjust_height(default_font_height);
-			/*
-			 *  ++Geert: vc_resize_con() will take note of the
-			 *	     changed screen size, if necessary
-			 */
 		con_set_default_unimap();
-
 		return 0;
 #endif
 	}
@@ -1187,21 +1129,16 @@
 	/* Set the colour palette for this VT */
 	if (vt_cons[new_console]->vc_mode == KD_TEXT)
 		set_palette() ;
-	
+
+	/* FIXME: Do we still need this? */
 #ifdef CONFIG_SUN_CONSOLE
 	if (old_vc_mode != vt_cons[new_console]->vc_mode)
 	{
 	 	if (old_vc_mode == KD_GRAPHICS)
-		{
-			suncons_ops.clear_margin();
-			suncons_ops.render_screen();
-			suncons_ops.set_cursor(fg_console);
-		}
-		else
-			suncons_ops.hide_cursor();
+			update_screen(new_console);
 	}
 #endif		
-      /*
+	/*
 	 * Wake anyone waiting for their VT to activate
 	 */
 	vt_wake_waitactive();
diff -u --recursive --new-file v2.1.108/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- v2.1.108/linux/drivers/misc/parport_pc.c	Tue Jun 23 10:01:23 1998
+++ linux/drivers/misc/parport_pc.c	Thu Jul 16 16:31:38 1998
@@ -783,11 +783,6 @@
 		count += probe_one_port(0x278, irq[0], dma[0]);
 	}
 
-	/* Give any attached devices a chance to gather their thoughts */
-	current->state = TASK_INTERRUPTIBLE;
-	current->timeout = jiffies + 75;
-	schedule ();
-
 	return count;
 }
 
diff -u --recursive --new-file v2.1.108/linux/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c
--- v2.1.108/linux/drivers/misc/parport_share.c	Tue Jun  9 11:57:29 1998
+++ linux/drivers/misc/parport_share.c	Mon Jul 13 12:28:52 1998
@@ -360,9 +360,10 @@
 			/* First add ourselves to the end of the wait list. */
 			dev->waitnext = NULL;
 			dev->waitprev = port->waittail;
-			if (port->waittail)
+			if (port->waittail) {
 				port->waittail->waitnext = dev;
-			else
+				port->waittail = dev;
+			} else
 				port->waithead = port->waittail = dev;
 		}
 		spin_unlock_irqrestore (&port->lock, flags);
diff -u --recursive --new-file v2.1.108/linux/drivers/net/8390.h linux/drivers/net/8390.h
--- v2.1.108/linux/drivers/net/8390.h	Tue Jun  9 11:57:29 1998
+++ linux/drivers/net/8390.h	Fri Jul 10 15:35:46 1998
@@ -13,7 +13,7 @@
 #include <linux/skbuff.h>
 
 /* With kmod, drivers can now load the 8390 module themselves! */
-#ifdef CONFIG_KMOD
+#if 0 /* def CONFIG_KMOD */
 #define LOAD_8390_BY_KMOD
 #endif
 
diff -u --recursive --new-file v2.1.108/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.1.108/linux/drivers/net/de4x5.c	Sun Jun  7 11:16:32 1998
+++ linux/drivers/net/de4x5.c	Tue Jul  7 20:22:25 1998
@@ -213,14 +213,17 @@
 
         insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'.
 
-    For a compiled in driver, somewhere in this file, place e.g.
+    For a compiled in driver, at or above line 548, place e.g.
 	#define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP"
 
-    Yes,  I know full duplex  isn't permissible on BNC  or AUI; they're just
-    examples. By default, full duplex is turned  off and AUTO is the default
-    autosense setting. In  reality, I expect only the  full duplex option to
+    Yes,  I know full duplex isn't  permissible on BNC  or AUI; they're just
+    examples. By default, full duplex is turned off and  AUTO is the default
+    autosense setting.  In reality, I expect only  the full duplex option to
     be used. Note the use of single quotes in the two examples above and the
-    lack of commas to separate items.
+    lack of commas to separate items. ALSO, you must get the requested media
+    correct in relation to what the adapter SROM says it has. There's no way
+    to  determine this in  advance other than by  trial and error and common
+    sense, e.g. call a BNC connectored port 'BNC', not '10Mb'.
 
     TO DO:
     ------
@@ -374,11 +377,33 @@
       0.535  21-Feb-98    Fix Ethernet Address PROM reset bug for DC21040.
       0.536  21-Mar-98    Change pci_probe() to use the pci_dev structure.
 			  **Incompatible with 2.0.x from here.**
+      0.540   5-Jul-98    Atomicize assertion of dev->interrupt for SMP
+                           from <lma@varesearch.com>
+			  Add TP, AUI and BNC cases to 21140m_autoconf() for
+			   case where a 21140 under SROM control uses, e.g. AUI
+			   from problem report by <delchini@lpnp09.in2p3.fr>
+			  Add MII parallel detection to 2114x_autoconf() for
+			   case where no autonegotiation partner exists from
+			   problem report by <mlapsley@ndirect.co.uk>.
+			  Add ability to force connection type directly even
+			   when using SROM control from problem report by
+			   <earl@exis.net>.
+			  Updated the PCI interface to conform with the latest
+			   version. I hope nothing is broken...
+          		  Add TX done interrupt modification from suggestion
+			   by <Austin.Donnelly@cl.cam.ac.uk>.
+			  Fix is_anc_capable() bug reported by 
+			   <Austin.Donnelly@cl.cam.ac.uk>.
+			  Fix type[13]_infoblock() bug: during MII search, PHY
+			   lp->rst not run because lp->ibn not initialised -
+			   from report & fix by <paubert@iram.es>.
+			  Fix probe bug with EISA & PCI cards present from
+                           report by <eirik@netcom.com>.
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:V0.536 1998/3/5 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.540 1998/7/5 davies@maniac.ultranet.com\n";
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -933,7 +958,7 @@
 static void    eisa_probe(struct device *dev, u_long iobase);
 #endif
 static void    pci_probe(struct device *dev, u_long iobase);
-static void    srom_search(int index);
+static void    srom_search(struct pci_dev *pdev);
 static char    *build_setup_frame(struct device *dev, int mode);
 static void    disable_ast(struct device *dev);
 static void    enable_ast(struct device *dev, u32 time_out);
@@ -980,12 +1005,12 @@
 static char name[DE4X5_NAME_LENGTH + 1];
 #if !defined(__sparc_v9__) && !defined(__powerpc__)
 static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST;
+static int lastEISA = 0;
+#else
+static int lastEISA = MAX_EISA_SLOTS;           /* Only PCI probes */
 #endif
 static int num_de4x5s = 0;
 static int cfrv = 0, useSROM = 0;
-#if !defined(__sparc_v9__) && !defined(__powerpc__)
-static int lastEISA = 0;
-#endif
 static int lastPCI = -1;
 static struct device *lastModule = NULL;
 
@@ -1036,9 +1061,9 @@
 
 #define PHY_HARD_RESET {\
     outl(GEP_HRST, DE4X5_GEP);           /* Hard RESET the PHY dev. */\
-    mdelay(1);                        /* Assert for 1ms */\
+    mdelay(1);                           /* Assert for 1ms */\
     outl(0x00, DE4X5_GEP);\
-    mdelay(2);                        /* Wait for 2ms */\
+    mdelay(2);                           /* Wait for 2ms */\
 }
 
 
@@ -1054,7 +1079,9 @@
 #if !defined(__sparc_v9__) && !defined(__powerpc__)
     eisa_probe(dev, iobase);
 #endif
-    pci_probe(dev, iobase);
+    if (lastEISA == MAX_EISA_SLOTS) {
+	pci_probe(dev, iobase);
+    }
     
     return (dev->priv ? 0 : -ENODEV);
 }
@@ -1151,21 +1178,15 @@
 	/*
 	** Choose correct autosensing in case someone messed up
 	*/
-	if ((lp->params.autosense & AUTO) || lp->useSROM) {
-	    lp->autosense = AUTO;
-	} else {
-	    if (lp->chipset != DC21140) {
-		if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) {
-		    lp->params.autosense = TP;
-		}
-		if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) {
-		    lp->params.autosense = BNC;
-		}
-		lp->autosense = lp->params.autosense & 0x001f;
-	    } else {
-		lp->autosense = lp->params.autosense & 0x00c0;
-	    }
-	}
+        lp->autosense = lp->params.autosense;
+        if (lp->chipset != DC21140) {
+            if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) {
+                lp->params.autosense = TP;
+            }
+            if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) {
+                lp->params.autosense = BNC;
+            }
+        }
 	lp->fdx = lp->params.fdx;
 	sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
 	
@@ -1308,8 +1329,9 @@
     
     lp->state = OPEN;
     de4x5_dbg_open(dev);
+    
     if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ, 
-	    lp->adapter_name, dev)) {
+		                                     lp->adapter_name, dev)) {
 	printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq);
 	if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ,
 			                             lp->adapter_name, dev)) {
@@ -1448,7 +1470,7 @@
 }
 
 /* 
-** Writes a socket buffer address to the next available transmit descriptor
+** Writes a socket buffer address to the next available transmit descriptor.
 */
 static int
 de4x5_queue_pkt(struct sk_buff *skb, struct device *dev)
@@ -1542,12 +1564,11 @@
     lp = (struct de4x5_private *)dev->priv;
     iobase = dev->base_addr;
 	
-    if (dev->interrupt)
-      printk("%s: Re-entering the interrupt handler.\n", dev->name);
+    if (test_and_set_bit(MASK_INTERRUPTS, (void*) &dev->interrupt))
+	printk("%s: Re-entering the interrupt handler.\n", dev->name);
 	
     DISABLE_IRQs;                        /* Ensure non re-entrancy */
     synchronize_irq();
-    dev->interrupt = MASK_INTERRUPTS;
 	
     for (limit=0; limit<8; limit++) {
 	sts = inl(DE4X5_STS);            /* Read IRQ status */
@@ -1868,16 +1889,27 @@
     return;
 }
 
+/*
+** Removes the TD_IC flag from previous descriptor to improve TX performance.
+** If the flag is changed on a descriptor that is being read by the hardware,
+** I assume PCI transaction ordering will mean you are either successful or
+** just miss asserting the change to the hardware. Anyway you're messing with
+** a descriptor you don't own, but this shouldn't kill the chip provided
+** the descriptor register is read only to the hardware.
+*/
 static void
 load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff *skb)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    
+    int entry = (lp->tx_new ? lp->tx_new-1 : lp->txRingSize-1);
+
     lp->tx_ring[lp->tx_new].buf = cpu_to_le32(virt_to_bus(buf));
     lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER);
     lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags);
     lp->tx_skb[lp->tx_new] = skb;
+    lp->tx_ring[entry].des1 &= cpu_to_le32(~TD_IC);
     barrier();
+
     lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN);
     barrier();
     
@@ -2044,7 +2076,7 @@
 
     return;
 }
-#endif                         /* !(__sparc_v9__) */
+#endif                         /* !(__sparc_v9__) && !(__powerpc__) */
 
 /*
 ** PCI bus I/O device probe
@@ -2057,22 +2089,25 @@
 ** bit. Here, check for I/O accesses and then set BM. If you put the card in
 ** a non BM slot, you're on your own (and complain to the PC vendor that your
 ** PC doesn't conform to the PCI standard)!
+**
+** This function is only compatible with the *latest* 2.1.x kernels. For 2.0.x
+** kernels use the V0.535[n] drivers.
 */
-#define PCI_DEVICE    (dev_num << 3)
 #define PCI_LAST_DEV  32
 
 __initfunc(static void
 pci_probe(struct device *dev, u_long ioaddr))
 {
-    u_char pb, pbus, dev_num, dnum, dev_fn, timer;
-    u_short dev_id, vendor, index, status;
+    u_char pb, pbus, dev_num, dnum, timer;
+    u_short vendor, index, status;
     u_int irq = 0, device, class = DE4X5_CLASS_CODE;
     u_long iobase = 0;                     /* Clear upper 32 bits in Alphas */
     struct bus_type *lp = &bus;
+    struct pci_dev *pdev = NULL;
 
     if (lastPCI == NO_MORE_PCI) return;
 
-    if (!pci_present()) {
+    if (!pcibios_present()) {
 	lastPCI = NO_MORE_PCI;
 	return;          /* No PCI bus in this machine! */
     }
@@ -2088,96 +2123,77 @@
 	dnum = 0;
     }
 
-    for (index=lastPCI+1; 
-	 (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
-	 index++) {
-	dev_num = PCI_SLOT(dev_fn);
-	if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) {
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,85)
-	    struct pci_dev *pdev = pci_find_slot(pb, dev_fn);
-#else
-	    u_char tirq;
-	    u_int tmp;
-#endif
-	    device = 0;
-	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
-	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
-	    device = dev_id;
-	    device <<= 8;
-	    if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) {
-		continue;
-	    }
-
-	    /* Search for an SROM on this bus */
-	    if (lp->bus_num != pb) {
-		lp->bus_num = pb;
-		srom_search(index);
-	    }
-
-	    /* Get the chip configuration revision register */
-	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
+    for (index=lastPCI+1; (pdev=pci_find_class(class, pdev))!=NULL; index++) {
+	dev_num = PCI_SLOT(pdev->devfn);
+	pb = pdev->bus->number;
+	if ((pbus || dnum) && ((pbus != pb) || (dnum != dev_num))) continue;
+
+	vendor = pdev->vendor;
+	device = pdev->device << 8;
+	if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue;
 
-	    /* Set the device number information */
-	    lp->device = dev_num;
+	/* Search for an SROM on this bus */
+	if (lp->bus_num != pb) {
 	    lp->bus_num = pb;
-	    
-	    /* Set the chipset information */
-	    if (is_DC2114x) device |= (cfrv & CFRV_RN);
-	    lp->chipset = device;
+	    srom_search(pdev);
+	}
 
-	    /* Get the board I/O address and IRQ */
-#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,85)
-	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp);
-	    iobase = tmp;
-	    pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &tirq);
-	    irq = tirq;
-#else
-	    iobase = pdev->base_address[0];
-	    irq = pdev->irq;
-#endif
-	    iobase &= CBIO_MASK;
+	/* Get the chip configuration revision register */
+	pcibios_read_config_dword(pb, pdev->devfn, PCI_REVISION_ID, &cfrv);
 
-	    if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue;
+	/* Set the device number information */
+	lp->device = dev_num;
+	lp->bus_num = pb;
+	    
+	/* Set the chipset information */
+	if (is_DC2114x) device |= (cfrv & CFRV_RN);
+	lp->chipset = device;
+
+	/* Get the board I/O address (64 bits on sparc64) */
+	iobase = pdev->base_address[0] & CBIO_MASK;
 
-	    /* Check if I/O accesses and Bus Mastering are enabled */
-	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+	/* Fetch the IRQ to be used */
+	irq = pdev->irq;
+	if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue;
+	    
+	/* Check if I/O accesses and Bus Mastering are enabled */
+	pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status);
 #ifdef __powerpc__
-	    if (!(status & PCI_COMMAND_IO)) {
-		status |= PCI_COMMAND_IO;
-		pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
-		pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
-	    }
+	if (!(status & PCI_COMMAND_IO)) {
+	    status |= PCI_COMMAND_IO;
+	    pcibios_write_config_word(pb, pdev->devfn, PCI_COMMAND, status);
+	    pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status);
+	}
 #endif /* __powerpc__ */
-	    if (!(status & PCI_COMMAND_IO)) continue;
-
-	    if (!(status & PCI_COMMAND_MASTER)) {
-		status |= PCI_COMMAND_MASTER;
-		pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
-		pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
-	    }
-	    if (!(status & PCI_COMMAND_MASTER)) continue;
+	if (!(status & PCI_COMMAND_IO)) continue;
 
-	    /* Check the latency timer for values >= 0x60 */
-	    pcibios_read_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, &timer);
-	    if (timer < 0x60) {
-		pcibios_write_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, 0x60);
-	    }
+	if (!(status & PCI_COMMAND_MASTER)) {
+	    status |= PCI_COMMAND_MASTER;
+	    pcibios_write_config_word(pb, pdev->devfn, PCI_COMMAND, status);
+	    pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status);
+	}
+	if (!(status & PCI_COMMAND_MASTER)) continue;
+
+	/* Check the latency timer for values >= 0x60 */
+	pcibios_read_config_byte(pb, pdev->devfn, PCI_LATENCY_TIMER, &timer);
+	if (timer < 0x60) {
+	    pcibios_write_config_byte(pb, pdev->devfn, PCI_LATENCY_TIMER, 0x60);
+	}
 
-	    DevicePresent(DE4X5_APROM);
-	    if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) {
-		dev->irq = irq;
-		if ((status = de4x5_hw_init(dev, iobase)) == 0) {
-		    num_de4x5s++;
-		    if (loading_module) {
-			link_modules(lastModule, dev);
-			lastPCI = index;
-		    }
-		    return;
+	DevicePresent(DE4X5_APROM);
+	if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) {
+	    dev->irq = irq;
+	    if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+		num_de4x5s++;
+		if (loading_module) {
+		    link_modules(lastModule, dev);
+		    lastPCI = index;
 		}
-	    } else if (ioaddr != 0) {
-		printk("%s: region already allocated at 0x%04lx.\n", dev->name,
-		       iobase);
+		return;
 	    }
+	} else if (ioaddr != 0) {
+	    printk("%s: region already allocated at 0x%04lx.\n", dev->name,
+		   iobase);
 	}
     }
 
@@ -2193,44 +2209,27 @@
 ** For single port cards this is a time waster...
 */
 __initfunc(static void
-srom_search(int index))
+srom_search(struct pci_dev *pdev))
 {
-    u_char pb, dev_fn;
-    u_short dev_id, dev_num, vendor, status;
+    u_char pb;
+    u_short vendor, status;
     u_int irq = 0, device, class = DE4X5_CLASS_CODE;
     u_long iobase = 0;                     /* Clear upper 32 bits in Alphas */
     int i, j;
     struct bus_type *lp = &bus;
-#ifndef __sparc_v9__
-    u_char tirq;
-    u_int tmp;
-#endif
 
-    for (; 
-	 (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
-	 index++) {
-#ifdef __sparc_v9__
-	struct pci_dev *pdev;
-	for (pdev = pci_devices; pdev; pdev = pdev->next) {
-	    if ((pdev->bus->number == pb) && (pdev->devfn == dev_fn)) break;
-	}
-#endif
-	if (lp->bus_num != pb) return;
-	dev_num = PCI_SLOT(dev_fn);
-	device = 0;
-	pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
-	pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
-	device = dev_id;
-	device <<= 8;
-	if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) {
-	    continue;
-	}
+    while ((pdev = pci_find_class(class, pdev))!= NULL) {
+	if (lp->bus_num != pdev->bus->number) return;
+	pb = pdev->bus->number;
+	vendor = pdev->vendor;
+	device = pdev->device << 8;
+	if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue;
 
 	/* Get the chip configuration revision register */
-	pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
+	pcibios_read_config_dword(pb, pdev->devfn, PCI_REVISION_ID, &cfrv);
 
 	/* Set the device number information */
-	lp->device = dev_num;
+	lp->device = PCI_SLOT(pdev->devfn);
 	lp->bus_num = pb;
 	    
 	/* Set the chipset information */
@@ -2238,25 +2237,14 @@
 	lp->chipset = device;
 
 	/* Get the board I/O address (64 bits on sparc64) */
-#ifndef __sparc_v9__
-	pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp);
-	iobase = tmp;
-#else
-	iobase = pdev->base_address[0];
-#endif
-	iobase &= CBIO_MASK;
+	iobase = pdev->base_address[0] & CBIO_MASK;
 
 	/* Fetch the IRQ to be used */
-#ifndef __sparc_v9__
-	pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &tirq);
-	irq = tirq;
-#else
 	irq = pdev->irq;
-#endif
 	if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue;
 	    
 	/* Check if I/O accesses are enabled */
-	pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+	pcibios_read_config_word(pb, pdev->devfn, PCI_COMMAND, &status);
 	if (!(status & PCI_COMMAND_IO)) continue;
 
 	/* Search for a valid SROM attached to this DECchip */
@@ -2709,9 +2697,9 @@
     int ana, anlpa, cap, cr, slnk, sr;
     int next_tick = DE4X5_AUTOSENSE_MS;
     u_long imr, omr, iobase = dev->base_addr;
-
+    
     switch(lp->media) {
-      case INIT: 
+    case INIT: 
         if (lp->timeout < 0) {
 	    DISABLE_IRQs;
 	    lp->tx_enable = FALSE;
@@ -2757,9 +2745,9 @@
 	}
 	break;
 	
-      case ANS:
+    case ANS:
 	switch (lp->local_state) {
-	  case 0:
+	case 0:
 	    if (lp->timeout < 0) {
 		mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
 	    }
@@ -2777,7 +2765,7 @@
 	    }
 	    break;
 	    
-	  case 1:
+	case 1:
 	    if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) {
 		next_tick = sr & ~TIMER_CB;
 	    } else {
@@ -2805,7 +2793,7 @@
 	}
 	break;
 	
-      case SPD_DET:                              /* Choose 10Mb/s or 100Mb/s */
+    case SPD_DET:                              /* Choose 10Mb/s or 100Mb/s */
         if (lp->timeout < 0) {
 	    lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS : 
 		                                  (~gep_rd(dev) & GEP_LNP));
@@ -2825,7 +2813,7 @@
 	}
 	break;
 	
-      case _100Mb:                               /* Set 100Mb/s */
+    case _100Mb:                               /* Set 100Mb/s */
         next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_100Mb;
@@ -2840,8 +2828,10 @@
 	    }
 	}
 	break;
-	
-      case _10Mb:                                /* Set 10Mb/s */
+
+    case BNC:
+    case AUI:
+    case _10Mb:                                /* Set 10Mb/s */
         next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_10Mb;
@@ -2857,7 +2847,7 @@
 	}
 	break;
 	
-      case NC:
+    case NC:
         if (lp->media != lp->c_media) {
 	    de4x5_dbg_media(dev);
 	    lp->c_media = lp->media;
@@ -2893,33 +2883,54 @@
     int next_tick = DE4X5_AUTOSENSE_MS;
 
     switch (lp->media) {
-      case INIT:
+    case INIT:
         if (lp->timeout < 0) {
 	    DISABLE_IRQs;
 	    lp->tx_enable = FALSE;
 	    lp->linkOK = 0;
             lp->timeout = -1;
-	    de4x5_save_skbs(dev);          /* Save non transmitted skb's */
+	    de4x5_save_skbs(dev);            /* Save non transmitted skb's */
+	    if (lp->params.autosense & ~AUTO) {
+		srom_map_media(dev);         /* Fixed media requested      */
+		if (lp->media != lp->params.autosense) {
+		    lp->tcount++;
+		    lp->media = INIT;
+		    return next_tick;
+		}
+		lp->media = INIT;
+	    }
 	}
 	if ((next_tick = de4x5_reset_phy(dev)) < 0) {
 	    next_tick &= ~TIMER_CB;
 	} else {
-            lp->media = SPD_DET;
-	    if ((lp->infoblock_media == ANS) && 
+	    if (lp->autosense == _100Mb) {
+		lp->media = _100Mb;
+	    } else if (lp->autosense == _10Mb) {
+		lp->media = _10Mb;
+	    } else if (lp->autosense == TP) {
+		lp->media = TP;
+	    } else if (lp->autosense == BNC) {
+		lp->media = BNC;
+	    } else if (lp->autosense == AUI) {
+		lp->media = AUI;
+	    } else {
+		lp->media = SPD_DET;
+		if ((lp->infoblock_media == ANS) && 
 		                    ((sr=is_anc_capable(dev)) & MII_SR_ANC)) {
 		    ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA);
 		    ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM);
 		    mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
 		    lp->media = ANS;
+		}
 	    }
 	    lp->local_state = 0;
 	    next_tick = dc2114x_autoconf(dev);
         }
 	break;
 	
-      case ANS:
+    case ANS:
 	switch (lp->local_state) {
-	  case 0:
+	case 0:
 	    if (lp->timeout < 0) {
 		mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
 	    }
@@ -2937,7 +2948,7 @@
 	    }
 	    break;
 	    
-	  case 1:
+	case 1:
 	    if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) {
 		next_tick = sr & ~TIMER_CB;
 	    } else {
@@ -2959,15 +2970,15 @@
 		    }
 		}                       /* Auto Negotiation failed to finish */
 		next_tick = dc2114x_autoconf(dev);
-	    }                           /* Auto Negotiation failed to start */
+	    }                           /* Auto Negotiation failed to start  */
 	    break;
 	}
 	break;
-	
-      case AUI:
+
+    case AUI:
 	if (!lp->tx_enable) {
 	    if (lp->timeout < 0) {
-		omr = inl(DE4X5_OMR);          /* Set up half duplex for AUI */
+		omr = inl(DE4X5_OMR);   /* Set up half duplex for AUI        */
 		outl(omr & ~OMR_FDX, DE4X5_OMR);
 	    }
 	    irqs = 0;
@@ -2990,13 +3001,13 @@
 	}
 	break;
 	
-      case AUI_SUSPECT:
+    case AUI_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc2114x_autoconf);
 	break;
 	
-      case BNC:
+    case BNC:
 	switch (lp->local_state) {
-	  case 0:
+	case 0:
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for BNC */
 		outl(omr & ~OMR_FDX, DE4X5_OMR);
@@ -3012,7 +3023,7 @@
 	    }
 	    break;
 	    
-	  case 1:
+	case 1:
 	    if (!lp->tx_enable) {
 		if ((sts = ping_media(dev, 3000)) < 0) {
 		    next_tick = sts & ~TIMER_CB;
@@ -3033,11 +3044,11 @@
 	}
 	break;
 	
-      case BNC_SUSPECT:
+    case BNC_SUSPECT:
 	next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc2114x_autoconf);
 	break;
 	
-      case SPD_DET:                              /* Choose 10Mb/s or 100Mb/s */
+    case SPD_DET:                              /* Choose 10Mb/s or 100Mb/s */
 	  if (srom_map_media(dev) < 0) {
 	      lp->tcount++;
 	      lp->media = INIT;
@@ -3053,9 +3064,17 @@
 		  lp->media = SPD_DET;
 		  return PDET_LINK_WAIT;
 	      }
-	  } 
-	  if (((lp->media == _100Mb) && is_100_up(dev)) ||
+	  }
+	  if (lp->media == ANS) {           /* Do MII parallel detection */
+	      if (is_spd_100(dev)) {
+		  lp->media = _100Mb;
+	      } else {
+		  lp->media = _10Mb;
+	      }
+	      next_tick = dc2114x_autoconf(dev);
+	  } else if (((lp->media == _100Mb) && is_100_up(dev)) ||
 	      ((lp->media == _10Mb)  && is_10_up(dev)) ||
+	       (lp->media == TP) ||
 	       (lp->media == BNC) || (lp->media == AUI)) {
 	      next_tick = dc2114x_autoconf(dev);
 	  } else {
@@ -3064,7 +3083,7 @@
 	  }
 	  break;
 	
-      case _10Mb:
+    case _10Mb:
         next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_10Mb;
@@ -3080,7 +3099,7 @@
 	}
 	break;
 
-      case _100Mb:
+    case _100Mb:
         next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_100Mb;
@@ -3096,7 +3115,7 @@
 	}
 	break;
 
-      default:
+    default:
 	lp->tcount++;
 printk("Huh?: media:%02x\n", lp->media);
 	lp->media = INIT;
@@ -3466,7 +3485,7 @@
     if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII));
     } else if ((lp->chipset & ~0x00ff) == DC2114x) {
-	return (inl(DE4X5_SISR) & SISR_LPN) >> 11;
+	return (inl(DE4X5_SISR) & SISR_LPN) >> 12;
     } else {
 	return 0;
     }
@@ -4415,7 +4434,7 @@
     while (count--) {
 	gep_wr(((lp->chipset==DC21140) && (lp->ibn!=5) ? 
 		                                   *p++ : TWIDDLE(w++)), dev);
-	mdelay(2);                       /* 2ms per action */
+	mdelay(2);                          /* 2ms per action */
     }
 
     if (lp->chipset != DC21140) {
@@ -4645,6 +4664,7 @@
 
     p += 2;
     if (lp->state == INITIALISED) {
+        lp->ibn = 1;
 	lp->active = *p++;
 	lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
 	lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
@@ -4724,6 +4744,7 @@
 
     p += 2;
     if (lp->state == INITIALISED) {
+        lp->ibn = 3;
         lp->active = *p++;
 	lp->phy[lp->active].gep = (*p ? p : 0); p += (2 * (*p) + 1);
 	lp->phy[lp->active].rst = (*p ? p : 0); p += (2 * (*p) + 1);
@@ -5471,24 +5492,24 @@
     } tmp;
     
     switch(ioc->cmd) {
-      case DE4X5_GET_HWADDR:           /* Get the hardware address */
+    case DE4X5_GET_HWADDR:           /* Get the hardware address */
 	ioc->len = ETH_ALEN;
 	status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len);
 	if (status)
-	  break;
+	    break;
 	for (i=0; i<ETH_ALEN; i++) {
 	    tmp.addr[i] = dev->dev_addr[i];
 	}
 	copy_to_user(ioc->data, tmp.addr, ioc->len);
 	
 	break;
-      case DE4X5_SET_HWADDR:           /* Set the hardware address */
+    case DE4X5_SET_HWADDR:           /* Set the hardware address */
 	status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN);
 	if (status)
-	  break;
+	    break;
 	status = -EPERM;
 	if (!capable(CAP_NET_ADMIN))
-	  break;
+	    break;
 	status = 0;
 	copy_from_user(tmp.addr, ioc->data, ETH_ALEN);
 	for (i=0; i<ETH_ALEN; i++) {
@@ -5498,13 +5519,13 @@
 	/* Set up the descriptor and give ownership to the card */
 	while (test_and_set_bit(0, (void *)&dev->tbusy) != 0);
 	load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | 
-		                                        SETUP_FRAME_LEN, NULL);
+		                                       SETUP_FRAME_LEN, NULL);
 	lp->tx_new = (++lp->tx_new) % lp->txRingSize;
 	outl(POLL_DEMAND, DE4X5_TPD);                /* Start the TX */
 	dev->tbusy = 0;                              /* Unlock the TX ring */
 	
 	break;
-      case DE4X5_SET_PROM:             /* Set Promiscuous Mode */
+    case DE4X5_SET_PROM:             /* Set Promiscuous Mode */
 	if (capable(CAP_NET_ADMIN)) {
 	    omr = inl(DE4X5_OMR);
 	    omr |= OMR_PR;
@@ -5515,7 +5536,7 @@
 	}
 	
 	break;
-      case DE4X5_CLR_PROM:             /* Clear Promiscuous Mode */
+    case DE4X5_CLR_PROM:             /* Clear Promiscuous Mode */
 	if (capable(CAP_NET_ADMIN)) {
 	    omr = inl(DE4X5_OMR);
 	    omr &= ~OMR_PR;
@@ -5526,11 +5547,11 @@
 	}
 	
 	break;
-      case DE4X5_SAY_BOO:              /* Say "Boo!" to the kernel log file */
+    case DE4X5_SAY_BOO:              /* Say "Boo!" to the kernel log file */
 	printk("%s: Boo!\n", dev->name);
 	
 	break;
-      case DE4X5_MCA_EN:               /* Enable pass all multicast addressing */
+    case DE4X5_MCA_EN:               /* Enable pass all multicast addressing */
 	if (capable(CAP_NET_ADMIN)) {
 	    omr = inl(DE4X5_OMR);
 	    omr |= OMR_PM;
@@ -5540,18 +5561,18 @@
 	}
 	
 	break;
-      case DE4X5_GET_STATS:            /* Get the driver statistics */
+    case DE4X5_GET_STATS:            /* Get the driver statistics */
 	ioc->len = sizeof(lp->pktStats);
 	status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len);
 	if (status)
-	  break;
+	    break;
 	
 	cli();
 	copy_to_user(ioc->data, &lp->pktStats, ioc->len); 
 	sti();
 	
 	break;
-      case DE4X5_CLR_STATS:            /* Zero out the driver statistics */
+    case DE4X5_CLR_STATS:            /* Zero out the driver statistics */
 	if (capable(CAP_NET_ADMIN)) {
 	    cli();
 	    memset(&lp->pktStats, 0, sizeof(lp->pktStats));
@@ -5561,14 +5582,14 @@
 	}
 	
 	break;
-      case DE4X5_GET_OMR:              /* Get the OMR Register contents */
+    case DE4X5_GET_OMR:              /* Get the OMR Register contents */
 	tmp.addr[0] = inl(DE4X5_OMR);
 	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, 1))) {
 	    copy_to_user(ioc->data, tmp.addr, 1);
 	}
 	
 	break;
-      case DE4X5_SET_OMR:              /* Set the OMR Register contents */
+    case DE4X5_SET_OMR:              /* Set the OMR Register contents */
 	if (capable(CAP_NET_ADMIN)) {
 	    if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) {
 		copy_from_user(tmp.addr, ioc->data, 1);
@@ -5579,7 +5600,7 @@
 	}
 	
 	break;
-      case DE4X5_GET_REG:              /* Get the DE4X5 Registers */
+    case DE4X5_GET_REG:              /* Get the DE4X5 Registers */
 	j = 0;
 	tmp.lval[0] = inl(DE4X5_STS); j+=4;
 	tmp.lval[1] = inl(DE4X5_BMR); j+=4;
@@ -5687,7 +5708,7 @@
 	
 	break;
 */
-      default:
+    default:
 	status = -EOPNOTSUPP;
     }
     
@@ -5766,30 +5787,32 @@
 static int
 count_adapters(void)
 {
-    int i, j;
+    int i, j=0;
     char name[DE4X5_STRLEN];
-    u_char pb, dev_fn, dev_num;
-    u_short dev_id, vendor;
+    u_char pb, dev_fn;
+    u_short vendor;
     u_int class = DE4X5_CLASS_CODE;
     u_int device;
+    struct pci_dev *pdev;
+
 #if !defined(__sparc_v9__) && !defined(__powerpc__)
     u_long iobase = 0x1000;
 
-    for (j=0, i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) {
+    for (i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) {
 	if (EISA_signature(name, EISA_ID)) j++;
     }
 #endif
-    if (!pci_present()) return j;
+    if (!pcibios_present()) return j;
 
     for (i=0; 
 	 (pcibios_find_class(class, i, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
 	 i++) {
-	dev_num = PCI_SLOT(dev_fn);
-	device = 0;
-	pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
-	pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
-	device = dev_id;
-	device <<= 8;
+	for (pdev = pci_devices; pdev; pdev = pdev->next) {
+	    if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break;
+	}
+
+	vendor = pdev->vendor;
+	device = pdev->device << 8;
 	if (is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x) j++;
     }
 
diff -u --recursive --new-file v2.1.108/linux/drivers/net/de4x5.h linux/drivers/net/de4x5.h
--- v2.1.108/linux/drivers/net/de4x5.h	Sun Dec 21 22:37:32 1997
+++ linux/drivers/net/de4x5.h	Tue Jul  7 20:22:25 1998
@@ -811,7 +811,7 @@
 ** Media / mode state machine definitions
 ** User selectable:
 */
-#define TP              0x0001     /* 10Base-T                             */
+#define TP              0x0040     /* 10Base-T (now equiv to _10Mb)        */
 #define TP_NW           0x0002     /* 10Base-T with Nway                   */
 #define BNC             0x0004     /* Thinwire                             */
 #define AUI             0x0008     /* Thickwire                            */
diff -u --recursive --new-file v2.1.108/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c
--- v2.1.108/linux/drivers/net/eepro100.c	Sun Jun  7 11:16:32 1998
+++ linux/drivers/net/eepro100.c	Thu Jul 16 16:16:59 1998
@@ -33,7 +33,23 @@
 
 /* Set the copy breakpoint for the copy-only-tiny-buffer Rx method.
    Lower values use more memory, but are faster. */
-static int rx_copybreak = 200;
+/*
+ * NOTE! The value of 2000 means that this optimization never gets
+ * used. Rationale: it seems to be broken when in low-memory situations,
+ * apparently when alloc_skb() can return NULL the clever list of
+ * copy-buffers can get buggered. 
+ *
+ * My personal suspicion is that the allocation failure will cause
+ * us to not remove the skb from the list of available buffers, but
+ * we'd already have done a "skb_push()" with the data we got, so
+ * the buffer stays on the list but the available memory in it
+ * shrinks until we panic.
+ *
+ * Donald, when you fix this you can shrink this value again.
+ *
+ *		Linus
+ */
+static int rx_copybreak = 2000;
 
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 200;
diff -u --recursive --new-file v2.1.108/linux/drivers/net/hamradio/baycom_epp.c linux/drivers/net/hamradio/baycom_epp.c
--- v2.1.108/linux/drivers/net/hamradio/baycom_epp.c	Wed Jul  1 19:38:54 1998
+++ linux/drivers/net/hamradio/baycom_epp.c	Fri Jul 10 14:01:13 1998
@@ -859,7 +859,7 @@
 #ifdef __i386__
 #define GETTICK(x)                                                \
 ({                                                                \
-	if (current_cpu_data.x86_capability & 0x10)               \
+	if (current_cpu_data.x86_capability & X86_FEATURE_TSC)    \
 		__asm__ __volatile__("rdtsc" : "=a" (x) : : "dx");\
 })
 #else /* __i386__ */
diff -u --recursive --new-file v2.1.108/linux/drivers/net/hamradio/soundmodem/sm.h linux/drivers/net/hamradio/soundmodem/sm.h
--- v2.1.108/linux/drivers/net/hamradio/soundmodem/sm.h	Tue Jun  9 11:57:29 1998
+++ linux/drivers/net/hamradio/soundmodem/sm.h	Fri Jul 10 14:01:13 1998
@@ -32,6 +32,7 @@
 
 #include <linux/hdlcdrv.h>
 #include <linux/soundmodem.h>
+#include <asm/processor.h>
 #include <linux/bitops.h>
 
 #define SM_DEBUG
@@ -297,7 +298,7 @@
 
 #include <asm/processor.h>
 
-#define HAS_RDTSC (current_cpu_data.x86_capability & 0x10)
+#define HAS_RDTSC (current_cpu_data.x86_capability & X86_FEATURE_TSC)
 
 /*
  * only do 32bit cycle counter arithmetic; we hope we won't overflow.
diff -u --recursive --new-file v2.1.108/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c
--- v2.1.108/linux/drivers/net/ibmtr.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/ibmtr.c	Fri Jul 10 13:51:41 1998
@@ -62,6 +62,8 @@
  *	Changes by Paul Norton (pnorton@cts.com) :
  *      + moved the header manipulation code in tr_tx and tr_rx to
  *        net/802/tr.c. (July 12 1997)
+ *      + lifted 2000 byte mtu limit. now depends on shared-RAM size.
+ *        May 25 1998)
  */
 
 #ifdef PCMCIA
@@ -91,7 +93,7 @@
 /* version and credits */
 static char *version =
 "ibmtr.c: v1.3.57  8/ 7/94 Peter De Schrijver and Mark Swanson\n"
-"         v2.1.42  7/12/97 Paul Norton <pnorton@cts.com>\n";
+"         v2.1.106 6/22/98 Paul Norton <p.norton@computer.org>\n";
 
 static char pcchannelid[] = {
 	0x05, 0x00, 0x04, 0x09,
@@ -138,6 +140,8 @@
 
 #define DPRINTK(format, args...) printk("%s: " format, dev->name , ## args)
 #define DPRINTD(format, args...) DummyCall("%s: " format, dev->name , ## args)
+#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
+#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
 
 #if TR_NEWFORMAT
 /* this allows displaying full adapter information */
@@ -185,6 +189,7 @@
 static struct net_device_stats * tok_get_stats(struct device *dev);
 void		ibmtr_readlog(struct device *dev);
 void		ibmtr_reset_timer(struct timer_list *tmr, struct device *dev);
+int             ibmtr_change_mtu(struct device *dev, int mtu);
 
 static unsigned int ibmtr_portlist[] __initdata = {
 	0xa20, 0xa24, 0
@@ -469,10 +474,36 @@
 	ti->shared_ram_paging = readb(ti->mmio + AIPSHRAMPAGE);
 
         /* Available DHB  4Mb size:   F=2048, E=4096, D=4464 */
-	ti->dhb_size4mb = readb(ti->mmio + AIP4MBDHB);
+	switch (readb(ti->mmio + AIP4MBDHB)) {
+	case 0xe : 
+		ti->dhb_size4mb = 4096;
+		break; 
+	case 0xd : 
+		ti->dhb_size4mb = 4464;
+		break; 
+	default  : 
+		ti->dhb_size4mb = 2048;
+		break; 
+	}
 
 	/* Available DHB 16Mb size:  F=2048, E=4096, D=8192, C=16384, B=17960 */
-	ti->dhb_size16mb = readb(ti->mmio + AIP16MBDHB);
+	switch (readb(ti->mmio + AIP16MBDHB)) {
+	case 0xe : 
+		ti->dhb_size16mb = 4096;
+		break; 
+	case 0xd : 
+		ti->dhb_size16mb = 8192;
+		break; 
+	case 0xc : 
+		ti->dhb_size16mb = 16384;
+		break; 
+	case 0xb : 
+		ti->dhb_size16mb = 17960;
+		break; 
+	default  : 
+		ti->dhb_size16mb = 2048;
+		break; 
+	}
 
 #if !TR_NEWFORMAT
 	DPRINTK("atype=%x, drate=%x, trel=%x, asram=%dK, srp=%x, "
@@ -598,6 +629,62 @@
 		dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
 		dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
 #endif
+	/* Calculate the maximum DHB we can use */
+	switch (ti->mapped_ram_size) {
+	case  16 : /* 8KB shared RAM */
+		ti->dhb_size4mb  = MIN(ti->dhb_size4mb, 2048);
+		ti->rbuf_len4 = 2048;
+		ti->rbuf_cnt4 = 1;
+		ti->dhb_size16mb = MIN(ti->dhb_size16mb, 2048);
+		ti->rbuf_len16 = 2048;
+		ti->rbuf_cnt16 = 1;
+		break;
+	case  32 : /* 16KB shared RAM */
+		ti->dhb_size4mb  = MIN(ti->dhb_size4mb, 4464);
+		ti->rbuf_len4 = 512;
+		ti->rbuf_cnt4 = 9;
+		ti->dhb_size16mb = MIN(ti->dhb_size16mb, 4096);
+		ti->rbuf_len16 = 2048;
+		ti->rbuf_cnt16 = 2;
+		break;
+	case  64 : /* 32KB shared RAM */
+		ti->dhb_size4mb  = MIN(ti->dhb_size4mb, 4464);
+		ti->rbuf_len4 = 2048;
+		ti->rbuf_cnt4 = 3;
+		ti->dhb_size16mb = MIN(ti->dhb_size16mb, 10240);
+		ti->rbuf_len16 = 2048;
+		ti->rbuf_cnt16 = 5;
+		break;
+	case 127 : /* 63KB shared RAM */
+		ti->dhb_size4mb  = MIN(ti->dhb_size4mb, 4464);
+		ti->rbuf_len4 = 2048;
+		ti->rbuf_cnt4 = 3;
+		ti->dhb_size16mb = MIN(ti->dhb_size16mb, 16384);
+		ti->rbuf_len16 = 2048;
+		ti->rbuf_cnt16 = 8;
+		break;
+	case 128 : /* 64KB shared RAM */
+		ti->dhb_size4mb  = MIN(ti->dhb_size4mb, 4464);
+		ti->rbuf_len4 = 2048;
+		ti->rbuf_cnt4 = 3;
+		ti->dhb_size16mb = MIN(ti->dhb_size16mb, 17960);
+		ti->rbuf_len16 = 2048;
+		ti->rbuf_cnt16 = 9;
+		break;
+	default  :
+		ti->dhb_size4mb  = 2048;
+		ti->rbuf_len4 = 2048;
+		ti->rbuf_cnt4 = 1;
+		ti->dhb_size16mb = 2048;
+		ti->rbuf_len16 = 2048;
+		ti->rbuf_cnt16 = 1;
+		break;
+	}
+
+	ti->maxmtu16 = ti->dhb_size16mb-((ti->rbuf_cnt16)<<3)-TR_HLEN;
+	ti->maxmtu4  = ti->dhb_size4mb-((ti->rbuf_cnt4)<<3)-TR_HLEN;
+	DPRINTK("Maximum MTU 16Mbps: %d, 4Mbps: %d\n",
+		ti->maxmtu16, ti->maxmtu4);
 
 	dev->base_addr=PIOaddr; /* set the value for device */
 
@@ -639,9 +726,9 @@
 	dev->open		= tok_open;
 	dev->stop		= tok_close;
 	dev->hard_start_xmit	= tok_send_packet;
-	dev->get_stats 		= NULL;
 	dev->get_stats 		= tok_get_stats;
 	dev->set_multicast_list = NULL;
+	dev->change_mtu         = ibmtr_change_mtu;
 
 #ifndef MODULE
 	tr_setup(dev);
@@ -1111,13 +1198,13 @@
 #endif
 
 	encoded_addr=(ti->sram + ntohs(hw_encoded_addr));
-
+	ti->ring_speed = readb(ti->init_srb+offsetof(struct srb_init_response, init_status)) & 0x01 ? 16 : 4;
 #if !TR_NEWFORMAT
 	DPRINTK("encoded addr (%04X,%04X,%08X): ", hw_encoded_addr,
 		ntohs(hw_encoded_addr), encoded_addr);
 #else
-	DPRINTK("Initial interrupt : %s Mbps, shared RAM base %08x.\n",
-		(readb(ti->init_srb+offsetof(struct srb_init_response, init_status)) & 0x01) ? "16" : "4", ti->sram);
+	DPRINTK("Initial interrupt : %d Mbps, shared RAM base %08x.\n",
+		ti->ring_speed, ti->sram);
 #endif
 
 	ti->auto_ringspeedsave=readb(ti->init_srb
@@ -1217,13 +1304,22 @@
 	       ti->init_srb + offsetof(struct dir_open_adapter, command));
 	writew(htons(OPEN_PASS_BCON_MAC),
 	       ti->init_srb + offsetof(struct dir_open_adapter, open_options));
-	writew(htons(NUM_RCV_BUF),
-	       ti->init_srb + offsetof(struct dir_open_adapter, num_rcv_buf));
-	writew(htons(RCV_BUF_LEN),
-	       ti->init_srb + offsetof(struct dir_open_adapter, rcv_buf_len));
-	writew(htons(DHB_LENGTH),
-	       ti->init_srb + offsetof(struct dir_open_adapter, dhb_length));
-	writeb(NUM_DHB,
+	if (ti->ring_speed == 16) {
+		writew(htons(ti->dhb_size16mb),
+		       ti->init_srb + offsetof(struct dir_open_adapter, dhb_length));
+		writew(htons(ti->rbuf_cnt16),
+		       ti->init_srb + offsetof(struct dir_open_adapter, num_rcv_buf));
+		writew(htons(ti->rbuf_len16),
+		       ti->init_srb + offsetof(struct dir_open_adapter, rcv_buf_len));
+	} else {
+		writew(htons(ti->dhb_size4mb),
+		       ti->init_srb + offsetof(struct dir_open_adapter, dhb_length));
+		writew(htons(ti->rbuf_cnt4),
+		       ti->init_srb + offsetof(struct dir_open_adapter, num_rcv_buf));
+		writew(htons(ti->rbuf_len4),
+		       ti->init_srb + offsetof(struct dir_open_adapter, rcv_buf_len));
+	}
+	writeb(NUM_DHB, /* always 2 */ 
 	       ti->init_srb + offsetof(struct dir_open_adapter, num_dhb));
 	writeb(DLC_MAX_SAP,
 	       ti->init_srb + offsetof(struct dir_open_adapter, dlc_max_sap));
@@ -1260,10 +1356,10 @@
 	
 	/* Figure out the size of the 802.5 header */
 	if (!(trhdr->saddr[0] & 0x80)) /* RIF present? */
-		hdr_len=sizeof(struct trh_hdr)-18;
+		hdr_len=sizeof(struct trh_hdr)-TR_MAXRIFLEN;
 	else 
 		hdr_len=((ntohs(trhdr->rcf) & TR_RCF_LEN_MASK)>>8)
-			+sizeof(struct trh_hdr)-18;
+			+sizeof(struct trh_hdr)-TR_MAXRIFLEN;
 
 	llc = (struct trllc *)(ti->current_skb->data + hdr_len);
 
@@ -1304,6 +1400,7 @@
 	memcpy_toio(dhb, ti->current_skb->data, ti->current_skb->len);
 
 	writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD);
+	ti->tr_stats.tx_bytes+=ti->current_skb->len;
 	dev->tbusy=0;
 	dev_kfree_skb(ti->current_skb);
 	ti->current_skb=NULL;
@@ -1364,8 +1461,8 @@
 		return;
 	}
 
-       	if ((readb(llc + offsetof(struct trllc, dsap))==0xAA) &&
-       	    (readb(llc + offsetof(struct trllc, ssap))==0xAA)) {
+       	if ((readb(llc + offsetof(struct trllc, dsap))==EXTENDED_SAP) &&
+       	    (readb(llc + offsetof(struct trllc, ssap))==EXTENDED_SAP)) {
        		IPv4_p = 1;
        	}
 
@@ -1452,9 +1549,9 @@
 
        	writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD);
 
+	ti->tr_stats.rx_bytes += skb->len;
        	ti->tr_stats.rx_packets++;
 
-	tr_reformat(skb, lan_hdr_len); 
 	skb->protocol = tr_type_trans(skb,dev);
 
  	if (IPv4_p){
@@ -1525,6 +1622,17 @@
 	struct tok_info *toki;
 	toki=(struct tok_info *) dev->priv;
 	return (struct net_device_stats *) &toki->tr_stats;
+}
+
+int ibmtr_change_mtu(struct device *dev, int mtu) {
+	struct tok_info *ti = (struct tok_info *) dev->priv;
+	
+	if (ti->ring_speed == 16 && mtu > ti->maxmtu16)
+		return -EINVAL;
+	if (ti->ring_speed == 4 && mtu > ti->maxmtu4)
+		return -EINVAL;
+	dev->mtu = mtu;
+	return 0;
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.1.108/linux/drivers/net/ibmtr.h linux/drivers/net/ibmtr.h
--- v2.1.108/linux/drivers/net/ibmtr.h	Sat Nov 29 10:33:19 1997
+++ linux/drivers/net/ibmtr.h	Fri Jul 10 13:51:41 1998
@@ -182,8 +182,14 @@
 	unsigned char token_release;
 	unsigned char avail_shared_ram;
 	unsigned char shared_ram_paging;
-	unsigned char dhb_size4mb;
-	unsigned char dhb_size16mb;
+	unsigned short dhb_size4mb;
+	unsigned short rbuf_len4;
+	unsigned short rbuf_cnt4;
+	unsigned short maxmtu4;
+	unsigned short dhb_size16mb;
+	unsigned short rbuf_len16;
+	unsigned short rbuf_cnt16;
+	unsigned short maxmtu16;
 	/* Additions by David Morris       */
 	unsigned char do_tok_int;
 	struct wait_queue *wait_for_tok_int;
@@ -207,6 +213,7 @@
 	unsigned char readlog_pending;
 	unsigned short adapter_int_enable; /* Adapter-specific int enable */
         struct timer_list tr_timer;
+	unsigned char ring_speed;
 	__u32 func_addr;
 };
 
diff -u --recursive --new-file v2.1.108/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v2.1.108/linux/drivers/net/ne.c	Tue Jun  9 11:57:30 1998
+++ linux/drivers/net/ne.c	Fri Jul 10 15:35:46 1998
@@ -82,6 +82,7 @@
 	{PCI_VENDOR_ID_KTI,		PCI_DEVICE_ID_KTI_ET32P2,		"KTI ET32P2" },
 	{PCI_VENDOR_ID_NETVIN,		PCI_DEVICE_ID_NETVIN_NV5000SC,		"NetVin NV5000" },
 	{PCI_VENDOR_ID_VIA,		PCI_DEVICE_ID_VIA_82C926,		"VIA 82C926 Amazon" },
+	{PCI_VENDOR_ID_SURECOM,		PCI_DEVICE_ID_SURECOM_NE34,		"SureCom NE34"},
 	{0,}
 };
 #endif
diff -u --recursive --new-file v2.1.108/linux/drivers/net/ne2k-pci.c linux/drivers/net/ne2k-pci.c
--- v2.1.108/linux/drivers/net/ne2k-pci.c	Wed Jun 24 22:54:06 1998
+++ linux/drivers/net/ne2k-pci.c	Fri Jul 10 15:35:46 1998
@@ -69,6 +69,7 @@
 	{0x8e2e, 0x3000, "KTI ET32P2"},
 	{0x4a14, 0x5000, "NetVin NV5000SC"},
 	{0x1106, 0x0926, "Via 82C926"},
+	{0x10bd, 0x0e34, "SureCom NE34"},
 	{0,}
 };
 
diff -u --recursive --new-file v2.1.108/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.1.108/linux/drivers/net/ppp.c	Tue Jun 23 10:01:23 1998
+++ linux/drivers/net/ppp.c	Fri Jul 10 14:02:00 1998
@@ -2,15 +2,9 @@
  *
  *  Michael Callahan <callahan@maths.ox.ac.uk>
  *  Al Longyear <longyear@netcom.com>
- *  Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
- *  Cyrus Durgin <cider@speakeasy.org> (changes for kmod)
+ *  Extensively rewritten by Paul Mackerras <paulus@cs.anu.edu.au>
  *
- *  Dynamic PPP devices by Jim Freeman <jfree@caldera.com>.
- *  ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid <ewerlid@syscon.uu.se>
- *
- *  Machine hang caused by NULLing a live wait queue fix. <Alan.Cox@linux.org>
- *
- *  ==FILEVERSION 980608==
+ *  ==FILEVERSION 980704==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the number above to the
@@ -47,21 +41,22 @@
 */
 
 #define OPTIMIZE_FLAG_TIME	((HZ * 3)/2)
-
 #define CHECK_CHARACTERS	1
-#define PPP_COMPRESS		1
 
-/* $Id: ppp.c,v 1.14 1997/11/27 06:04:45 paulus Exp $ */
+#define PPP_MAX_RCV_QLEN	32	/* max # frames we queue up for pppd */
+
+/* $Id: ppp.c,v 1.19 1998/07/07 04:27:37 paulus Exp $ */
 
-#include <linux/config.h> /* for CONFIG_KMOD */
+#include <linux/version.h>
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
-#include <linux/poll.h>
 #include <linux/interrupt.h>
 #include <linux/ptrace.h>
+#include <linux/poll.h>
 #include <linux/in.h>
 #include <linux/malloc.h>
 #include <linux/tty.h>
@@ -72,18 +67,13 @@
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/uaccess.h>
+
 #include <linux/if.h>
 #include <linux/if_ether.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
-#include <linux/rtnetlink.h>
 #include <linux/inet.h>
 #include <linux/ioctl.h>
-#include <linux/init.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>
@@ -101,49 +91,38 @@
 #include <linux/kmod.h>
 #endif
 
-#ifndef PPP_IPX
-#define PPP_IPX 0x2b  /* IPX protocol over PPP */
-#endif
-
-#ifndef PPP_LQR
-#define PPP_LQR 0xc025	/* Link Quality Reporting Protocol */
-#endif
+/*
+ * Local functions
+ */
 
 #ifdef CONFIG_MODULES
 static int ppp_register_compressor (struct compressor *cp);
 static void ppp_unregister_compressor (struct compressor *cp);
 #endif
 
-/*
- * Local functions
- */
+static void ppp_async_init(struct ppp *ppp);
+static void ppp_async_release(struct ppp *ppp);
+static int ppp_tty_push(struct ppp *ppp);
+static int ppp_async_encode(struct ppp *ppp);
+static int ppp_async_send(struct ppp *, struct sk_buff *);
+
+static int ppp_ioctl(struct ppp *, unsigned int, unsigned long);
+static int ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp);
+static void ppp_proto_ccp(struct ppp *ppp, __u8 *dp, int len, int rcvd);
+static void ppp_ccp_closed(struct ppp *ppp);
+static int ppp_receive_frame(struct ppp *, struct sk_buff *);
+static void ppp_receive_error(struct ppp *ppp);
+static void ppp_output_wakeup(struct ppp *ppp);
+static void ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb);
+static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
+static struct sk_buff *ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb);
 
-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 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 ppp_dev_xmit_ip (struct ppp *ppp, struct ppp_buffer *buf,
-			    __u8 *data, int len, enum NPmode npmode);
-
-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 *);
-
-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))
+static struct ppp *ppp_alloc (void);
+static void ppp_generic_init(struct ppp *ppp);
+static void ppp_release(struct ppp *ppp);
+static void ppp_print_buffer (const char *, const __u8 *, int);
+static struct compressor *find_compressor (int type);
 
 #ifndef OPTIMIZE_FLAG_TIME
 #define OPTIMIZE_FLAG_TIME	0
@@ -156,68 +135,26 @@
 static int  flag_time = OPTIMIZE_FLAG_TIME;
 MODULE_PARM(flag_time, "i");
 
-/*
- * 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 net_device_stats *ppp_dev_stats (struct device *);
-
-/*
- * TTY callbacks
- */
-
-static ssize_t ppp_tty_read (struct tty_struct *, struct file *, __u8 *,
-			     size_t);
-static ssize_t ppp_tty_write (struct tty_struct *, struct file *,
-			      const __u8 *, size_t);
-static int ppp_tty_ioctl (struct tty_struct *, struct file *, unsigned int,
-			  unsigned long);
-static unsigned int ppp_tty_poll (struct tty_struct *tty, struct file *filp,
-				  poll_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_MAGIC(ppp)	do { \
 	if (ppp->magic != PPP_MAGIC) { \
-		printk(KERN_WARNING "bad magic for ppp %p at %s:%d\n", \
-		       ppp, __FILE__, __LINE__); \
+		printk(ppp_magic_warn, ppp, __FILE__, __LINE__); \
 	} \
 } while (0)
 #define CHECK_PPP(a)	do { \
 	CHECK_PPP_MAGIC(ppp); \
 	if (!ppp->inuse) { \
-		printk (ppp_warning, __LINE__); \
+		printk(ppp_warning, __LINE__); \
 		return a; \
 	} \
 } while (0)
 #define CHECK_PPP_VOID() do { \
 	CHECK_PPP_MAGIC(ppp); \
 	if (!ppp->inuse) { \
-		printk (ppp_warning, __LINE__); \
+		printk(ppp_warning, __LINE__); \
 		return; \
 	} \
 } while (0)
 
-#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)	((ppp)->tty)
@@ -226,39 +163,43 @@
 static struct ppp *ppp_list = NULL;
 static struct ppp *ppp_last = NULL;
 
-/* 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 */
+/* Define these strings only once for all macro invocations */
 static char ppp_warning[] = KERN_WARNING "PPP: ALERT! not INUSE! %d\n";
+static char ppp_magic_warn[] = KERN_WARNING "bad magic for ppp %p at %s:%d\n";
 
 static char szVersion[]		= PPP_VERSION;
 
-/*
- * Information for the protocol decoder
- */
+EXPORT_SYMBOL(ppp_register_compressor);
+EXPORT_SYMBOL(ppp_unregister_compressor);
 
-typedef int (*pfn_proto)  (struct ppp *, __u16, __u8 *, int);
+/*************************************************************
+ * LINE DISCIPLINE SUPPORT
+ *    The following code implements the PPP line discipline
+ *    and supports using PPP on an async serial line.
+ *************************************************************/
 
-typedef struct ppp_proto_struct {
-	int		proto;
-	pfn_proto	func;
-} ppp_proto_type;
+#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)))
 
-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 !!! */
-};
+/*
+ * TTY callbacks
+ */
+
+static ssize_t ppp_tty_read(struct tty_struct *, struct file *, __u8 *,
+			    size_t);
+static ssize_t ppp_tty_write(struct tty_struct *, struct file *, const __u8 *,
+			     size_t);
+static int ppp_tty_ioctl(struct tty_struct *, struct file *, unsigned int,
+			 unsigned long);
+static unsigned int ppp_tty_poll(struct tty_struct *tty, struct file *filp,
+				 poll_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);
 
 __u16 ppp_crc16_table[256] =
 {
@@ -295,6 +236,7 @@
 	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
 	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
 };
+EXPORT_SYMBOL(ppp_crc16_table);
 
 #ifdef CHECK_CHARACTERS
 static __u32 paritytab[8] =
@@ -304,43 +246,29 @@
 };
 #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.
+/*
+ * This procedure is called at initialization time to register
+ * the PPP line discipline.
  */
-
-__initfunc(static int
-ppp_first_time (void))
+static int
+ppp_first_time(void)
 {
 	static struct tty_ldisc	ppp_ldisc;
 	int    status;
 
-	printk (KERN_INFO
-		"PPP: version %s (demand dialling)"
-		"\n", szVersion);
+	printk(KERN_INFO
+	       "PPP: version %s (demand dialling)"
+	       "\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");
+	printk(KERN_INFO
+	       "TCP compression code copyright 1989 Regents of the "
+	       "University of California\n");
 #endif
 
-/*
- * Register the tty discipline
- */
+	/*
+	 * Register the tty discipline
+	 */
 	(void) memset (&ppp_ldisc, 0, sizeof (ppp_ldisc));
 	ppp_ldisc.magic		= TTY_LDISC_MAGIC;
 	ppp_ldisc.name          = "ppp";
@@ -356,89 +284,20 @@
 
 	status = tty_register_ldisc (N_PPP, &ppp_ldisc);
 	if (status == 0)
-		printk (KERN_INFO "PPP line discipline registered.\n");
+		printk(KERN_INFO "PPP line discipline registered.\n");
 	else
-		printk (KERN_ERR "error registering line discipline: %d\n",
-			status);
+		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)
-{
-	dev->hard_header_len  = PPP_HDRLEN;
-
-	/* 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;
-
-	dev_init_buffers(dev);
-
-	/* New-style flags */
-	dev->flags	= IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
-
-	return 0;
-}
 
+#ifndef MODULE
 /*
- * Local procedure to initialize the ppp structure
+ * Called at boot time if the PPP driver is compiled into the kernel.
  */
-
-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->last_xmit	= jiffies - flag_time;
-	ppp->last_recv  = jiffies;
-
-	/* clear statistics */
-	memset(&ppp->stats, 0, sizeof (struct pppstat));
-	memset(&ppp->estats, 0, sizeof(ppp->estats));
-
-	/* PPP compression data */
-	ppp->sc_xc_state =
-	ppp->sc_rc_state = NULL;
-}
-
-EXPORT_SYMBOL(ppp_register_compressor);
-EXPORT_SYMBOL(ppp_unregister_compressor);
-EXPORT_SYMBOL(ppp_crc16_table);
-
-/* called at boot/load time for each ppp device defined in the kernel */
-
-#ifndef MODULE
 int
-ppp_init (struct device *dev)
+ppp_init(struct device *dev)
 {
 	static int first_time = 1;
 	int    answer = 0;
@@ -453,1712 +312,1259 @@
 }
 #endif
 
-#define BUFFER_MAGIC	0x1d10
-#define CHECK_BUF_MAGIC(buf)	do { \
-	if (buf->magic != BUFFER_MAGIC) { \
-		printk(KERN_WARNING "bad magic for ppp buffer %p at %s:%d\n", \
-		       buf, __FILE__, __LINE__); \
-	} \
-} while (0)
-
 /*
- * Routine to allocate a buffer for later use by the driver.
+ * Initialize the async-specific parts of the ppp structure.
  */
-
-static struct ppp_buffer *
-ppp_alloc_buf (int size, int type)
+static void
+ppp_async_init(struct ppp *ppp)
 {
-	struct ppp_buffer *buf;
+	ppp->escape = 0;
+	ppp->toss   = 0xE0;
+	ppp->tty_pushing = 0;
 
-	buf = (struct ppp_buffer *) kmalloc (size + sizeof (struct ppp_buffer),
-					     GFP_ATOMIC);
+	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    = 0xffffffff;
 
-	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;
-		buf->magic  = BUFFER_MAGIC;
-	}
-	return (buf);
-}
+	ppp->tpkt = NULL;
+	ppp->tfcs = PPP_INITFCS;
+	ppp->optr = ppp->obuf;
+	ppp->olim = ppp->obuf;
 
-/*
- * Routine to release the allocated buffer.
- */
+	ppp->rpkt = NULL;
+	ppp->rfcs = PPP_INITFCS;
 
-static void
-ppp_free_buf (struct ppp_buffer *ptr)
-{
-	if (ptr != NULL) {
-		CHECK_BUF_MAGIC(ptr);
-		kfree (ptr);
-	}
+	ppp->tty  = NULL;
+	ppp->backup_tty = NULL;
+
+	ppp->bytes_sent = 0;
+	ppp->bytes_rcvd = 0;
 }
 
 /*
- * Lock the indicated transmit buffer
+ * Clean up the async-specific parts of the ppp structure.
  */
-
-extern inline int
-lock_buffer (register struct ppp_buffer *buf)
+static void
+ppp_async_release(struct ppp *ppp)
 {
-	unsigned long state;
-	unsigned long flags;
-/*
- * Save the current state and if free then set it to the "busy" state
- */
-	CHECK_BUF_MAGIC(buf);
-	save_flags (flags);
-	cli ();
-	state = buf->locked;
-	if (state == 0)
-		buf->locked = 2;
+	struct sk_buff *skb;
 
-	restore_flags (flags);
-	return (state);
+	if ((skb = ppp->rpkt) != NULL)
+		kfree_skb(skb);
+	ppp->rpkt = NULL;
+	if ((skb = ppp->tpkt) != NULL)
+		kfree_skb(skb);
+	ppp->tpkt = NULL;
 }
 
 /*
- * 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.
+ * TTY callback.
+ *
+ * Called when the tty discipline is switched to PPP.
  */
 
 static int
-ppp_changedmtu (struct ppp *ppp, int new_mtu, int new_mru)
+ppp_tty_open (struct tty_struct *tty)
 {
-	struct device *dev;
-	unsigned long flags;
+	struct ppp *ppp;
+
+	/*
+	 * Allocate a ppp structure to use.
+	 */
+	tty->disc_data = NULL;
+	ppp = ppp_find(current->pid);
+	if (ppp != NULL) {
+		/*
+		 * If we are taking over a ppp unit which is currently
+		 * connected to a loopback pty, there's not much to do.
+		 */
+		CHECK_PPP(-EINVAL);
 
-	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;
+	} else {
+		ppp = ppp_alloc();
+		if (ppp == NULL) {
+			printk(KERN_ERR "ppp_alloc failed\n");
+			return -ENFILE;
+		}
 
-	int mtu, mru;
-/*
- *  Allocate the buffer from the kernel for the data
- */
-	CHECK_PPP(0);
-	dev = ppp2dev (ppp);
-	if (ppp->flags & SC_DEBUG)
-		printk(KERN_DEBUG "%s: changedmtu %d %d\n", ppp->name,
-		       new_mtu, new_mru);
-	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;
-
-	new_wbuf = ppp_alloc_buf (mtu+PPP_HDRLEN,	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_HDRLEN,	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) {
-		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;
+		/*
+		 * Initialize the control block
+		 */
+		ppp_generic_init(ppp);
+		ppp_async_init(ppp);
+
+		MOD_INC_USE_COUNT;
 	}
-/*
- *  Update the pointers to the new buffer structures.
- */
-	save_flags(flags);
-	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;
-
-	if (old_wbuf)
-		new_wbuf->locked = old_wbuf->locked;
-
-	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;
+	tty->disc_data = ppp;
+	ppp->tty       = tty;
 
-	restore_flags(flags);
-/*
- *  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;
+	/*
+	 * Flush any pending characters in the driver
+	 */
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer (tty);
+
+	return ppp->line;
 }
 
 /*
- * CCP is down; free (de)compressor state if necessary.
+ * TTY callback.
+ *
+ * Called when the line discipline is changed to something
+ * else, the tty is closed, or the tty detects a hangup.
  */
 
 static void
-ppp_ccp_closed (struct ppp *ppp)
+ppp_tty_close (struct tty_struct *tty)
 {
-	unsigned long flags;
+	struct ppp *ppp = tty2ppp(tty);
 
-	save_flags(flags);
-	cli();
-	ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN);
-	restore_flags(flags);
-	if (ppp->flags & SC_DEBUG)
-		printk(KERN_DEBUG "%s: ccp closed\n", ppp->name);
-	if (ppp->sc_xc_state) {
-		(*ppp->sc_xcomp->comp_free) (ppp->sc_xc_state);
-		ppp->sc_xc_state = NULL;
+	if (ppp == NULL)
+		return;
+	tty->disc_data = NULL;
+	if (ppp->magic != PPP_MAGIC) {
+		printk(KERN_WARNING "ppp_tty_close: bogus\n");
+		return;
+	}
+	if (!ppp->inuse) {
+		printk(KERN_WARNING "ppp_tty_close: not inuse\n");
+		ppp->tty = ppp->backup_tty = 0;
+		return;
 	}
+	if (tty == ppp->backup_tty)
+		ppp->backup_tty = 0;
+	if (tty != ppp->tty)
+		return;
+	if (ppp->backup_tty) {
+		ppp->tty = ppp->backup_tty;
+	} else {
+		ppp->tty = 0;
+		ppp->sc_xfer = 0;
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_INFO "ppp: channel %s closing.\n",
+			       ppp2dev(ppp)->name);
 
-	if (ppp->sc_rc_state) {
-		(*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state);
-		ppp->sc_rc_state = NULL;
+		ppp_async_release(ppp);
+		ppp_release(ppp);
+		ppp->inuse = 0;
+		MOD_DEC_USE_COUNT;
 	}
 }
 
 /*
- * 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.
+ * Read a PPP frame from the rcv_q list,
+ * waiting if necessary
  */
-
-static void
-ppp_release (struct ppp *ppp)
+static ssize_t
+ppp_tty_read(struct tty_struct *tty, struct file *file, __u8 * buf,
+	     size_t nr)
 {
-	struct tty_struct *tty;
-	struct device *dev;
-
-	CHECK_PPP_MAGIC(ppp);
-	tty = ppp2tty (ppp);
-	dev = ppp2dev (ppp);
-
-	if (ppp->flags & SC_DEBUG)
-		printk(KERN_DEBUG "%s released\n", ppp->name);
-
-	ppp_ccp_closed (ppp);
-
-	/* Ensure that the pppd process is not hanging on poll() */
-	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 */
-
-	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;
-	ppp->backup_tty = NULL;
-}
+	struct ppp *ppp = tty2ppp (tty);
+	struct sk_buff *skb;
+	ssize_t len, err;
 
-/*
- * TTY callback.
- *
- * Called when the line discipline is changed to something
- * else, the tty is closed, or the tty detects a hangup.
- */
+	/*
+	 * Validate the pointers
+	 */
+	if (!ppp)
+		return -EIO;
+	CHECK_PPP(-ENXIO);
 
-static void
-ppp_tty_close (struct tty_struct *tty)
-{
-	struct ppp *ppp = tty2ppp (tty);
+	/*
+	 * Before we attempt to write the frame to the user, ensure that the
+	 * user has access to the pages for the total buffer length.
+	 */
+	err = verify_area(VERIFY_WRITE, buf, nr);
+	if (err != 0)
+		return (err);
 
-	if (ppp != NULL) {
-		if (ppp->magic != PPP_MAGIC) {
-			if (ppp->flags & SC_DEBUG)
-				printk (KERN_WARNING
-				       "ppp: trying to close unopened tty!\n");
-			return;
-		}
-		CHECK_PPP_VOID();
-		tty->disc_data = NULL;
-		if (tty == ppp->backup_tty)
-			ppp->backup_tty = 0;
-		if (tty != ppp->tty)
-			return;
-		if (ppp->backup_tty) {
-			ppp->tty = ppp->backup_tty;
-		} else {
-			ppp->sc_xfer = 0;
-			if (ppp->flags & SC_DEBUG)
-				printk (KERN_INFO "ppp: channel %s closing.\n",
-					ppp2dev(ppp)->name);
-			ppp_release (ppp);
-			MOD_DEC_USE_COUNT;
-		}
-	}
-}
+	/*
+	 * Wait for a frame to arrive if necessary.
+	 * We increment the module use count so that the module
+	 * can't go away while we're sleeping.
+	 */
+	MOD_INC_USE_COUNT;
+	skb = NULL;
+	for (;;) {
+		ppp = tty2ppp(tty);
+		err = 0;
+		if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse
+		    || tty != ppp->tty)
+			break;
 
-/*
- * TTY callback.
- *
- * Called when the tty discipline is switched to PPP.
- */
+		skb = skb_dequeue(&ppp->rcv_q);
+		if (skb != 0)
+			break;
 
-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) {
-		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) {
 		/*
-		 * If we are taking over a ppp unit which is currently
-		 * connected to a loopback pty, there's not much to do.
+		 * If no frame is available, return -EAGAIN or wait.
 		 */
-		CHECK_PPP(-EINVAL);
-		tty->disc_data = ppp;
-		ppp->tty       = tty;
+		err = -EAGAIN;
+		if (file->f_flags & O_NONBLOCK)
+			break;
 
-	} else {
-		ppp = ppp_alloc();
-		if (ppp == NULL) {
-			printk (KERN_ERR "ppp_alloc failed\n");
-			return -ENFILE;
-		}
-/*
- * Initialize the control block
- */
-		ppp_init_ctrl_blk (ppp);
-		tty->disc_data = ppp;
-		ppp->tty       = tty;
-/*
- * Allocate space for the default VJ header compression slots
- */
-		ppp->slcomp = slhc_init (16, 16);
-		if (ppp->slcomp == NULL) {
-			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) {
-			printk (KERN_ERR "ppp_tty_open: "
-				"no space for user receive buffer\n");
-			ppp_release (ppp);
-			return -ENOMEM;
-		}
+		current->timeout = 0;
+		interruptible_sleep_on(&ppp->read_wait);
+		err = -EINTR;
+		if (signal_pending(current))
+			break;
+	}
+	MOD_DEC_USE_COUNT;
+	if (skb == 0)
+		return err;
 
+	/*
+	 * Ensure that the frame will fit within the caller's buffer.
+	 * If not, just discard the frame.
+	 */
+	len = skb->len;
+	if (len > nr) {
 		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;
+			printk(KERN_DEBUG
+			       "ppp: read of %lu bytes too small for %ld "
+			       "frame\n", (unsigned long) nr, (long) len);
+		ppp->stats.ppp_ierrors++;
+		err = -EOVERFLOW;
+		goto out;
 	}
-/*
- * 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);
-	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;
-	unsigned long flags;
+	/*
+	 * Copy the received data from the buffer to the caller's area.
+	 */
+	err = len;
+	if (copy_to_user(buf, skb->data, len))
+		err = -EFAULT;
 
-	CHECK_PPP_VOID();
-	CHECK_BUF_MAGIC(xbuf);
-/*
- * Prevent re-entrancy by ensuring that this routine is called only once.
- */
-	save_flags(flags);
-	cli ();
-	if (ppp->flags & SC_XMIT_BUSY) {
-		restore_flags(flags);
-		return;
-	}
-	ppp->flags |= SC_XMIT_BUSY;
-	restore_flags(flags);
-/*
- * 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.
- */
-			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.
- */
-			save_flags(flags);
-			cli();
-			xbuf = ppp->s1buf;
-			ppp->s1buf = NULL;
-			if (xbuf == NULL) {
-				xbuf = ppp->s2buf;
-				ppp->s2buf = NULL;
-			}
-/*
- * If there is a pending buffer then transmit it now.
- */
-			if (xbuf != NULL) {
-				ppp->flags &= ~SC_XMIT_BUSY;
-				ppp_kick_tty (ppp, xbuf);
-				restore_flags(flags);
-				return;
-			}
-			restore_flags(flags);
-		}
-	}
-/*
- * Clear the re-entry flag
- */
-	save_flags(flags);	/* &=~ may not be atomic */
-	cli ();
-	ppp->flags &= ~SC_XMIT_BUSY;
-	restore_flags(flags);
+out:
+	kfree_skb(skb);
+	return err;
 }
 
 /*
- * 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.
+ * Writing to a tty in ppp line discipline sends a PPP frame.
+ * Used by pppd to send control packets (LCP, etc.).
  */
-
-static void
-ppp_tty_wakeup (struct tty_struct *tty)
+static ssize_t
+ppp_tty_write(struct tty_struct *tty, struct file *file, const __u8 * data,
+	      size_t count)
 {
-	struct ppp_buffer *xbuf;
 	struct ppp *ppp = tty2ppp (tty);
+	__u8 *new_data;
+	struct sk_buff *skb;
 
+	/*
+	 * Verify the pointers.
+	 */
 	if (!ppp)
-		return;
-	CHECK_PPP_VOID();
+		return -EIO;
 
-	if (tty != ppp->tty) {
-		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-		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);
-}
+	if (ppp->magic != PPP_MAGIC)
+		return -EIO;
 
-/*
- * 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.
- */
+	CHECK_PPP(-ENXIO);
 
-static void
-ppp_kick_tty (struct ppp *ppp, struct ppp_buffer *xbuf)
-{
-	unsigned long flags;
+	/*
+	 * Ensure that the caller does not wish to send too much.
+	 */
+	if (count > PPP_MTU + PPP_HDRLEN) {
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_WARNING
+			       "ppp_tty_write: truncating user packet "
+			       "from %lu to mtu %d\n", (unsigned long) count,
+			       PPP_MTU + PPP_HDRLEN);
+		count = PPP_MTU + PPP_HDRLEN;
+	}
 
-	CHECK_PPP_VOID();
-	CHECK_BUF_MAGIC(xbuf);
-/*
- * 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;
+	/*
+	 * Allocate a buffer for the data and fetch it from the user space.
+	 */
+	skb = alloc_skb(count, GFP_KERNEL);
+	if (skb == NULL) {
+		printk(KERN_ERR "ppp_tty_write: no memory\n");
+		return 0;
 	}
-/*
- * 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);
-}
+	new_data = skb_put(skb, count);
 
-/*************************************************************
- * 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.
- *************************************************************/
+	/*
+	 * Retrieve the user's buffer
+	 */
+	if (copy_from_user(new_data, data, count)) {
+		kfree_skb(skb);
+		return -EFAULT;
+	}
 
-/*
- * 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.
- */
+	/*
+	 * Send the frame
+	 */
+	ppp_send_ctrl(ppp, skb);
 
-static int
-ppp_tty_room (struct tty_struct *tty)
-{
-	return 65536;	    /* We can handle an infinite amount of data. :-) */
+	return (ssize_t) count;
 }
 
 /*
- * Callback function when data is available at the tty driver.
+ * Process the IOCTL call for the tty device.
+ * Only the ioctls that relate to using ppp on async serial lines
+ * are processed here; the rest are handled by ppp_ioctl.
  */
-static void
-ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
-		 char *flags, int count)
+static int
+ppp_tty_ioctl (struct tty_struct *tty, struct file * file,
+               unsigned int param2, unsigned long param3)
 {
-	register struct ppp *ppp = tty2ppp (tty);
-	register struct ppp_buffer *buf = NULL;
-	__u8 chr;
+	struct ppp *ppp = tty2ppp (tty);
+	register int temp_i = 0;
+	int error = -EFAULT;
 
-	if (ppp != 0)
-		CHECK_PPP_VOID();
 	/*
-	 * This can happen if stuff comes in on the backup tty.
+	 * Verify the status of the PPP device.
 	 */
-	if (ppp == 0 || tty != ppp->tty)
-		return;
-/*
- * Fetch the pointer to the buffer. Be careful about race conditions.
- */
-	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: tty_receive 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);
+	if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse)
+		return -ENXIO;
 
-/*
- * 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;
-				switch (ppp->toss) {
-				case TTY_OVERRUN:
-					++ppp->estats.rx_fifo_errors;
-					break;
-				case TTY_FRAME:
-				case TTY_BREAK:
-					++ppp->estats.rx_frame_errors;
-					break;
-				}
-			}
-			++flags;
-		}
+	/*
+	 * The user must have an euid of root to do these requests.
+	 */
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
 
-/*
- * Set 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 indicates an error condition.
- */
+	switch (param2) {
+	case PPPIOCGASYNCMAP:
+		/*
+		 * Retrieve the transmit async map
+		 */
+		if (put_user(ppp->xmit_async_map[0], (int *) param3))
+			break;
+		error = 0;
+		break;
+
+	case PPPIOCSASYNCMAP:
+		/*
+		 * Set the transmit async map
+		 */
+		if (get_user(temp_i, (int *) param3))
+			break;
+		ppp->xmit_async_map[0] = temp_i;
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_INFO
+			       "ppp_tty_ioctl: set xmit asyncmap %x\n",
+			       ppp->xmit_async_map[0]);
+		error = 0;
+		break;
 
-#ifdef CHECK_CHARACTERS
-		if (chr & 0x80)
-			ppp->flags |= SC_RCV_B7_1;
-		else
-			ppp->flags |= SC_RCV_B7_0;
+	case PPPIOCSRASYNCMAP:
+		/*
+		 * Set the receive async map
+		 */
+		if (get_user(temp_i, (int *) param3))
+			break;
+		ppp->recv_async_map = temp_i;
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_INFO
+			       "ppp_tty_ioctl: set rcv asyncmap %x\n",
+			       ppp->recv_async_map);
+		error = 0;
+		break;
 
-		if (paritytab[chr >> 5] & (1 << (chr & 0x1F)))
-			ppp->flags |= SC_RCV_ODDP;
-		else
-			ppp->flags |= SC_RCV_EVNP;
-#endif
-/*
- * Branch on the character.
- */
-		switch (chr) {
-/*
- * 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_doframe (ppp) == 0) {
-				++ppp->stats.ppp_ierrors;
-				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;
+	case PPPIOCGXASYNCMAP:
+		/*
+		 * Get the map of characters to be escaped on transmission.
+		 */
+		if (copy_to_user((void *) param3, ppp->xmit_async_map,
+				 sizeof (ppp->xmit_async_map)))
 			break;
-/*
- * All other characters in the data come here. If the character is in the
- * receive mask then ignore the character.
- */
-		default:
-			/* If we're tossing, look no further. */
-			if (ppp->toss != 0)
-				break;
+		error = 0;
+		break;
 
-			/* If this is a control char to be ignored, do so */
-			if (in_rmap (ppp, chr))
-				break;
+	case PPPIOCSXASYNCMAP:
+		/*
+		 * Set the map of characters to be escaped on transmission.
+		 */
+		{
+			__u32 temp_tbl[8];
 
-			/*
-			 * Modify the next character if preceded by escape.
-			 * The escape character (0x7d) could be an escaped
-			 * 0x5d, if it follows an escape :-)
-			 */
-			if (ppp->escape) {
-				chr ^= PPP_TRANS;
-				ppp->escape = 0;
-			} else if (chr == PPP_ESCAPE) {
-				ppp->escape = PPP_TRANS;
+			if (copy_from_user(temp_tbl, (void *) param3,
+					   sizeof (temp_tbl)))
 				break;
-			}
 
-			/*
-			 * Decompress A/C and protocol compression here.
-			 */
-			if (buf->count == 0 && chr != PPP_ALLSTATIONS) {
-				buf_base(buf)[0] = PPP_ALLSTATIONS;
-				buf_base(buf)[1] = PPP_UI;
-				buf->count = 2;
-			}
-			if (buf->count == 2 && (chr & 1) != 0) {
-				buf_base(buf)[2] = 0;
-				buf->count = 3;
-			}
-/*
- * 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->estats.rx_length_errors;
-			ppp->toss |= 0xC0;
-			break;
-		}
-	}
-}
+			temp_tbl[1]  =	0x00000000;
+			temp_tbl[2] &= ~0x40000000;
+			temp_tbl[3] |=	0x60000000;
 
-/* on entry, a received frame is in ppp->rbuf.bufr
-   check it and dispose as appropriate */
+			memcpy(ppp->xmit_async_map, temp_tbl,
+			       sizeof (ppp->xmit_async_map));
 
-static int
-ppp_doframe (struct ppp *ppp)
-{
-	__u8	*data = buf_base (ppp->rbuf);
-	int	count = ppp->rbuf->count;
-	int	proto;
-	int	new_count;
-	__u8 *new_data;
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_INFO
+				       "ppp_tty_ioctl: set xasyncmap\n");
+			error = 0;
+		}
+		break;
 
-	CHECK_PPP(0);
-	CHECK_BUF_MAGIC(ppp->rbuf);
+	case PPPIOCXFERUNIT:
+		/*
+		 * Set up this PPP unit to be used next time this
+		 * process sets a tty to PPP line discipline.
+		 */
+		ppp->backup_tty = tty;
+		ppp->sc_xfer = current->pid;
+		error = 0;
+		break;
 
-/*
- * 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) && count > 0)
-			printk (KERN_DEBUG
-				"ppp_toss: tossing frame, reason = %x\n",
-				ppp->toss);
-		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_HDRLEN + 2) {
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_DEBUG
-				"ppp: got runt ppp frame, %d chars\n", count);
-		++ppp->estats.rx_length_errors;
-		return 0;
-	}
-/*
- * 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_DEBUG
-				"ppp: frame with bad fcs, length = %d\n",
-				count);
-			ppp_print_buffer("bad frame", data, count);
-		}
-		++ppp->estats.rx_crc_errors;
-		return 0;
-	}
-	count -= 2;		/* ignore the fcs characters */
-/*
- * Obtain the protocol from the frame
- */
-	proto = PPP_PROTOCOL(data);
-/*
- * 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 + PPP_HDRLEN, GFP_ATOMIC);
-			if (new_data == NULL) {
-				printk (KERN_ERR "ppp_doframe: no memory\n");
-				new_count = DECOMP_ERROR;
-			} else {
-				new_count = (*ppp->sc_rcomp->decompress)
-					(ppp->sc_rc_state, data, count,
-					 new_data, ppp->mru + PPP_HDRLEN);
-			}
-			switch (new_count) {
-			default:
-				ppp_doframe_lower (ppp, new_data, new_count);
-				kfree (new_data);
-				return 1;
+	case TCGETS:
+	case TCGETA:
+		/*
+		 * Allow users to read, but not set, the serial port parameters
+		 */
+		error = n_tty_ioctl (tty, file, param2, param3);
+		break;
 
-			case DECOMP_ERROR:
-				ppp->flags |= SC_DC_ERROR;
-				break;
+	case FIONREAD:
+		/*
+		 * Returns how many bytes are available for a read().
+		 */
+		{
+			unsigned long flags;
+			struct sk_buff *skb;
+			int count = 0;
 
-			case DECOMP_FATALERROR:
-				ppp->flags |= SC_DC_FERROR;
-				printk(KERN_ERR "ppp: fatal decomp error\n");
+			save_flags(flags);
+			cli();
+			skb = skb_peek(&ppp->rcv_q);
+			if (skb != 0)
+				count = skb->len;
+			restore_flags(flags);
+			if (put_user(count, (int *) param3))
 				break;
-			}
-/*
- * Log the error condition and discard the frame.
- */
-			if (new_data != 0)
-				kfree (new_data);
-			slhc_toss (ppp->slcomp);
-			++ppp->stats.ppp_ierrors;
-		} else {
-/*
- * 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);
+			error = 0;
 		}
-	} else if (proto == PPP_COMP && (ppp->flags & SC_DEBUG)) {
-		printk(KERN_DEBUG "ppp: frame not decompressed: "
-		       "flags=%x, count=%d, sc_rc_state=%p\n",
-		       ppp->flags, count, ppp->sc_rc_state);
+		break;
+
+	default:
+		/*
+		 *  All other ioctl() events will come here.
+		 */
+		error = ppp_ioctl(ppp, param2, param3);
+		break;
 	}
+	return error;
+}
+
 /*
- * Process the uncompressed frame.
+ * TTY callback.
+ *
+ * Process the poll() statement for the PPP device.
  */
-	ppp_doframe_lower (ppp, data, count);
-	return 1;
-}
 
-static void ppp_doframe_lower (struct ppp *ppp, __u8 *data, int count)
+static unsigned int
+ppp_tty_poll(struct tty_struct *tty, struct file *filp, poll_table * wait)
 {
-	__u16		proto = PPP_PROTOCOL (data);
-	ppp_proto_type	*proto_ptr;
+	struct ppp *ppp = tty2ppp(tty);
+	unsigned int mask = 0;
 
-	CHECK_PPP_VOID();
-/*
- * Ignore empty frames
- */
-	if (count <= PPP_HDRLEN)
-		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_HDRLEN],
-				count - PPP_HDRLEN))
-		ppp->stats.ppp_ioctects += count;
-	else
-		++ppp->stats.ppp_discards;
-}
+	if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) {
+		CHECK_PPP(0);
 
-/*
- * Put the input frame into the networking system for the indicated protocol
- */
+		poll_wait(filp, &ppp->read_wait, wait);
 
-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;
+		if (skb_peek(&ppp->rcv_q) != NULL)
+			mask |= POLLIN | POLLRDNORM;
+		if (tty->flags & (1 << TTY_OTHER_CLOSED)
+		    || tty_hung_up_p(filp))
+			mask |= POLLHUP;
+		mask |= POLLOUT | POLLWRNORM;
 	}
-/*
- * 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
- */
-	ppp->last_recv = jiffies;
-	netif_rx (skb);
-	return 1;
+	return mask;
 }
 
 /*
- * Process the receipt of an IP frame
+ * 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.
  */
-
-static int
-rcv_proto_ip (struct ppp *ppp, __u16 proto, __u8 * data, int count)
+static void
+ppp_tty_wakeup (struct tty_struct *tty)
 {
-	CHECK_PPP(0);
-	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;
+	struct ppp *ppp = tty2ppp (tty);
+
+	tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+	if (!ppp)
+		return;
+	CHECK_PPP_VOID();
+	if (tty != ppp->tty)
+		return;
+
+	if (ppp_tty_push(ppp))
+		ppp_output_wakeup(ppp);
 }
 
 /*
- * Process the receipt of an IPX frame
+ * Send a packet to the peer over an async tty line.
+ * Returns -1 iff the packet could not be accepted at present,
+ * 0 if the packet was accepted but we can't accept another yet, or
+ * 1 if we can accept another packet immediately.
+ * If this procedure returns 0, ppp_output_wakeup will be called
+ * exactly once.
  */
-
 static int
-rcv_proto_ipx (struct ppp *ppp, __u16 proto, __u8 * data, int count)
+ppp_async_send(struct ppp *ppp, struct sk_buff *skb)
 {
 	CHECK_PPP(0);
-	if (((ppp2dev (ppp)->flags & IFF_UP) != 0) && (count > 0))
-		return ppp_rcv_rx (ppp, htons (ETH_P_IPX), data, count);
-	return 0;
+
+	ppp_tty_push(ppp);
+
+	if (ppp->tpkt != NULL)
+		return -1;
+	ppp->tpkt = skb;
+	ppp->tpkt_pos = 0;
+
+	return ppp_tty_push(ppp);
 }
 
 /*
- * Process the receipt of an VJ Compressed frame
+ * Push as much data as possible out to the tty.
+ * Returns 1 if we finished encoding the current frame, 0 otherwise.
  */
-
 static int
-rcv_proto_vjc_comp (struct ppp *ppp, __u16 proto,
-		    __u8 *data, int count)
+ppp_tty_push(struct ppp *ppp)
 {
+	int avail, sent, done = 0;
+	struct tty_struct *tty = ppp2tty(ppp);
+
 	CHECK_PPP(0);
-	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->tty_pushing)
+		return 0;
+	if (tty == NULL || tty->disc_data != (void *) ppp)
+		goto flush;
+	while (ppp->optr < ppp->olim || ppp->tpkt != 0) {
+		ppp->tty_pushing = 1;
+		avail = ppp->olim - ppp->optr;
+		if (avail > 0) {
+			tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+			sent = tty->driver.write(tty, 0, ppp->optr, avail);
+			if (sent < 0)
+				goto flush;	/* error, e.g. loss of CD */
+			ppp->stats.ppp_obytes += sent;
+			ppp->optr += sent;
+			if (sent < avail) {
+				ppp->tty_pushing = 0;
+				return done;
+			}
 		}
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_NOTICE
-				"ppp: error in VJ decompression\n");
+		if (ppp->tpkt != 0)
+			done = ppp_async_encode(ppp);
+		ppp->tty_pushing = 0;
 	}
-	return 0;
+	return done;
+
+flush:
+	ppp->tty_pushing = 1;
+	ppp->stats.ppp_oerrors++;
+	if (ppp->tpkt != 0) {
+		kfree_skb(ppp->tpkt);
+		ppp->tpkt = 0;
+		done = 1;
+	}
+	ppp->optr = ppp->olim;
+	ppp->tty_pushing = 0;
+	return done;
 }
 
 /*
- * Process the receipt of an VJ Un-compressed frame
+ * Procedure to encode the data for async serial transmission.
+ * Does octet stuffing (escaping) and address/control
+ * and protocol compression.
+ * Assumes ppp->opkt != 0 on entry.
+ * Returns 1 if we finished the current frame, 0 otherwise.
  */
-
 static int
-rcv_proto_vjc_uncomp (struct ppp *ppp, __u16 proto,
-		      __u8 *data, int count)
+ppp_async_encode(struct ppp *ppp)
 {
+	int fcs, i, count, c;
+	unsigned char *buf, *buflim;
+	unsigned char *data;
+	int islcp;
+
 	CHECK_PPP(0);
-	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);
+
+	buf = ppp->obuf;
+	ppp->olim = buf;
+	ppp->optr = buf;
+	i = ppp->tpkt_pos;
+	data = ppp->tpkt->data;
+	count = ppp->tpkt->len;
+	fcs = ppp->tfcs;
+
+	/*
+	 * LCP packets with code values between 1 (configure-reqest)
+	 * and 7 (code-reject) must be sent as though no options
+	 * had been negotiated.
+	 */
+	islcp = PPP_PROTOCOL(data) == PPP_LCP
+		&& 1 <= data[PPP_HDRLEN] && data[PPP_HDRLEN] <= 7;
+
+	if (i == 0) {
+		/*
+		 * Start of a new packet - insert the leading FLAG
+		 * character if necessary.
+		 */
+		if (islcp || flag_time == 0
+		    || jiffies - ppp->last_xmit >= flag_time)
+			*buf++ = PPP_FLAG;
+		/* only reset idle time for data packets */
+		if (PPP_PROTOCOL(data) < 0x8000)
+			ppp->last_xmit = jiffies;
+		fcs = PPP_INITFCS;
+		++ppp->stats.ppp_opackets;
+		ppp->stats.ppp_ooctects += count;
+
+		/*
+		 * Do address/control compression
+		 */
+		if ((ppp->flags & SC_COMP_AC) != 0 && !islcp
+		    && PPP_ADDRESS(data) == PPP_ALLSTATIONS
+		    && PPP_CONTROL(data) == PPP_UI)
+			i += 2;
+	}
+
+	/*
+	 * Once we put in the last byte, we need to put in the FCS
+	 * and closing flag, so make sure there is at least 7 bytes
+	 * of free space in the output buffer.
+	 */
+	buflim = buf + OBUFSIZE - 6;
+	while (i < count && buf < buflim) {
+		c = data[i++];
+		if (i == 3 && c == 0 && (ppp->flags & SC_COMP_PROT))
+			continue;	/* compress protocol field */
+		fcs = PPP_FCS(fcs, c);
+		if (in_xmap(ppp, c) || (islcp && c < 0x20)) {
+			*buf++ = PPP_ESCAPE;
+			c ^= 0x20;
 		}
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_NOTICE
-				"ppp: error in VJ memorizing\n");
+		*buf++ = c;
+	}
+
+	if (i == count) {
+		/*
+		 * We have finished the packet.  Add the FCS and flag.
+		 */
+		fcs = ~fcs;
+		c = fcs & 0xff;
+		if (in_xmap(ppp, c) || (islcp && c < 0x20)) {
+			*buf++ = PPP_ESCAPE;
+			c ^= 0x20;
+		}
+		*buf++ = c;
+		c = (fcs >> 8) & 0xff;
+		if (in_xmap(ppp, c) || (islcp && c < 0x20)) {
+			*buf++ = PPP_ESCAPE;
+			c ^= 0x20;
+		}
+		*buf++ = c;
+		*buf++ = PPP_FLAG;
+		ppp->olim = buf;
+
+		kfree_skb(ppp->tpkt);
+		ppp->tpkt = 0;
+		return 1;
 	}
+
+	/*
+	 * Remember where we are up to in this packet.
+	 */
+	ppp->olim = buf;
+	ppp->tpkt_pos = i;
+	ppp->tfcs = fcs;
 	return 0;
 }
 
 /*
- * Receive all unclassified protocols.
+ * 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
-rcv_proto_unknown (struct ppp *ppp, __u16 proto,
-		   __u8 *data, int len)
+ppp_tty_room (struct tty_struct *tty)
 {
-	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;				 \
+	return 65536;	    /* We can handle an infinite amount of data. :-) */
 }
 
-	CHECK_PPP(0);
-/*
- * The total length includes the protocol data.
- * Lock the user information buffer.
- */
-	if (test_and_set_bit (0, &ppp->ubuf->locked)) {
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_DEBUG
-				"ppp: rcv_proto_unknown: can't get lock\n");
-	} else {
-		CHECK_BUF_MAGIC(ppp->ubuf);
-		current_idx = ppp->ubuf->head;
 /*
- * Insert the buffer length (not counted), the protocol, and the data
+ * Callback function when data is available at the tty driver.
  */
-		totlen = len + 2;
-		PUTC (totlen >> 8);
-		PUTC (totlen);
+static void
+ppp_tty_receive (struct tty_struct *tty, const __u8 * data,
+		 char *flags, int count)
+{
+	register struct ppp *ppp = tty2ppp (tty);
+	struct sk_buff *skb;
+	int chr, flg;
+	unsigned char *p;
 
-		PUTC (proto >> 8);
-		PUTC (proto);
+	if (ppp != 0)
+		CHECK_PPP_VOID();
+	/*
+	 * This can happen if stuff comes in on the backup tty.
+	 */
+	if (ppp == 0 || tty != ppp->tty)
+		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: tty_receive called but couldn't find "
+			       "PPP struct.\n");
+		return;
+	}
+	/*
+	 * Print the buffer if desired
+	 */
+	if (ppp->flags & SC_LOG_RAWIN)
+		ppp_print_buffer ("receive buffer", data, count);
 
-		totlen -= 2;
-		while (totlen-- > 0) {
-			PUTC (*data++);
+	ppp->stats.ppp_ibytes += count;
+	skb = ppp->rpkt;
+	while (count-- > 0) {
+		/*
+		 * Collect the character and error condition for the character.
+		 * Set the toss flag for the first character error.
+		 */
+		chr = *data++;
+		if (flags) {
+			flg = *flags++;
+			if (flg) {
+				if (ppp->toss == 0)
+					ppp->toss = flg;
+				switch (flg) {
+				case TTY_OVERRUN:
+					++ppp->estats.rx_fifo_errors;
+					break;
+				case TTY_FRAME:
+				case TTY_BREAK:
+					++ppp->estats.rx_frame_errors;
+					break;
+				}
+				continue;
+			}
 		}
-#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);
+		/*
+		 * Set 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 indicates an error condition.
+		 */
 
-		return 1;
-/*
- * The buffer is full. Unlock the header
- */
-failure:
-		clear_bit (0, &ppp->ubuf->locked);
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_DEBUG
-				"ppp: rcv_proto_unknown: buffer overflow\n");
-	}
-/*
- * Discard the frame. There are no takers for this protocol.
- */
-	if (ppp->flags & SC_DEBUG)
-		printk (KERN_DEBUG
-			"ppp: rcv_proto_unknown: dropping packet\n");
-	return 0;
-}
+#ifdef CHECK_CHARACTERS
+		if (chr & 0x80)
+			ppp->flags |= SC_RCV_B7_1;
+		else
+			ppp->flags |= SC_RCV_B7_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.
- */
+		if (paritytab[chr >> 5] & (1 << (chr & 0x1F)))
+			ppp->flags |= SC_RCV_ODDP;
+		else
+			ppp->flags |= SC_RCV_EVNP;
+#endif
 
-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;
-	unsigned long flags;
+		if (chr == PPP_FLAG) {
+			/*
+			 * FLAG. This is the end of the block. If the block
+			 * ends with ESC FLAG, then the block is to be ignored.
+			 */
+			if (ppp->escape)
+				ppp->toss |= 0x80;
+			/*
+			 * Process the frame if it was received correctly.
+			 * If there was an error, let the VJ decompressor know.
+			 * There are 4 cases here:
+			 * skb != NULL, toss != 0: error in frame
+			 * skb != NULL, toss == 0: frame ok
+			 * skb == NULL, toss != 0: very first frame,
+			 *	error on 1st char, or alloc_skb failed
+			 * skb == NULL, toss == 0: empty frame (~~)
+			 */
+			if (ppp->toss || !ppp_receive_frame(ppp, skb)) {
+				if (ppp->toss && (ppp->flags & SC_DEBUG))
+					printk(KERN_DEBUG
+					       "ppp: tossing frame (%x)\n",
+					       ppp->toss);
+				if (skb != NULL)
+					kfree_skb(skb);
+				if (!(ppp->toss == 0xE0 || ppp->toss == 0x80))
+					++ppp->stats.ppp_ierrors;
+				ppp_receive_error(ppp);
+			}
+			/*
+			 * Reset for the next frame.
+			 */
+			skb = NULL;
+			ppp->rfcs = PPP_INITFCS;
+			ppp->escape = 0;
+			ppp->toss = 0;
+			continue;
+		}
 
-	if (slen > len)
-		return;
+		/* If we're tossing, look no further. */
+		if (ppp->toss != 0)
+			continue;
 
-	save_flags(flags);
-	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) {
-			cli();
-			ppp->flags &= ~(SC_CCP_UP   |
-					SC_COMP_RUN |
-					SC_DECOMP_RUN);
-		}
-		break;
+		/* If this is a control char to be ignored, do so */
+		if (in_rmap(ppp, chr))
+			continue;
 
-	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;
+		/*
+		 * Modify the next character if preceded by escape.
+		 * The escape character (0x7d) could be an escaped
+		 * 0x5d, if it follows an escape :-)
+		 */
+		if (ppp->escape) {
+			chr ^= PPP_TRANS;
+			ppp->escape = 0;
+		} else if (chr == PPP_ESCAPE) {
+			ppp->escape = PPP_TRANS;
+			continue;
+		}
 
-			if ((*ppp->sc_xcomp->comp_init)
-			    (ppp->sc_xc_state,
-			     opt,
-			     opt_len,
-			     ppp2dev (ppp)->base_addr,
-			     0,
-			     ppp->flags & SC_DEBUG)) {
+		/*
+		 * Allocate an skbuff on the first character received.
+		 * The 128 is room for VJ header expansion and FCS.
+		 */
+		if (skb == NULL) {
+			skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN);
+			if (skb == NULL) {
 				if (ppp->flags & SC_DEBUG)
-					printk(KERN_DEBUG "%s: comp running\n",
-					       ppp->name);
-				cli();
-				ppp->flags |= SC_COMP_RUN;
+					printk(KERN_DEBUG "couldn't "
+					       "alloc skb for recv\n");
+				ppp->toss = 1;
+				continue;
 			}
-			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 & SC_DEBUG)) {
-			if (ppp->flags & SC_DEBUG)
-				printk(KERN_DEBUG "%s: decomp running\n",
-				       ppp->name);
-			cli();
-			ppp->flags |= SC_DECOMP_RUN;
-			ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
+		/*
+		 * Decompress A/C and protocol compression here.
+		 */
+		if (skb->len == 0 && chr != PPP_ALLSTATIONS) {
+			p = skb_put(skb, 2);
+			p[0] = PPP_ALLSTATIONS;
+			p[1] = PPP_UI;
+		}
+		if (skb->len == 2 && (chr & 1) != 0) {
+			p = skb_put(skb, 1);
+			p[0] = 0;
 		}
-		break;
-/*
- * CCP Reset-ack resets compressors and decompressors as it passes through.
- */
-	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);
-				if (ppp->flags & SC_DEBUG)
-					printk(KERN_DEBUG "%s: comp reset\n",
-					       ppp->name);
-			}
-		} else {
-			if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) {
-			      (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state);
-			      if (ppp->flags & SC_DEBUG)
-					printk(KERN_DEBUG "%s: decomp reset\n",
-					       ppp->name);
-			      cli();
-			      ppp->flags &= ~SC_DC_ERROR;
-			}
+		/*
+		 * Check if we've overflowed the MRU
+		 */
+		if (skb->len >= ppp->mru + PPP_HDRLEN + 2
+		    || skb_tailroom(skb) <= 0) {
+			++ppp->estats.rx_length_errors;
+			ppp->toss = 0xC0;
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_DEBUG "rcv frame too long: "
+				       "len=%d mru=%d hroom=%d troom=%d\n",
+				       skb->len, ppp->mru, skb_headroom(skb),
+				       skb_tailroom(skb));
+			continue;
 		}
-		break;
+
+		/*
+		 * Store the character and update the FCS.
+		 */
+		p = skb_put(skb, 1);
+		*p = chr;
+		ppp->rfcs = PPP_FCS(ppp->rfcs, chr);
 	}
-	if (ppp->flags & SC_DEBUG)
-		printk(KERN_DEBUG "ppp_proto_ccp: %s code %d, flags=%x\n",
-		       (rcvd? "rcvd": "sent"), CCP_CODE(dp), ppp->flags);
-	restore_flags(flags);
+	ppp->rpkt = skb;
 }
 
-static int
-rcv_proto_ccp (struct ppp *ppp, __u16 proto, __u8 *dp, int len)
-{
-	CHECK_PPP(0);
-	ppp_proto_ccp (ppp, dp, len, 1);
-	return rcv_proto_unknown (ppp, proto, dp, len);
-}
+/*************************************************************
+ * PPP NETWORK INTERFACE SUPPORT
+ *	The following code implements the PPP network
+ *	interface device and handles those parts of
+ *	the PPP processing which are independent of the
+ *	type of hardware link being used, including
+ *	VJ and packet compression.
+ *************************************************************/
 
 /*
- * Handle a LQR packet.
+ * Network device driver callback routines
  */
 
-static int
-rcv_proto_lqr (struct ppp *ppp, __u16 proto, __u8 * data, int len)
-{
-	return rcv_proto_unknown (ppp, proto, data, len);
-}
+static int ppp_init_dev(struct device *dev);
+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(struct sk_buff *, struct device *);
+static struct net_device_stats *ppp_dev_stats (struct device *);
 
-/*************************************************************
- * 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.
- *************************************************************/
+/*
+ * Information for the protocol decoder
+ */
 
-/* read a PPP frame from the us_rbuff circular buffer,
-   waiting if necessary
-*/
+typedef int (*pfn_proto)  (struct ppp *, struct sk_buff *);
 
-static ssize_t
-ppp_tty_read (struct tty_struct *tty, struct file *file, __u8 * buf, size_t nr)
-{
-	struct ppp *ppp = tty2ppp (tty);
-	__u8 c;
-	ssize_t len, ret;
+typedef struct ppp_proto_struct {
+	int		proto;
+	pfn_proto	func;
+} ppp_proto_type;
 
-#define GETC(c)						\
-{							\
-	c = buf_base (ppp->ubuf) [ppp->ubuf->tail++];	\
-	ppp->ubuf->tail &= ppp->ubuf->size;		\
-}
+static int rcv_proto_ip		(struct ppp *, struct sk_buff *);
+static int rcv_proto_ipx	(struct ppp *, struct sk_buff *);
+static int rcv_proto_at		(struct ppp *, struct sk_buff *);
+static int rcv_proto_vjc_comp	(struct ppp *, struct sk_buff *);
+static int rcv_proto_vjc_uncomp (struct ppp *, struct sk_buff *);
+static int rcv_proto_ccp	(struct ppp *, struct sk_buff *);
+static int rcv_proto_unknown	(struct ppp *, struct sk_buff *);
 
-/*
- * Validate the pointers
- */
-	if (!ppp)
-		return -EIO;
-	CHECK_PPP (-ENXIO);
+static
+ppp_proto_type proto_list[] = {
+	{ PPP_IP,	  rcv_proto_ip	       },
+	{ PPP_IPX,	  rcv_proto_ipx	       },
+	{ PPP_AT,	  rcv_proto_at	       },
+	{ PPP_VJC_COMP,	  rcv_proto_vjc_comp   },
+	{ PPP_VJC_UNCOMP, rcv_proto_vjc_uncomp },
+	{ PPP_CCP,	  rcv_proto_ccp	       },
+	{ 0,		  rcv_proto_unknown    }  /* !!! MUST BE LAST !!! */
+};
 
 /*
- * Before we attempt to write the frame to the user, ensure that the
- * user has access to the pages for the total buffer length.
+ * Called when the PPP network interface device is actually created.
  */
-	if (verify_area (VERIFY_WRITE, buf, nr))
-		return -EFAULT;
+static int
+ppp_init_dev (struct device *dev)
+{
+	dev->hard_header_len  = PPP_HDRLEN;
 
-/*
- * Increment the module use count so that the module can't get unloaded
- * while we're sleeping below.  The problem is that ppp_tty_close() can
- * get called (as a result of a hangup from the tty) while we're sleeping.
- */
-	MOD_INC_USE_COUNT;
+	/* 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;
 
-/*
- * Acquire the read lock.
- */
-	for (;;) {
-		ret = 0;
-		ppp = tty2ppp (tty);
-		if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse
-		    || tty != ppp->tty)
-			goto done;
+	dev_init_buffers(dev);
 
-		if (test_and_set_bit (0, &ppp->ubuf->locked) != 0) {
-			current->timeout = 0;
-			current->state	 = TASK_INTERRUPTIBLE;
-			schedule();
-
-			ret = -EINTR;
-			if (signal_pending(current))
-				goto done;
-			continue;
-		}
+	dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
 
-/*
- * 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 (len)
-				break;
-		}
+	return 0;
+}
 
 /*
- * If there is no length then wait for the data to arrive.
+ * Callback from the network layer when the device goes up.
  */
-		/* no data */
-		clear_bit (0, &ppp->ubuf->locked);
-		ret = -EAGAIN;
-		if (file->f_flags & O_NONBLOCK)
-			goto done;
-		current->timeout = 0;
-		interruptible_sleep_on (&ppp->read_wait);
-		ret = -EINTR;
-		if (signal_pending(current))
-			goto done;
-	}
 
-/*
- * 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 */
+static int
+ppp_dev_open (struct device *dev)
+{
+	struct ppp *ppp = dev2ppp(dev);
 
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_DEBUG
-				"ppp: read of %lu bytes too small for %ld "
-				"frame\n", (unsigned long) nr, (long) len + 2);
-		ppp->stats.ppp_ierrors++;
-		ret = -EOVERFLOW;
-		goto out;
+	if (!ppp->inuse || ppp2tty(ppp) == NULL) {
+		printk(KERN_ERR "ppp: %s not active\n", dev->name);
+		return -ENXIO;
 	}
 
-/*
- * Fake the insertion of the ADDRESS and CONTROL information because these
- * were not saved in the buffer.
- */
-	ret = -EFAULT;
-	if (put_user((u_char) PPP_ALLSTATIONS, buf)
-	    || put_user((u_char) PPP_UI, buf+1))
-		goto out;
-	buf += 2;
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
 
 /*
- * Copy the received data from the buffer to the caller's area.
+ * Callback from the network layer when the ppp device goes down.
  */
-	nr = len + 2; 	/* Account for ADDRESS and CONTROL bytes */
-	while (len-- > 0) {
-		GETC (c);
-		if (put_user(c, buf))
-			goto out;
-		++buf;
-	}
-	ret = nr;
 
-out:
-	if (len > 0)
-		ppp->ubuf->tail = (ppp->ubuf->tail + len) & ppp->ubuf->size;
-	clear_bit(0, &ppp->ubuf->locked);
+static int
+ppp_dev_close (struct device *dev)
+{
+	struct ppp *ppp = dev2ppp (dev);
+
+	CHECK_PPP_MAGIC(ppp);
 
-done:
 	MOD_DEC_USE_COUNT;
-	return ret;
-#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 */
+	return 0;
+}
 
-extern inline void
-ppp_stuff_char (struct ppp *ppp, register struct ppp_buffer *buf,
-		register __u8 chr)
+static inline void
+get_vj_stats(struct vjstat *vj, struct slcompress *slc)
 {
-/*
- * The buffer should not be full.
- */
-	if (ppp->flags & SC_DEBUG) {
-		if ((buf->count < 0) || (buf->count > 3000))
-			printk (KERN_DEBUG "ppp_stuff_char: %d %x\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);
+	vj->vjs_packets    = slc->sls_o_compressed + slc->sls_o_uncompressed;
+	vj->vjs_compressed = slc->sls_o_compressed;
+	vj->vjs_searches   = slc->sls_o_searches;
+	vj->vjs_misses     = slc->sls_o_misses;
+	vj->vjs_errorin    = slc->sls_i_error;
+	vj->vjs_tossed     = slc->sls_i_tossed;
+	vj->vjs_uncompressedin = slc->sls_i_uncompressed;
+	vj->vjs_compressedin   = slc->sls_i_compressed;
 }
 
 /*
- * Procedure to encode the data with the proper escaping and send the
- * data to the remote system.
+ * Callback from the network layer to process the sockioctl functions.
  */
-
-static void
-ppp_dev_xmit_lower (struct ppp *ppp, struct ppp_buffer *buf,
-		    __u8 *data, int count, int non_ip)
+static int
+ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd)
 {
-	__u16   write_fcs;
-	int	address, control;
-	int	proto;
+	struct ppp *ppp = dev2ppp(dev);
+	int nb;
+	union {
+		struct ppp_stats stats;
+		struct ppp_comp_stats cstats;
+		char vers[32];
+	} u;
 
-	CHECK_PPP_VOID();
-	CHECK_BUF_MAGIC(buf);
-	++ppp->stats.ppp_opackets;
-	ppp->stats.ppp_ooctects += count;
+	CHECK_PPP_MAGIC(ppp);
 
-/*
- * Insert the leading FLAG character
- */
-	buf->count = 0;
+	memset(&u, 0, sizeof(u));
+	switch (cmd) {
+	case SIOCGPPPSTATS:
+		u.stats.p = ppp->stats;
+		if (ppp->slcomp != NULL)
+			get_vj_stats(&u.stats.vj, ppp->slcomp);
+		nb = sizeof(u.stats);
+		break;
 
-	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);
+	case SIOCGPPPCSTATS:
+		if (ppp->sc_xc_state != NULL)
+			(*ppp->sc_xcomp->comp_stat)
+				(ppp->sc_xc_state, &u.cstats.c);
+		if (ppp->sc_rc_state != NULL)
+			(*ppp->sc_rcomp->decomp_stat)
+				(ppp->sc_rc_state, &u.cstats.d);
+		nb = sizeof(u.cstats);
+		break;
 
-	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);
+	case SIOCGPPPVER:
+		strcpy(u.vers, szVersion);
+		nb = strlen(u.vers) + 1;
+		break;
 
-	ppp_stuff_char (ppp, buf, proto);
-/*
- * Insert the data
- */
-	data  += 4;
-	count -= 4;
+	default:
+		return -EINVAL;
+	}
 
-	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);
-/*
- * Add the trailing flag character
- */
-	ins_char (buf, PPP_FLAG);
-/*
- * Send the block to the tty driver.
- */
-	ppp->stats.ppp_obytes += buf->count;
-	ppp_kick_tty (ppp, buf);
+	if (copy_to_user((void *) ifr->ifr_ifru.ifru_data, &u, nb))
+		return -EFAULT;
+	return 0;
 }
 
 /*
- * Compress and send an frame to the peer.
- *
- * Return 0 if frame was queued for transmission.
- *	  1 if frame must be re-queued for later driver support.
+ * Process the generic PPP ioctls, i.e. those which are not specific
+ * to any particular type of hardware link.
  */
-
 static int
-ppp_dev_xmit_frame (struct ppp *ppp, struct ppp_buffer *buf,
-		    __u8 *data, int count)
+ppp_ioctl(struct ppp *ppp, unsigned int param2, unsigned long param3)
 {
-	int	proto;
-	int	address, control;
-	__u8 *new_data;
-	int	new_count;
+	register int temp_i = 0, oldflags;
+	int error = -EFAULT;
+	unsigned long flags;
+	struct ppp_idle cur_ddinfo;
+	struct npioctl npi;
 
-	CHECK_PPP(0);
-	CHECK_BUF_MAGIC(buf);
-/*
- * 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);
+	CHECK_PPP(-ENXIO);
 
-	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 (ppp->mtu + PPP_HDRLEN, GFP_ATOMIC);
-		if (new_data == NULL) {
-			printk (KERN_ERR "ppp_dev_xmit_frame: no memory\n");
-			return 1;
-		}
+	/*
+	 * The user must have an euid of root to do these requests.
+	 */
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
 
-		new_count = (*ppp->sc_xcomp->compress)
-		    (ppp->sc_xc_state, data, new_data, count,
-		     ppp->mtu + PPP_HDRLEN);
+	switch (param2) {
+	case PPPIOCSMRU:
+		/*
+		 * Set the MRU value
+		 */
+		if (get_user(temp_i, (int *) param3))
+			break;
+		if (temp_i < PPP_MRU)
+			temp_i = PPP_MRU;
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_INFO
+			       "ppp_ioctl: set mru to %x\n", temp_i);
+		error = 0;
+		break;
 
-		if (new_count > 0 && (ppp->flags & SC_CCP_UP)) {
-			ppp_dev_xmit_lower (ppp, buf, new_data, new_count, 0);
-			kfree (new_data);
-			return 0;
-		}
-/*
- * The frame could not be compressed, or it could not be sent in
- * compressed form because CCP is not yet up.
- */
-		kfree (new_data);
-	}
-/*
- * Go to the escape encoding
- */
-	ppp_dev_xmit_lower (ppp, buf, data, count, !!(proto & 0xFF00));
-	return 0;
-}
+	case PPPIOCGFLAGS:
+		/*
+		 * Fetch the current flags
+		 */
+		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
+		if (put_user(temp_i, (int *) param3))
+			break;
+		error = 0;
+		break;
 
-/*
- * Revise the tty frame for specific protocols.
- */
+	case PPPIOCSFLAGS:
+		/*
+		 * Set the flags for the various options
+		 */
+		if (get_user(temp_i, (int *) param3))
+			break;
 
-static int
-send_revise_frame (register struct ppp *ppp, __u8 *data, int len)
-{
-	__u8 *p;
+		if (ppp->flags & ~temp_i & SC_CCP_OPEN)
+			ppp_ccp_closed(ppp);
 
-	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);
+		save_flags(flags);
+		cli();
+		oldflags = ppp->flags;
+		temp_i = (temp_i & SC_MASK) | (oldflags & ~SC_MASK);
+		ppp->flags = temp_i;
+		restore_flags(flags);
+
+		if ((oldflags | temp_i) & SC_DEBUG)
+			printk(KERN_INFO
+			       "ppp_ioctl: set flags to %x\n", temp_i);
+		error = 0;
 		break;
-/*
- * Outbound compression frames
- */
-	case PPP_CCP:
-		ppp_proto_ccp (ppp,
-			       data + PPP_HDRLEN,
-			       len  - PPP_HDRLEN,
-			       0);
+
+	case PPPIOCSCOMPRESS:
+		/*
+		 * Set the compression mode
+		 */
+		error = ppp_set_compression
+			(ppp, (struct ppp_option_data *) param3);
 		break;
 
-	default:
+	case PPPIOCGUNIT:
+		/*
+		 * Obtain the unit number for this device.
+		 */
+		if (put_user(ppp->line, (int *) param3))
+			break;
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_INFO
+			       "ppp_ioctl: get unit: %d\n", ppp->line);
+		error = 0;
 		break;
-	}
 
-	return len;
-}
+	case PPPIOCSDEBUG:
+		/*
+		 * Set the debug level
+		 */
+		if (get_user(temp_i, (int *) param3))
+			break;
+		temp_i = (temp_i & 0x1F) << 16;
 
-/*
- * write a frame with NR chars from BUF to TTY
- * we have to put the FCS field on ourselves
- */
+		if ((ppp->flags | temp_i) & SC_DEBUG)
+			printk(KERN_INFO
+			       "ppp_ioctl: set dbg flags to %x\n", temp_i);
 
-static ssize_t
-ppp_tty_write (struct tty_struct *tty, struct file *file, const __u8 * data,
-	       size_t count)
-{
-	struct ppp *ppp = tty2ppp (tty);
-	__u8 *new_data;
-	int error;
-	struct wait_queue wait = {current, NULL};
+		save_flags(flags);
+		cli();
+		ppp->flags = (ppp->flags & ~0x1F0000) | temp_i;
+		restore_flags(flags);
+		error = 0;
+		break;
 
-/*
- * Verify the pointers.
- */
-	error = -EIO;
-	if (!ppp)
-		goto out;
-	if (ppp->magic != PPP_MAGIC)
-		goto out;
+	case PPPIOCGDEBUG:
+		/*
+		 * Get the debug level
+		 */
+		temp_i = (ppp->flags >> 16) & 0x1F;
+		if (put_user(temp_i, (int *) param3))
+			break;
+		error = 0;
+		break;
 
-	CHECK_PPP (-ENXIO);
-/*
- * Ensure that the caller does not wish to send too much.
- */
-	if (count > PPP_MTU + PPP_HDRLEN) {
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_WARNING
-				"ppp_tty_write: truncating user packet "
-				"from %lu to mtu %d\n", (unsigned long) count,
-				PPP_MTU + PPP_HDRLEN);
-		count = PPP_MTU + PPP_HDRLEN;
-	}
-/*
- * Allocate a buffer for the data and fetch it from the user space.
- */
-	new_data = kmalloc (count, GFP_KERNEL);
-	if (new_data == NULL) {
-		printk (KERN_ERR "ppp_tty_write: no memory\n");
-		return 0;
-	}
-/*
- * Retrieve the user's buffer
- */
-	error = -EFAULT;
-	if (copy_from_user(new_data, data, count))
-		goto out_free;
+	case PPPIOCGIDLE:
+		/*
+		 * Get the times since the last send/receive frame operation
+		 */
+		/* change absolute times to relative times. */
+		cur_ddinfo.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
+		cur_ddinfo.recv_idle = (jiffies - ppp->last_recv) / HZ;
+		if (copy_to_user((void *) param3, &cur_ddinfo,
+				 sizeof (cur_ddinfo)))
+			break;
+		error = 0;
+		break;
 
-/*
- * Increment the module use count so that the module can't get unloaded
- * while we're sleeping below.  The problem is that ppp_tty_close() can
- * get called (as a result of a hangup from the tty) while we're sleeping.
- */
-	MOD_INC_USE_COUNT;
+	case PPPIOCSMAXCID:
+		/*
+		 * Set the maximum VJ header compression slot number.
+		 */
+		if (get_user(temp_i, (int *) param3))
+			break;
+		error = -EINVAL;
+		if (temp_i < 2 || temp_i > 255)
+			break;
+		++temp_i;
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_INFO "ppp_ioctl: set maxcid to %d\n",
+			       temp_i);
+		if (ppp->slcomp != NULL)
+			slhc_free(ppp->slcomp);
+		ppp->slcomp = slhc_init(16, temp_i);
 
-/*
- * Lock this PPP unit so we will be the only writer,
- * sleeping if necessary.
- *
- * Note that we add our task to the wait queue before
- * attempting to lock, as the lock flag may be cleared
- * from an interrupt.
- */
-	add_wait_queue(&ppp->write_wait, &wait);
-	while (1) {
+		error = -ENOMEM;
+		if (ppp->slcomp == NULL) {
+			printk(KERN_ERR "ppp: no memory for VJ compression\n");
+			break;
+		}
 		error = 0;
-		current->timeout = 0;
-		current->state = TASK_INTERRUPTIBLE;
-		if (lock_buffer(ppp->tbuf) == 0)
+		break;
+
+	case PPPIOCGNPMODE:
+	case PPPIOCSNPMODE:
+		if (copy_from_user(&npi, (void *) param3, sizeof(npi)))
 			break;
-		schedule();
 
-		error = -EINVAL;
-		ppp = tty2ppp (tty);
-		if (!ppp || ppp->magic != PPP_MAGIC || 
-		    !ppp->inuse || tty != ppp->tty) {
-			printk("ppp_tty_write: %p invalid after wait!\n", ppp);
+		switch (npi.protocol) {
+		case PPP_IP:
+			npi.protocol = NP_IP;
+			break;
+		case PPP_IPX:
+			npi.protocol = NP_IPX;
 			break;
+		case PPP_AT:
+			npi.protocol = NP_AT;
+			break;
+		default:
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_DEBUG "pppioc[gs]npmode: "
+				       "invalid proto %d\n", npi.protocol);
+			error = -EINVAL;
+			goto out;
+		}
+
+		if (param2 == PPPIOCGNPMODE) {
+			npi.mode = ppp->sc_npmode[npi.protocol];
+			if (copy_to_user((void *) param3, &npi, sizeof(npi)))
+				break;
+		} else {
+			ppp->sc_npmode[npi.protocol] = npi.mode;
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_DEBUG "ppp: set np %d to %d\n",
+				       npi.protocol, npi.mode);
+			mark_bh(NET_BH);
 		}
-		error = -EINTR;
-		if (signal_pending(current))
-			break;
-	}
-	current->state = TASK_RUNNING;
-	remove_wait_queue(&ppp->write_wait, &wait);
-	if (error)
-		goto out_free_dec;
+		error = 0;
+		break;
 
-/*
- * Change the LQR frame
- */
-	count = send_revise_frame (ppp, new_data, count);
-/*
- * Send the data
- */
-	if (PPP_PROTOCOL(new_data) == PPP_IP) {
+	default:
 		/*
-		 * IP frames can be sent by pppd when we're doing
-		 * demand-dialling.  We send them via ppp_dev_xmit_ip
-		 * to make sure that VJ compression happens properly.
+		 *  All other ioctl() events will come here.
 		 */
-		ppp_dev_xmit_ip(ppp, ppp->tbuf, new_data + PPP_HDRLEN,
-				count - PPP_HDRLEN, NPMODE_PASS);
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_ERR
+			       "ppp_ioctl: invalid ioctl: %x, addr %lx\n",
+			       param2, param3);
 
-	} else {
-		ppp_dev_xmit_frame (ppp, ppp->tbuf, new_data, count);
+		error = -ENOIOCTLCMD;
+		break;
 	}
-	error = count;
-
-out_free_dec:
-	MOD_DEC_USE_COUNT;
-out_free:
-	kfree (new_data);
 out:
 	return error;
 }
@@ -2166,7 +1572,6 @@
 /*
  * Process the set-compression ioctl.
  */
-
 static int
 ppp_set_compression (struct ppp *ppp, struct ppp_option_data *odp)
 {
@@ -2177,16 +1582,16 @@
 	__u8 ccp_option[CCP_MAX_OPTION_LENGTH];
 	struct ppp_option_data data;
 
-/*
- * Fetch the compression parameters
- */
+	/*
+	 * Fetch the compression parameters
+	 */
 	error = -EFAULT;
 	if (copy_from_user(&data, odp, sizeof (data)))
 		goto out;
 
 	nb  = data.length;
 	ptr = data.ptr;
-	if ((__u32) nb >= (__u32)CCP_MAX_OPTION_LENGTH)
+	if ((unsigned) nb >= CCP_MAX_OPTION_LENGTH)
 		nb = CCP_MAX_OPTION_LENGTH;
 
 	if (copy_from_user(ccp_option, ptr, nb))
@@ -2211,8 +1616,15 @@
 	}
 #endif /* CONFIG_KMOD */
 
-	if (cp == NULL)
-		goto out_no_comp;
+	if (cp == NULL) {
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_DEBUG
+			       "%s: no compressor for [%x %x %x], %x\n",
+			       ppp->name, ccp_option[0], ccp_option[1],
+			       ccp_option[2], nb);
+		goto out;		/* compressor not loaded */
+	}
+
 	/*
 	 * Found a handler for the protocol - try to allocate
 	 * a compressor or decompressor.
@@ -2226,12 +1638,11 @@
 		ppp->sc_xcomp	 = cp;
 		ppp->sc_xc_state = cp->comp_alloc(ccp_option, nb);
 		if (ppp->sc_xc_state == NULL) {
-			printk(KERN_WARNING "%s: comp_alloc failed\n",
-				ppp->name);
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_DEBUG "%s: comp_alloc failed\n",
+				       ppp->name);
 			error = -ENOBUFS;
-		} else if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG "%s: comp_alloc -> %p\n",
-			       ppp->name, ppp->sc_xc_state);
+		}
 	} else {
 		if (ppp->sc_rc_state != NULL)
 			(*ppp->sc_rcomp->decomp_free)(ppp->sc_rc_state);
@@ -2240,761 +1651,817 @@
 		ppp->sc_rcomp	 = cp;
 		ppp->sc_rc_state = cp->decomp_alloc(ccp_option, nb);
 		if (ppp->sc_rc_state == NULL) {
-			printk(KERN_WARNING "%s: decomp_alloc failed\n",
-				ppp->name);
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_DEBUG "%s: decomp_alloc failed\n",
+				       ppp->name);
 			error = -ENOBUFS;
-		} else if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG "%s: decomp_alloc -> %p\n",
-			       ppp->name, ppp->sc_rc_state);
+		}
 	}
 out:
 	return error;
-
-out_no_comp:
-	error = -EINVAL;	/* no handler found */
-	if (ppp->flags & SC_DEBUG)
-		printk(KERN_DEBUG "%s: no compressor for [%x %x %x], %x\n",
-			ppp->name, ccp_option[0], ccp_option[1],
-			ccp_option[2], nb);
-	goto out;
 }
 
 /*
- * Process the IOCTL event for the tty device.
+ * 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 int
-ppp_tty_ioctl (struct tty_struct *tty, struct file * file,
-               unsigned int param2, unsigned long param3)
+static void ppp_proto_ccp(struct ppp *ppp, __u8 *dp, int len, int rcvd)
 {
-	struct ppp *ppp = tty2ppp (tty);
-	register int temp_i = 0, oldflags;
-	int error = 0;
+	int slen    = CCP_LENGTH(dp);
+	__u8 *opt = dp	 + CCP_HDRLEN;
+	int opt_len = slen - CCP_HDRLEN;
 	unsigned long flags;
-/*
- * 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 (!capable(CAP_NET_ADMIN))
-		return -EPERM;
-/*
- * Set the MRU value
- */
-	switch (param2) {
-	case PPPIOCSMRU:
-		error = get_user(temp_i, (int *) param3);
-		if (error != 0)
-			break;
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_INFO
-				"ppp_tty_ioctl: set mru to %x\n", temp_i);
+	if (slen > len)
+		return;
 
-		if (ppp->mru != temp_i)
-			ppp_changedmtu (ppp, ppp2dev (ppp)->mtu, temp_i);
-		break;
-/*
- * Fetch the flags
- */
-	case PPPIOCGFLAGS:
-		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
-		error = put_user(temp_i, (int *) param3);
+	if (ppp->flags & SC_DEBUG)
+		printk(KERN_DEBUG "ppp_proto_ccp rcvd=%d code=%x flags=%x\n",
+		       rcvd, CCP_CODE(dp), ppp->flags);
+	save_flags(flags);
+	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) {
+			cli();
+			ppp->flags &= ~(SC_CCP_UP   |
+					SC_COMP_RUN |
+					SC_DECOMP_RUN);
+		}
 		break;
-/*
- * Set the flags for the various options
- */
-	case PPPIOCSFLAGS:
-		error = get_user(temp_i, (int *) param3);
-		if (error != 0)
-			break;
-		temp_i &= SC_MASK;
 
-		if ((ppp->flags & SC_CCP_OPEN) && (temp_i & SC_CCP_OPEN) == 0)
-			ppp_ccp_closed(ppp);
-
-		save_flags(flags);
-		cli();
-		oldflags = ppp->flags;
-		ppp->flags = temp_i |= (ppp->flags & ~SC_MASK);
-		restore_flags(flags);
-		if ((oldflags | temp_i) & SC_DEBUG)
-			printk (KERN_INFO
-				"ppp_tty_ioctl: set flags to %x\n", 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 = put_user(ppp->xmit_async_map[0], (int *) param3);
-		break;
-/*
- * Set the transmit async map
- */
-	case PPPIOCSASYNCMAP:
-		error = get_user(temp_i, (int *) param3);
-		if (error != 0)
+	case CCP_CONFACK:
+		if ((ppp->flags & SC_CCP_OPEN) == 0)
 			break;
-		ppp->xmit_async_map[0] = temp_i;
-		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 = get_user(temp_i, (int *) param3);
-		if (error != 0)
+		if (ppp->flags & SC_CCP_UP)
 			break;
-		ppp->recv_async_map = temp_i;
-		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 = put_user(ppp2dev (ppp)->base_addr, (int *) param3);
-		if (error != 0)
+		if (slen < (CCP_HDRLEN + CCP_OPT_MINLEN))
 			break;
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_INFO
-				"ppp_tty_ioctl: get unit: %ld\n",
-				ppp2dev (ppp)->base_addr);
-		break;
-/*
- * Set the debug level
- */
-	case PPPIOCSDEBUG:
-		error = get_user(temp_i, (int *) param3);
-		if (error != 0)
+		if (slen < (CCP_OPT_LENGTH (opt) + CCP_HDRLEN))
 			break;
-		temp_i	= (temp_i & 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:
-		temp_i = (ppp->flags >> 16) & 0x1F;
-		error = put_user(temp_i, (int *) param3);
-		break;
-/*
- * Get the times since the last send/receive frame operation
- */
-	case PPPIOCGIDLE:
-		{
-			struct ppp_idle cur_ddinfo;
-
-			/* change absolute times to relative times. */
-			cur_ddinfo.xmit_idle = (jiffies - ppp->last_xmit) / HZ;
-			cur_ddinfo.recv_idle = (jiffies - ppp->last_recv) / HZ;
-			error = -EFAULT;
-			if (!copy_to_user((void *) param3, &cur_ddinfo,
-					     sizeof (cur_ddinfo)))
-				error = 0;
-		}
-		break;
-/*
- * Retrieve the extended async map
- */
-	case PPPIOCGXASYNCMAP:
-		error = -EFAULT;
-		if (!copy_to_user((void *) param3, ppp->xmit_async_map,
-				     sizeof (ppp->xmit_async_map)))
-			error = 0;
-		break;
-/*
- * Set the async extended map
- */
-	case PPPIOCSXASYNCMAP:
-		{
-			__u32 temp_tbl[8];
-
-			error = -EFAULT;
-			if (copy_from_user(temp_tbl, (void *) param3,
-					       sizeof (temp_tbl)))
+		if (!rcvd) {
+			/*
+			 * we're agreeing to send compressed packets.
+			 */
+			if (ppp->sc_xc_state == NULL)
 				break;
 
-			temp_tbl[1]  =	0x00000000;
-			temp_tbl[2] &= ~0x40000000;
-			temp_tbl[3] |=	0x60000000;
-
-			error = 0;
-			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->sc_xcomp->comp_init)
+			    (ppp->sc_xc_state,
+			     opt, opt_len,
+			     ppp->line, 0, ppp->flags & SC_DEBUG)) {
 				if (ppp->flags & SC_DEBUG)
-					printk (KERN_INFO
-						"ppp_tty_ioctl: set xasyncmap\n");
+					printk(KERN_DEBUG "%s: comp running\n",
+					       ppp->name);
+				cli();
+				ppp->flags |= SC_COMP_RUN;
 			}
+			break;
 		}
-		break;
-/*
- * Set the maximum VJ header compression slot number.
- */
-	case PPPIOCSMAXCID:
-		error = get_user(temp_i, (int *) param3);
-		if (error != 0)
+
+		/*
+		 * peer is agreeing to send compressed packets.
+		 */
+		if (ppp->sc_rc_state == NULL)
 			break;
-		temp_i = (temp_i & 255) + 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 = NULL;
 
-		ppp->slcomp = slhc_init (16, temp_i);
-		if (ppp->slcomp == NULL) {
-			printk (KERN_ERR "ppp_tty_ioctl: "
-				"no space for compression buffers!\n");
-			ppp_release (ppp);
-			error = -ENOMEM;
+		if ((*ppp->sc_rcomp->decomp_init)
+		    (ppp->sc_rc_state,
+		     opt, opt_len,
+		     ppp->line, 0, ppp->mru, ppp->flags & SC_DEBUG)) {
+			if (ppp->flags & SC_DEBUG)
+				printk(KERN_DEBUG "%s: decomp running\n",
+				       ppp->name);
+			cli();
+			ppp->flags |= SC_DECOMP_RUN;
+			ppp->flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
 		}
 		break;
 
-    case PPPIOCXFERUNIT:
-		ppp->backup_tty = tty;
-		ppp->sc_xfer = current->pid;
-		break;
-
-    case PPPIOCGNPMODE:
-    case PPPIOCSNPMODE:
-		{
-			struct npioctl npi;
-
-			error = -EFAULT;
-			if (copy_from_user(&npi, (void *) param3, sizeof(npi)))
-				break;
+	case CCP_RESETACK:
+		/*
+		 * CCP Reset-ack resets compressors and decompressors
+		 * as it passes through.
+		 */
+		if ((ppp->flags & SC_CCP_UP) == 0)
+			break;
 
-			if (npi.protocol != PPP_IP) {
+		if (!rcvd) {
+			if (ppp->sc_xc_state && (ppp->flags & SC_COMP_RUN)) {
+				(*ppp->sc_xcomp->comp_reset)(ppp->sc_xc_state);
 				if (ppp->flags & SC_DEBUG)
-					printk(KERN_DEBUG "pppioc[gs]npmode: "
-					       "invalid protocol %d\n",
-						npi.protocol);
-				error = -EINVAL;
-				break;
+					printk(KERN_DEBUG "%s: comp reset\n",
+					       ppp->name);
 			}
-			npi.protocol = NP_IP;
-
-			if (param2 == PPPIOCGNPMODE) {
-				npi.mode = ppp->sc_npmode[npi.protocol];
-				if (copy_to_user((void *) param3, &npi,
-						sizeof (npi)))
-					break;
+		} else {
+			if (ppp->sc_rc_state && (ppp->flags & SC_DECOMP_RUN)) {
+			      (*ppp->sc_rcomp->decomp_reset)(ppp->sc_rc_state);
+			      if (ppp->flags & SC_DEBUG)
+					printk(KERN_DEBUG "%s: decomp reset\n",
+					       ppp->name);
+			      cli();
+			      ppp->flags &= ~SC_DC_ERROR;
 			}
-
-			ppp->sc_npmode[npi.protocol] = npi.mode;
-			if (ppp->flags & SC_DEBUG)
-				printk(KERN_DEBUG "ppp: set np %d to %d\n",
-				       npi.protocol, npi.mode);
-			/* N.B. Why is the busy flag cleared here? */
-			ppp2dev(ppp)->tbusy = 0;
-			mark_bh(NET_BH);
-			error = 0;
-		}
-		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:
-		{
-			int count = ppp->ubuf->tail - ppp->ubuf->head;
-			if (count < 0)
-				count += (ppp->ubuf->size + 1);
-			error = put_user(count, (int *) param3);
 		}
 		break;
-/*
- *  All other ioctl() events will come here.
- */
-	default:
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_WARNING "ppp_tty_ioctl: "
-				"invalid ioctl=%x, addr=%lx\n", param2, param3);
-		error = -ENOIOCTLCMD;
-		break;
 	}
-	return error;
+	restore_flags(flags);
 }
 
 /*
- * TTY callback.
- *
- * Process the poll() statement for the PPP device.
+ * CCP is down; free (de)compressor state if necessary.
  */
 
-static unsigned int
-ppp_tty_poll (struct tty_struct *tty, struct file *filp, poll_table * wait)
+static void
+ppp_ccp_closed(struct ppp *ppp)
 {
-	struct ppp *ppp = tty2ppp (tty);
-	unsigned int mask = 0;
-
-	if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) {
-		CHECK_PPP (0);
+	unsigned long flags;
 
-		poll_wait(filp, &ppp->read_wait, wait);
-		poll_wait(filp, &ppp->write_wait, wait);
+	save_flags(flags);
+	cli();
+	ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN);
+	restore_flags(flags);
+	if (ppp->flags & SC_DEBUG)
+		printk(KERN_DEBUG "%s: ccp closed\n", ppp->name);
+	if (ppp->sc_xc_state) {
+		(*ppp->sc_xcomp->comp_free) (ppp->sc_xc_state);
+		ppp->sc_xc_state = NULL;
+	}
 
-		/* Must lock the user buffer area while checking. */
-		CHECK_BUF_MAGIC(ppp->ubuf);
-		if(test_and_set_bit(0, &ppp->ubuf->locked) == 0) {
-			if(ppp->ubuf->head != ppp->ubuf->tail)
-				mask |= POLLIN | POLLRDNORM;
-			clear_bit(0, &ppp->ubuf->locked);
-		}
-		if(tty->flags & (1 << TTY_OTHER_CLOSED))
-			mask |= POLLHUP;
-		if(tty_hung_up_p(filp))
-			mask |= POLLHUP;
-		if(ppp->tbuf->locked == 0)
-			mask |= POLLOUT | POLLWRNORM;
+	if (ppp->sc_rc_state) {
+		(*ppp->sc_rcomp->decomp_free) (ppp->sc_rc_state);
+		ppp->sc_rc_state = NULL;
 	}
-	return mask;
 }
 
 /*************************************************************
- * 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...).
+ * RECEIVE-SIDE ROUTINES
  *************************************************************/
 
 /*
- * Callback from the network layer when the device goes up.
+ * On entry, a received frame is in skb.
+ * Check it and dispose as appropriate.
  */
-
 static int
-ppp_dev_open (struct device *dev)
+ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb)
 {
-	struct ppp *ppp = dev2ppp (dev);
+	__u8	*data;
+	int	count;
+	int	proto;
+	int	new_count;
+	struct sk_buff *new_skb;
+	ppp_proto_type	*proto_ptr;
 
-	if (ppp2tty (ppp) == NULL) {
-		printk (KERN_ERR
-			"ppp: %s not connected to a TTY! can't go open!\n",
-			dev->name);
-		return -ENXIO;
+	/*
+	 * An empty frame is ignored. This occurs if the FLAG sequence
+	 * precedes and follows each frame.
+	 */
+	if (skb == NULL)
+		return 1;
+	if (skb->len == 0) {
+		kfree_skb(skb);
+		return 1;
 	}
+	data = skb->data;
+	count = skb->len;
 
-	if (ppp->flags & SC_DEBUG)
-		printk (KERN_INFO
-			"ppp: channel %s going up for IP packets!\n",
-			dev->name);
+	/*
+	 * Generate an error if the frame is too small.
+	 */
+	if (count < PPP_HDRLEN + 2) {
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_DEBUG
+			       "ppp: got runt ppp frame, %d chars\n", count);
+		++ppp->estats.rx_length_errors;
+		return 0;
+	}
 
-	CHECK_PPP (-ENXIO);
-	MOD_INC_USE_COUNT;
-	return 0;
+	/*
+	 * Verify the FCS of the frame and discard the FCS characters
+	 * from the end of the buffer.
+	 */
+	if (ppp->rfcs != PPP_GOODFCS) {
+		if (ppp->flags & SC_DEBUG) {
+			printk(KERN_DEBUG
+			       "ppp: frame with bad fcs, length = %d\n",
+			       count);
+			ppp_print_buffer("bad frame", data, count);
+		}
+		++ppp->estats.rx_crc_errors;
+		return 0;
+	}
+	count -= 2;		/* ignore the fcs characters */
+	skb_trim(skb, count);
+
+	/*
+	 * Process the active decompressor.
+	 */
+	if (ppp->sc_rc_state != NULL &&
+	    (ppp->flags & SC_DECOMP_RUN) &&
+	    ((ppp->flags & (SC_DC_FERROR | SC_DC_ERROR)) == 0)) {
+		if (PPP_PROTOCOL(data) == PPP_COMP) {
+			/*
+			 * If the frame is compressed then decompress it.
+			 */
+			new_skb = dev_alloc_skb(ppp->mru + 128 + PPP_HDRLEN);
+			if (new_skb == NULL) {
+				printk(KERN_ERR "ppp_recv_frame: no memory\n");
+				new_count = DECOMP_ERROR;
+			} else {
+				new_count = (*ppp->sc_rcomp->decompress)
+					(ppp->sc_rc_state, data, count,
+					 new_skb->data, ppp->mru + PPP_HDRLEN);
+			}
+			if (new_count > 0) {
+				/* Frame was decompressed OK */
+				kfree_skb(skb);
+				skb = new_skb;
+				count = new_count;
+				data = skb_put(skb, count);
+
+			} else {
+				/*
+				 * On a decompression error, we pass the
+				 * compressed frame up to pppd as an
+				 * error indication.
+				 */
+				if (ppp->flags & SC_DEBUG)
+					printk(KERN_INFO "%s: decomp err %d\n",
+					       ppp->name, new_count);
+				if (new_skb != 0)
+					kfree_skb(new_skb);
+				if (ppp->slcomp != 0)
+					slhc_toss(ppp->slcomp);
+				++ppp->stats.ppp_ierrors;
+				if (new_count == DECOMP_FATALERROR) {
+					ppp->flags |= SC_DC_FERROR;
+				} else {
+					ppp->flags |= SC_DC_ERROR;
+				}
+			}
+
+
+		} else {
+			/*
+			 * The frame is not compressed. Pass it to the
+			 * decompression code so it can update its
+			 * dictionary if necessary.
+			 */
+			(*ppp->sc_rcomp->incomp)(ppp->sc_rc_state,
+						 data, count);
+		}
+	}
+	else if (PPP_PROTOCOL(data) == PPP_COMP && (ppp->flags & SC_DEBUG))
+		printk(KERN_INFO "%s: not decomp, rc_state=%p flags=%x\n",
+		       ppp->name, ppp->sc_rc_state, ppp->flags);
+
+	/*
+	 * Count the frame and print it
+	 */
+	++ppp->stats.ppp_ipackets;
+	ppp->stats.ppp_ioctects += count;
+	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 protocol 0 which is the 'catch-all'
+	 * to feed it to the pppd daemon.
+	 */
+	proto = PPP_PROTOCOL(data);
+	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, skb)) {
+		kfree_skb(skb);
+		++ppp->stats.ppp_discards;
+	}
+
+	return 1;
 }
 
 /*
- * Callback from the network layer when the ppp device goes down.
+ * An input error has been detected, so we need to inform
+ * the VJ decompressor.
  */
+static void
+ppp_receive_error(struct ppp *ppp)
+{
+	CHECK_PPP_VOID();
 
+	if (ppp->slcomp != 0)
+		slhc_toss(ppp->slcomp);
+}
+
+/*
+ * Put the input frame into the networking system for the indicated protocol
+ */
 static int
-ppp_dev_close (struct device *dev)
+ppp_rcv_rx(struct ppp *ppp, __u16 proto, struct sk_buff *skb)
 {
-	struct ppp *ppp = dev2ppp (dev);
 
-	MOD_DEC_USE_COUNT;
-	if (ppp2tty (ppp) == NULL) {
-		return -ENXIO;
-	}
+	/*
+	 * Fill in a few fields of the skb and give it to netif_rx().
+	 */
+	skb->dev      = ppp2dev(ppp);	/* We are the device */
+	skb->protocol = htons(proto);
+	skb->mac.raw  = skb->data;
+	skb_pull(skb, PPP_HDRLEN);	/* pull off ppp header */
+	ppp->last_recv = jiffies;
+	netif_rx (skb);
+	return 1;
+}
+
 /*
- * We don't do anything about the device going down. It is not important
- * for us.
+ * Process the receipt of an IP frame
  */
-	if (ppp->flags & SC_DEBUG)
-		printk (KERN_INFO
-			"ppp: channel %s going down for IP packets!\n",
-			dev->name);
-	CHECK_PPP (-ENXIO);
+static int
+rcv_proto_ip(struct ppp *ppp, struct sk_buff *skb)
+{
+	CHECK_PPP(0);
+	if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0)
+	    && ppp->sc_npmode[NP_IP] == NPMODE_PASS)
+		return ppp_rcv_rx(ppp, ETH_P_IP, skb);
 	return 0;
 }
 
 /*
- * IOCTL operation to read the version of the driver.
+ * Process the receipt of an IPX frame
  */
-
 static int
-ppp_dev_ioctl_version (struct ppp *ppp, struct ifreq *ifr)
+rcv_proto_ipx(struct ppp *ppp, struct sk_buff *skb)
 {
-	int error;
-	char *result  = (char *) ifr->ifr_ifru.ifru_data;
-	int  len      = strlen (szVersion) + 1;
-/*
- * Move the version data
- */
-	error = -EFAULT;
-	if (!copy_to_user(result, szVersion, len))
-		error = 0;
-	return error;
+	CHECK_PPP(0);
+	if (((ppp2dev(ppp)->flags & IFF_UP) != 0) && (skb->len > 0)
+	    && ppp->sc_npmode[NP_IPX] == NPMODE_PASS)
+		return ppp_rcv_rx(ppp, ETH_P_IPX, skb);
+	return 0;
 }
 
 /*
- * IOCTL to read the statistics for the pppstats program.
+ * Process the receipt of an Appletalk frame
  */
-
 static int
-ppp_dev_ioctl_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev)
+rcv_proto_at(struct ppp *ppp, struct sk_buff *skb)
 {
-	struct ppp_stats *result, temp;
-	int    error;
+	CHECK_PPP(0);
+	if ((ppp2dev(ppp)->flags & IFF_UP) && (skb->len > 0)
+	    && ppp->sc_npmode[NP_AT] == NPMODE_PASS)
+		return ppp_rcv_rx(ppp, ETH_P_PPPTALK, skb);
+	return 0;
+}
+
 /*
- * Supply the information for the caller. First move the version data
- * then move the ppp stats; and finally the vj stats.
+ * Process the receipt of an VJ Compressed frame
  */
-	memset (&temp, 0, sizeof(temp));
-	if (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;
-		}
-	}
-
-	result = (struct ppp_stats *) ifr->ifr_ifru.ifru_data;
+static int
+rcv_proto_vjc_comp(struct ppp *ppp, struct sk_buff *skb)
+{
+	int new_count;
 
-	error = -EFAULT;
-	if (!copy_to_user(result, &temp, sizeof (temp)))
-		error = 0;
-	return error;
+	CHECK_PPP(0);
+	if ((ppp->flags & SC_REJ_COMP_TCP) || ppp->slcomp == NULL)
+		return 0;
+	new_count = slhc_uncompress(ppp->slcomp, skb->data + PPP_HDRLEN,
+				    skb->len - PPP_HDRLEN);
+	if (new_count < 0) {
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_NOTICE
+			       "ppp: error in VJ decompression\n");
+		return 0;
+	}
+	skb_put(skb, new_count + PPP_HDRLEN - skb->len);
+	return rcv_proto_ip(ppp, skb);
 }
 
 /*
- * IOCTL to read the compression statistics for the pppstats program.
+ * Process the receipt of an VJ Un-compressed frame
  */
+static int
+rcv_proto_vjc_uncomp(struct ppp *ppp, struct sk_buff *skb)
+{
+	CHECK_PPP(0);
+	if ((ppp->flags & SC_REJ_COMP_TCP) || ppp->slcomp == NULL)
+		return 0;
+	if (slhc_remember(ppp->slcomp, skb->data + PPP_HDRLEN,
+			  skb->len - PPP_HDRLEN) <= 0) {
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_NOTICE "ppp: error in VJ memorizing\n");
+		return 0;
+	}
+	return rcv_proto_ip(ppp, skb);
+}
 
 static int
-ppp_dev_ioctl_comp_stats (struct ppp *ppp, struct ifreq *ifr, struct device *dev)
+rcv_proto_ccp(struct ppp *ppp, struct sk_buff *skb)
 {
-	struct ppp_comp_stats *result, temp;
-	int    error;
+	CHECK_PPP(0);
+	ppp_proto_ccp (ppp, skb->data + PPP_HDRLEN, skb->len - PPP_HDRLEN, 1);
+	return rcv_proto_unknown(ppp, skb);
+}
+
 /*
- * Supply the information for the caller.
+ * Receive all unclassified protocols.
  */
-	memset (&temp, 0, sizeof(temp));
-	if (dev->flags & IFF_UP) {
-		if (ppp->sc_xc_state != NULL)
-			(*ppp->sc_xcomp->comp_stat) (ppp->sc_xc_state,
-						     &temp.c);
+static int
+rcv_proto_unknown(struct ppp *ppp, struct sk_buff *skb)
+{
+	CHECK_PPP(0);
 
-		if (ppp->sc_rc_state != NULL)
-			(*ppp->sc_rcomp->decomp_stat) (ppp->sc_rc_state,
-						       &temp.d);
+	/*
+	 * Limit queue length by dropping old frames.
+	 */
+	skb_queue_tail(&ppp->rcv_q, skb);
+	while (ppp->rcv_q.qlen > PPP_MAX_RCV_QLEN) {
+		struct sk_buff *skb = skb_dequeue(&ppp->rcv_q);
+		if (skb)
+			kfree_skb(skb);
 	}
-/*
- * Move the data to the caller's buffer
- */
-	result = (struct ppp_comp_stats *) ifr->ifr_ifru.ifru_data;
 
-	error = -EFAULT;
-	if (!copy_to_user(result, &temp, sizeof (temp)))
-		error = 0;
-	return error;
+	wake_up_interruptible (&ppp->read_wait);
+	if (ppp->tty->fasync != NULL)
+		kill_fasync (ppp->tty->fasync, SIGIO);
+
+	return 1;
 }
 
-/*
- * Callback from the network layer to process the sockioctl functions.
- */
+/*************************************************************
+ * TRANSMIT-SIDE ROUTINES
+ *************************************************************/
 
-static int
-ppp_dev_ioctl (struct device *dev, struct ifreq *ifr, int cmd)
-{
-	struct ppp *ppp = dev2ppp (dev);
-	int error;
+/* 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;
+}
 
-	CHECK_PPP_MAGIC(ppp);
 /*
- * Process the requests
+ * Compress and send an frame to the peer.
+ * Should be called with dev->tbusy == 1, having been set by the caller.
+ * That is, we use dev->tbusy as a lock to prevent reentry of this
+ * procedure.
  */
-	switch (cmd) {
-	case SIOCGPPPSTATS:
-		error = ppp_dev_ioctl_stats (ppp, ifr, dev);
-		break;
+static void
+ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
+{
+	int	proto;
+	__u8	*data;
+	int	count;
+	__u8	*p;
+	int	ret;
 
-	case SIOCGPPPCSTATS:
-		error = ppp_dev_ioctl_comp_stats (ppp, ifr, dev);
+	CHECK_PPP_VOID();
+	data = skb->data;
+	count = skb->len;
+
+	/* dump the buffer */
+	if (ppp->flags & SC_LOG_OUTPKT)
+		ppp_print_buffer ("write frame", data, count);
+
+	/*
+	 * Handle various types of protocol-specific compression
+	 * and other processing, including:
+	 * - VJ TCP header compression
+	 * - updating LQR packets
+	 * - updating CCP state on CCP packets
+	 */
+	proto = PPP_PROTOCOL(data);
+	switch (proto) {
+	case PPP_IP:
+		if ((ppp->flags & SC_COMP_TCP) && ppp->slcomp != NULL)
+			skb = ppp_vj_compress(ppp, skb);
 		break;
 
-	case SIOCGPPPVER:
-		error = ppp_dev_ioctl_version (ppp, ifr);
+	case PPP_LQR:
+		/*
+		 * Update the LQR frame with the current MIB information.
+		 * This way the information is accurate and up-to-date.
+		 */
+		if (count < 48)
+			break;
+		p = data + 40;	/* Point to last two items. */
+		p = store_long(p, ppp->stats.ppp_opackets + 1);
+		p = store_long(p, ppp->stats.ppp_ooctects + count);
+		++ppp->stats.ppp_olqrs;
 		break;
 
-	default:
-		error = -EINVAL;
+	case PPP_CCP:
+		/*
+		 * Outbound compression control frames
+		 */
+		ppp_proto_ccp(ppp, data + PPP_HDRLEN, count - PPP_HDRLEN, 0);
 		break;
 	}
-	return error;
+	data = skb->data;
+	count = skb->len;
+
+	/*
+	 * Compress the whole frame if possible.
+	 */
+	if (((ppp->flags & SC_COMP_RUN) != 0)	&&
+	    (ppp->sc_xc_state != (void *) 0)	&&
+	    (proto != PPP_LCP)			&&
+	    (proto != PPP_CCP)) {
+		struct sk_buff *new_skb;
+		int new_count;
+
+		/* Allocate an skb for the compressed frame. */
+		new_skb = alloc_skb(ppp->mtu + PPP_HDRLEN, GFP_ATOMIC);
+		if (new_skb == NULL) {
+			printk(KERN_ERR "ppp_send_frame: no memory\n");
+			kfree_skb(skb);
+			ppp->dev.tbusy = 0;
+			return;
+		}
+
+		/* Compress the frame. */
+		new_count = (*ppp->sc_xcomp->compress)
+			(ppp->sc_xc_state, data, new_skb->data,
+			 count, ppp->mtu + PPP_HDRLEN);
+
+		/* Did it compress? */
+		if (new_count > 0 && (ppp->flags & SC_CCP_UP)) {
+			skb_put(new_skb, new_count);
+			kfree_skb(skb);
+			skb = new_skb;
+		} else {
+			/*
+			 * The frame could not be compressed, or it could not
+			 * be sent in compressed form because CCP is down.
+			 */
+			kfree_skb(new_skb);
+		}
+	}
+
+	/*
+	 * Send the frame
+	 */
+	ret = ppp_async_send(ppp, skb);
+	if (ret > 0) {
+		/* we can release the lock */
+		ppp->dev.tbusy = 0;
+	} else if (ret < 0) {
+		/* this can't happen, since the caller got the tbusy lock */
+		printk(KERN_ERR "ppp: ppp_async_send didn't accept pkt\n");
+	}
 }
 
 /*
- * 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.
- *	  -1 if frame should be dropped.
+ * Apply VJ TCP header compression to a packet.
  */
-
-static int
-ppp_dev_xmit_ip (struct ppp *ppp, struct ppp_buffer *buf,
-		 __u8 *data, int len, enum NPmode npmode)
+static struct sk_buff *
+ppp_vj_compress(struct ppp *ppp, struct sk_buff *skb)
 {
-	int	proto = PPP_IP;
-	__u8	*hdr;
-/*
- * Branch on the type of processing for the IP frame.
- */
-	switch (npmode) {
-	case NPMODE_PASS:
-		break;
+	__u8 *orig_data, *data;
+	struct sk_buff *new_skb;
+	int len, proto;
 
-	case NPMODE_QUEUE:
-		/*
-		 * We may not send the packet now, so drop it.
-		 * XXX It would be nice to be able to return it to the
-		 * network system to be queued and retransmitted later.
-		 */
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG "%s: returning frame\n",
-			       ppp->name);
-		return -1;
+	new_skb = alloc_skb(skb->len, GFP_ATOMIC);
+	if (new_skb == NULL) {
+		printk(KERN_ERR "ppp: no memory for vj compression\n");
+		return skb;
+	}
 
-	case NPMODE_ERROR:
-	case NPMODE_DROP:
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_DEBUG
-				"ppp_dev_xmit: npmode = %d on %s\n",
-				ppp->sc_npmode[NP_IP], ppp->name);
-		return -1;
+	orig_data = data = skb->data + PPP_HDRLEN;
+	len = slhc_compress(ppp->slcomp, data, skb->len - PPP_HDRLEN,
+			    new_skb->data + PPP_HDRLEN, &data,
+			    (ppp->flags & SC_NO_TCP_CCID) == 0);
 
-	default:
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_WARNING
-				"ppp_dev_xmit: unknown npmode %d on %s\n",
-				ppp->sc_npmode[NP_IP], ppp->name);
-		return -1;
+	if (data == orig_data) {
+		/* Couldn't compress the data */
+		kfree_skb(new_skb);
+		return skb;
 	}
-/*
- * At this point, the buffer will be transmitted. There is no other exit.
- *
- * Try to compress the header.
- */
-	if (ppp->flags & SC_COMP_TCP) {
-		len = slhc_compress (ppp->slcomp, data, len,
-				     buf_base (ppp->cbuf) + PPP_HDRLEN,
-				     &data,
-				     (ppp->flags & SC_NO_TCP_CCID) == 0);
-
-		if (data[0] & SL_TYPE_COMPRESSED_TCP) {
-			proto	 = PPP_VJC_COMP;
-			data[0] ^= SL_TYPE_COMPRESSED_TCP;
-		} else {
-			if (data[0] >= SL_TYPE_UNCOMPRESSED_TCP)
-				proto = PPP_VJC_UNCOMP;
-			data[0] = (data[0] & 0x0f) | 0x40;
-		}
+
+	/* The data has been changed */
+	if (data[0] & SL_TYPE_COMPRESSED_TCP) {
+		proto = PPP_VJC_COMP;
+		data[0] ^= SL_TYPE_COMPRESSED_TCP;
+	} else {
+		if (data[0] >= SL_TYPE_UNCOMPRESSED_TCP)
+			proto = PPP_VJC_UNCOMP;
+		else
+			proto = PPP_IP;
+		data[0] = orig_data[0];
 	}
-/*
- * Send the frame
- */
-	len  += PPP_HDRLEN;
-	hdr   = data - PPP_HDRLEN;
 
-	hdr[0] = PPP_ALLSTATIONS;
-	hdr[1] = PPP_UI;
-	hdr[2] = 0;
-	hdr[3] = proto;
+	data = skb_put(new_skb, len + PPP_HDRLEN);
+	data[0] = PPP_ALLSTATIONS;
+	data[1] = PPP_UI;
+	data[2] = 0;
+	data[3] = proto;
 
-	return ppp_dev_xmit_frame (ppp, buf, hdr, len);
+	kfree_skb(skb);
+	return new_skb;
+}
+
+static inline void
+ppp_send_frames(struct ppp *ppp)
+{
+	struct sk_buff *skb;
+
+	while (!test_and_set_bit(0, &ppp->dev.tbusy)) {
+		skb = skb_dequeue(&ppp->xmt_q);
+		if (skb == NULL) {
+			ppp->dev.tbusy = 0;
+			mark_bh(NET_BH);
+			break;
+		}
+		ppp_send_frame(ppp, skb);
+	}
 }
 
 /*
- * Send a non-IP data frame (such as an IPX frame) to the remote.
- *
- * Return 0 if frame was queued for transmission.
- *	  1 if frame must be re-queued for later driver support.
+ * Called from the hardware (tty) layer when it can accept
+ * another packet.
  */
-static int
-ppp_dev_xmit_other (struct device *dev, struct ppp *ppp,
-		  __u8 *data, int len, int proto)
+static void
+ppp_output_wakeup(struct ppp *ppp)
 {
-	__u8	*hdr;
+	CHECK_PPP_VOID();
+
+	if (!ppp->dev.tbusy) {
+		printk(KERN_ERR "ppp_output_wakeup called but tbusy==0\n");
+		return;
+	}
+	ppp->dev.tbusy = 0;
+	ppp_send_frames(ppp);
+}
+
 /*
- * Send the frame
+ * Send a control frame (from pppd).
  */
-	len += PPP_HDRLEN;
-	hdr = data - PPP_HDRLEN;
-
-	hdr[0] = PPP_ALLSTATIONS;
-	hdr[1] = PPP_UI;
-	hdr[2] = proto >> 8;
-	hdr[3] = proto;
+static void
+ppp_send_ctrl(struct ppp *ppp, struct sk_buff *skb)
+{
+	CHECK_PPP_VOID();
 
-	return ppp_dev_xmit_frame (ppp, ppp->wbuf, hdr, len);
+	/*
+	 * Put the packet on the queue, then send as many as we can.
+	 */
+	skb_queue_tail(&ppp->xmt_q, skb);
+	ppp_send_frames(ppp);
 }
 
+
+/*************************************************************
+ * NETWORK OUTPUT
+ *    This routine accepts requests from the network layer
+ *    and attempts to deliver the packets.
+ *************************************************************/
 /*
- * Send a frame to the remote.
+ * Send a frame to the peer.
+ * Returns 1 iff the frame was not accepted.
  */
-
 static int
-ppp_dev_xmit (sk_buff *skb, struct device *dev)
+ppp_dev_xmit(struct sk_buff *skb, struct device *dev)
 {
-	int answer, len;
-	__u8		  *data;
-	struct ppp	  *ppp = dev2ppp (dev);
-	struct tty_struct *tty = ppp2tty (ppp);
-/*
- * just a little sanity check.
- */
-	if (skb == NULL) {
-		if (ppp->flags & SC_DEBUG)
-			printk (KERN_WARNING "ppp_dev_xmit: null packet!\n");
+	struct ppp *ppp = dev2ppp(dev);
+	struct tty_struct *tty = ppp2tty(ppp);
+	enum NPmode npmode;
+	int proto;
+	unsigned char *hdr;
+
+	/* just a little sanity check. */
+	if (skb == NULL)
+		return 0;
+	if (skb->data == NULL) {
+		kfree_skb(skb);
 		return 0;
 	}
-/*
- * Avoid timing problem should tty hangup while data is queued to be sent
- */
+
+	/*
+	 * Avoid timing problem should tty hangup while data is
+	 * queued to be sent.
+	 */
 	if (!ppp->inuse) {
-		dev_kfree_skb (skb);
+		dev_kfree_skb(skb);
 		return 0;
 	}
-/*
- * Validate the tty interface
- */
+
+	/*
+	 * Validate the tty interface
+	 */
 	if (tty == NULL) {
 		if (ppp->flags & SC_DEBUG)
-			printk (KERN_ERR
-				"ppp_dev_xmit: %s not connected to a TTY!\n",
-				dev->name);
-		dev_kfree_skb (skb);
+			printk(KERN_ERR
+			       "ppp_dev_xmit: %s not connected to a TTY!\n",
+			       dev->name);
+		dev_kfree_skb(skb);
 		return 0;
 	}
-/*
- * Fetch the pointer to the data
- */
-	len   = skb->len;
-	data  = skb_data(skb);
 
-	if (data == (__u8 *) 0) {
+	/*
+	 * Work out the appropriate network-protocol mode for this packet.
+	 */
+	npmode = NPMODE_PASS;	/* default */
+	switch (ntohs(skb->protocol)) {
+	case ETH_P_IP:
+		proto = PPP_IP;
+		npmode = ppp->sc_npmode[NP_IP];
+		break;
+	case ETH_P_IPX:
+		proto = PPP_IPX;
+		npmode = ppp->sc_npmode[NP_IPX];
+		break;
+	case ETH_P_PPPTALK:
+	case ETH_P_ATALK:
+		proto = PPP_AT;
+		npmode = ppp->sc_npmode[NP_AT];
+		break;
+	default:
 		if (ppp->flags & SC_DEBUG)
-			printk (KERN_CRIT "ppp_dev_xmit: %s Null skb data\n",
-				dev->name);
-		dev_kfree_skb (skb);
+			printk(KERN_INFO "%s: packet for unknown proto %x\n",
+			       ppp->name, ntohs(skb->protocol));
+		dev_kfree_skb(skb);
 		return 0;
 	}
-/*
- * Detect a change in the transfer size
- */
-	if (ppp->mtu != ppp2dev (ppp)->mtu) {
-		ppp_changedmtu (ppp,
-				ppp2dev (ppp)->mtu,
-				ppp->mru);
-	}
-/*
- * Acquire the lock on the transmission buffer. If the buffer was busy then
- * mark the device as busy.
- * We also require that ppp->tbuf be unlocked, in order to serialize
- * calls to ppp_dev_xmit_frame (which does compression) and the output
- * of frames w.r.t. tty writes from pppd.
- */
-	CHECK_BUF_MAGIC(ppp->wbuf);
-	if (ppp->tbuf->locked || lock_buffer (ppp->wbuf) != 0) {
-		dev->tbusy = 1;
-		if (ppp->flags & SC_DEBUG)
-			printk(KERN_DEBUG "dev_xmit blocked, t=%lu w=%lu\n",
-			       ppp->tbuf->locked, ppp->wbuf->locked);
-		return 1;
-	}
-/*
- * Look at the protocol in the skb to determine the difference between
- * an IP frame and an IPX frame.
- */
-	switch (ntohs (skb->protocol)) {
-	case ETH_P_IPX:
-		answer = ppp_dev_xmit_other (dev, ppp, data, len, PPP_IPX);
-		break;
 
-	case ETH_P_IP:
-		answer = ppp_dev_xmit_ip (ppp, ppp->wbuf, data, len,
-					  ppp->sc_npmode[NP_IP]);
+	/*
+	 * Drop, accept or reject the packet depending on the mode.
+	 */
+	switch (npmode) {
+	case NPMODE_PASS:
 		break;
 
-	default: /* All others have no support at this time. */
-		dev_kfree_skb (skb);
+	case NPMODE_QUEUE:
+		/*
+		 * We may not send the packet now, so drop it.
+		 * XXX It would be nice to be able to return it to the
+		 * network system to be queued and retransmitted later.
+		 */
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_DEBUG "%s: returning frame\n", ppp->name);
+		dev_kfree_skb(skb);
+		return 0;
+
+	case NPMODE_ERROR:
+	case NPMODE_DROP:
+		if (ppp->flags & SC_DEBUG)
+			printk(KERN_DEBUG
+			       "ppp_dev_xmit: dropping (npmode = %d) on %s\n",
+			       npmode, ppp->name);
+		dev_kfree_skb(skb);
 		return 0;
 	}
-/*
- * This is the end of the transmission. Release the buffer if it was sent.
- */
-	if (answer == 0) {
-		/* packet queued OK */
-		dev_kfree_skb (skb);
-	} else {
-		ppp->wbuf->locked = 0;
-		if (answer < 0) {
-			/* packet should be dropped */
-			dev_kfree_skb (skb);
-			answer = 0;
-		} else {
-			/* packet should be queued for later */
-			dev->tbusy = 1;
+
+	/*
+	 * The dev->tbusy field acts as a lock to allow only
+	 * one packet to be processed at a time.  If we can't
+	 * get the lock, try again later.
+	 */
+	if (test_and_set_bit(0, &dev->tbusy))
+		return 1;
+
+	/*
+	 * Put the 4-byte PPP header on the packet.
+	 * If there isn't room for it, we have to copy the packet.
+	 */
+	if (skb_headroom(skb) < PPP_HDRLEN) {
+		struct sk_buff *new_skb;
+
+		new_skb = alloc_skb(skb->len + PPP_HDRLEN, GFP_ATOMIC);
+		if (new_skb == NULL) {
+			printk(KERN_ERR "%s: skb hdr alloc failed\n",
+			       ppp->name);
+			dev_kfree_skb(skb);
+			dev->tbusy = 0;
+			return 0;
 		}
+		skb_reserve(new_skb, PPP_HDRLEN);
+		memcpy(skb_put(new_skb, skb->len), skb->data, skb->len);
+		dev_kfree_skb(skb);
+		skb = new_skb;
 	}
-	return answer;
+
+	hdr = skb_push(skb, PPP_HDRLEN);
+	hdr[0] = PPP_ALLSTATIONS;
+	hdr[1] = PPP_UI;
+	hdr[2] = proto >> 8;
+	hdr[3] = proto;
+
+	ppp_send_frame(ppp, skb);
+	return 0;
 }
 
 /*
  * Generate the statistic information for the /proc/net/dev listing.
  */
-
 static struct net_device_stats *
 ppp_dev_stats (struct device *dev)
 {
@@ -3017,7 +2484,7 @@
 
 /* Locate the previous instance of the PPP channel */
 static struct ppp *
-ppp_find (int pid_value)
+ppp_find(int pid_value)
 {
 	struct ppp	*ppp;
 
@@ -3031,104 +2498,152 @@
 	return ppp;
 }
 
-/* Collect hanged up channels */
-
-static void ppp_sync(void)
-{
-	struct device	*dev;
-	struct ppp	*ppp;
-
-	rtnl_lock();
-	for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
-		if (!ppp->inuse) {
-			dev = ppp2dev(ppp);
-			if (dev->flags&IFF_UP)
-				dev_close(dev);
-		}
-	}
-	rtnl_unlock();
-}
-
-
 /* allocate or create a PPP channel */
 static struct ppp *
-ppp_alloc (void)
+ppp_alloc(void)
 {
 	int		if_num;
 	int		status;
 	struct device	*dev;
 	struct ppp	*ppp;
 
-	ppp_sync();
-
 	/* try to find an free device */
-	if_num = 0;
 	for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
 		if (!test_and_set_bit(0, &ppp->inuse)) {
-
-			/* Reregister device */
-
 			dev = ppp2dev(ppp);
-			unregister_netdev (dev);
-
-			if (register_netdev (dev)) {
-				printk(KERN_DEBUG "cannot reregister ppp device\n");
-				return NULL;
+			if (dev->flags & IFF_UP) {
+				clear_bit(0, &ppp->inuse);
+				continue;
 			}
-			return ppp;
+			/* Reregister device */
+			unregister_netdev(dev);
+			if (register_netdev(dev) == 0)
+				return ppp;
+			printk(KERN_DEBUG "could not reregister ppp device\n");
+			/* leave inuse set in this case */
 		}
-		++if_num;
 	}
-/*
- * There are no available units, so make a new one.
- */
-	ppp = (struct ppp *) kmalloc (sizeof(struct ppp), GFP_KERNEL);
-	if (ppp == 0)
+
+	/*
+	 * There are no available units, so make a new one.
+	 */
+	ppp = (struct ppp *) kmalloc(sizeof(struct ppp), GFP_KERNEL);
+	if (ppp == 0) {
+		printk(KERN_ERR "ppp: struct ppp allocation failed\n");
 		return 0;
+	}
 	memset(ppp, 0, sizeof(*ppp));
 
 	/* initialize channel control data */
-	set_bit(0, &ppp->inuse);
-
-	ppp->line      = if_num;
-	ppp->tty       = NULL;
-	ppp->backup_tty = NULL;
-	if (ppp_last == 0)
-		ppp_list = ppp;
-	else
-		ppp_last->next = ppp;
-	ppp_last = ppp;
-	ppp->next = 0;
-	ppp->read_wait	= NULL;
-	ppp->write_wait = NULL;
+	ppp->magic = PPP_MAGIC;
+	ppp->next = NULL;
+	ppp->inuse = 1;
+	ppp->read_wait = NULL;
 
+	/*
+	 * Make up a suitable name for this device
+	 */
 	dev = ppp2dev(ppp);
-	dev->next      = NULL;
-	dev->init      = ppp_init_dev;
-	dev->name      = ppp->name;
-	sprintf(dev->name, "ppp%d", if_num);
-	dev->base_addr = (__u32) if_num;
-	dev->priv      = (void *) ppp;
+	dev->name = ppp->name;
+	if_num = dev_alloc_name(dev, "ppp%d");
+	if (if_num < 0) {
+		printk(KERN_ERR "ppp: dev_alloc_name failed (%d)\n", if_num);
+		kfree(ppp);
+		return 0;
+	}
+	ppp->line = if_num;
+	ppp->slcomp = NULL;
+
+	dev->next = NULL;
+	dev->init = ppp_init_dev;
+	dev->name = ppp->name;
+	dev->priv = (void *) ppp;
 
 	/* register device so that we can be ifconfig'd */
 	/* ppp_init_dev() will be called as a side-effect */
 	status = register_netdev (dev);
 	if (status == 0) {
-		printk (KERN_INFO "registered device %s\n", dev->name);
+		printk(KERN_INFO "registered device %s\n", dev->name);
 	} else {
-		printk (KERN_ERR
+		printk(KERN_ERR
 		       "ppp_alloc - register_netdev(%s) = %d failure.\n",
-			dev->name, status);
+		       dev->name, status);
+		kfree(ppp);
 		ppp = NULL;
-		/* This one will forever be busy as it is not initialized */
 	}
+
+	/* link this unit into our list */
+	if (ppp_list == 0)
+		ppp_list = ppp;
+	else
+		ppp_last->next = ppp;
+	ppp_last = ppp;
+
 	return ppp;
 }
 
 /*
- * Utility procedures to print a buffer in hex/ascii
+ * Initialize the generic parts of the ppp structure.
+ */
+static void
+ppp_generic_init(struct ppp *ppp)
+{
+	int indx;
+
+	ppp->flags  = 0;
+	ppp->mtu    = PPP_MTU;
+	ppp->mru    = PPP_MRU;
+
+	skb_queue_head_init(&ppp->xmt_q);
+	skb_queue_head_init(&ppp->rcv_q);
+
+	ppp->last_xmit	= jiffies;
+	ppp->last_recv  = jiffies;
+
+	/* clear statistics */
+	memset(&ppp->stats, 0, sizeof (struct pppstat));
+	memset(&ppp->estats, 0, sizeof(struct net_device_stats));
+
+	/* PPP compression data */
+	ppp->sc_xc_state = NULL;
+	ppp->sc_rc_state = NULL;
+
+	for (indx = 0; indx < NUM_NP; ++indx)
+		ppp->sc_npmode[indx] = NPMODE_PASS;
+}
+
+/*
+ * Called to clean up the generic parts of the ppp structure.
  */
+static void
+ppp_release(struct ppp *ppp)
+{
+	struct sk_buff *skb;
+
+	CHECK_PPP_MAGIC(ppp);
+
+	if (ppp->flags & SC_DEBUG)
+		printk(KERN_DEBUG "%s released\n", ppp->name);
+
+	ppp_ccp_closed(ppp);
+
+        /* Ensure that the pppd process is not hanging on select()/poll() */
+        wake_up_interruptible(&ppp->read_wait);
+
+	if (ppp->slcomp) {
+		slhc_free(ppp->slcomp);
+		ppp->slcomp = NULL;
+	}
+
+	while ((skb = skb_dequeue(&ppp->rcv_q)) != NULL)
+		kfree_skb(skb);
+	while ((skb = skb_dequeue(&ppp->xmt_q)) != NULL)
+		kfree_skb(skb);
+}
 
+/*
+ * Utility procedures to print a buffer in hex/ascii
+ */
 static void
 ppp_print_hex (register __u8 * out, const __u8 * in, int count)
 {
@@ -3163,18 +2678,18 @@
 }
 
 static void
-ppp_print_buffer (const __u8 * name, const __u8 * buf, int count)
+ppp_print_buffer (const char *name, const __u8 *buf, int count)
 {
 	__u8 line[44];
 
-	if (name != (__u8 *) NULL)
-		printk (KERN_DEBUG "ppp: %s, count = %d\n", name, count);
+	if (name != NULL)
+		printk(KERN_DEBUG "ppp: %s, count = %d\n", name, count);
 
 	while (count > 8) {
 		memset (line, 32, 44);
 		ppp_print_hex (line, buf, 8);
 		ppp_print_char (&line[8 * 3], buf, 8);
-		printk (KERN_DEBUG "%s\n", line);
+		printk(KERN_DEBUG "%s\n", line);
 		count -= 8;
 		buf += 8;
 	}
@@ -3183,7 +2698,7 @@
 		memset (line, 32, 44);
 		ppp_print_hex (line, buf, count);
 		ppp_print_char (&line[8 * 3], buf, count);
-		printk (KERN_DEBUG "%s\n", line);
+		printk(KERN_DEBUG "%s\n", line);
 	}
 }
 
@@ -3225,7 +2740,8 @@
 	struct compressor_link *new;
 	unsigned long flags;
 
-	new = (struct compressor_link *) kmalloc (sizeof (struct compressor_link), GFP_KERNEL);
+	new = (struct compressor_link *)
+		kmalloc (sizeof (struct compressor_link), GFP_KERNEL);
 
 	if (new == (struct compressor_link *) 0)
 		return 1;
@@ -3286,63 +2802,49 @@
 	/* register our line disciplines */
 	status = ppp_first_time();
 	if (status != 0)
-		printk (KERN_INFO
-		       "PPP: ppp_init() failure %d\n", status);
-	return (status);
+		printk(KERN_INFO "PPP: ppp_init() failure %d\n", status);
+
+	return status;
 }
 
 void
 cleanup_module(void)
 {
 	int status;
-	struct device *dev;
 	struct ppp *ppp, *next_ppp;
-	int busy_flag = 0;
-/*
- * Ensure that the devices are not in operation.
- */
-	for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
-		if (ppp->inuse && ppp->tty != NULL) {
-			busy_flag = 1;
-			break;
-		}
+	int busy = 0;
 
-		dev = ppp2dev (ppp);
-		if (dev->start || dev->flags & IFF_UP) {
-			busy_flag = 1;
-			break;
-		}
-	}
-/*
- * Ensure that there are no compressor modules registered
- */
-	if (ppp_compressors != NULL)
-		busy_flag = 1;
+	/*
+	 * Ensure that the devices are not in operation.
+	 */
+	for (ppp = ppp_list; ppp != 0; ppp = ppp->next) {
+		CHECK_PPP_MAGIC(ppp);
+		if (ppp->inuse || (ppp->dev.flags & IFF_UP))
+			++busy;
+	}
+	if (busy)
+		printk(KERN_CRIT "PPP: removing despite %d units in use!\n",
+		       busy);
 
-	if (busy_flag) {
-		printk (KERN_INFO
-			"PPP: device busy, remove delayed\n");
-		return;
-	}
-/*
- * Release the tty registration of the line discipline so that no new entries
- * may be created.
- */
+	/*
+	 * Release the tty registration of the line discipline so that
+	 * ttys can no longer be put into PPP line discipline.
+	 */
 	status = tty_register_ldisc (N_PPP, NULL);
 	if (status != 0)
-		printk (KERN_INFO
-			"PPP: Unable to unregister ppp line discipline "
-			"(err = %d)\n", status);
+		printk(KERN_ERR
+		       "PPP: Unable to unregister ppp line discipline "
+		       "(err = %d)\n", status);
 	else
-		printk (KERN_INFO
+		printk(KERN_INFO
 		       "PPP: ppp line discipline successfully unregistered\n");
-/*
- * De-register the devices so that there is no problem with them
- */
+
+	/*
+	 * De-register the devices so that there is no problem with them
+	 */
 	for (ppp = ppp_list; ppp != 0; ppp = next_ppp) {
 		next_ppp = ppp->next;
-		ppp_release (ppp);
-		unregister_netdev (&ppp->dev);
+		unregister_netdev(&ppp->dev);
 		kfree (ppp);
 	}
 }
diff -u --recursive --new-file v2.1.108/linux/drivers/pci/oldproc.c linux/drivers/pci/oldproc.c
--- v2.1.108/linux/drivers/pci/oldproc.c	Wed May 20 19:10:39 1998
+++ linux/drivers/pci/oldproc.c	Fri Jul 10 10:48:37 1998
@@ -403,10 +403,15 @@
 	DEVICE( KINETIC,	KINETIC_2915,	"2915 CAMAC"),
 	DEVICE( COMPEX,		COMPEX_ENET100VG4, "Readylink ENET100-VG4"),
 	DEVICE( COMPEX,		COMPEX_RL2000,	"ReadyLink 2000"),
-	DEVICE( RP,             RP8OCTA,        "RocketPort 8 Oct"),
+	DEVICE( RP,             RP32INTF,       "RocketPort 32 Intf"),
 	DEVICE( RP,             RP8INTF,        "RocketPort 8 Intf"),
 	DEVICE( RP,             RP16INTF,       "RocketPort 16 Intf"),
-	DEVICE( RP,             RP32INTF,       "RocketPort 32 Intf"),
+	DEVICE( RP, 		RP4QUAD,	"Rocketport 4 Quad"),
+	DEVICE( RP,             RP8OCTA,        "RocketPort 8 Oct"),
+	DEVICE( RP,             RP8J,	        "RocketPort 8 J"),
+	DEVICE( RP,             RPP4,	        "RocketPort Plus 4 Quad"),
+	DEVICE( RP,             RPP8,	        "RocketPort Plus 8 Oct"),
+	DEVICE( RP,             RP8M,	        "RocketModem 8 J"),
 	DEVICE( CYCLADES,	CYCLOM_Y_Lo,	"Cyclom-Y below 1Mbyte"),
 	DEVICE( CYCLADES,	CYCLOM_Y_Hi,	"Cyclom-Y above 1Mbyte"),
 	DEVICE( CYCLADES,	CYCLOM_Z_Lo,	"Cyclom-Z below 1Mbyte"),
diff -u --recursive --new-file v2.1.108/linux/drivers/sbus/char/Config.in linux/drivers/sbus/char/Config.in
--- v2.1.108/linux/drivers/sbus/char/Config.in	Mon Jan 12 15:15:45 1998
+++ linux/drivers/sbus/char/Config.in	Fri Jul 10 15:18:31 1998
@@ -1,42 +1,45 @@
 comment 'SBUS Frame Buffer support'
-bool 'Sun FB drivers appear in PROCFS' SUN_FBS_IN_PROCFS
-bool 'Load All Supported Drivers' CONFIG_SUN_FB_DISPLAY
 
-if [ "$CONFIG_SUN_FB_DISPLAY" = "n" ]; then
-	bool 'cgsix support' SUN_FB_CGSIX
-	bool 'tcx support' SUN_FB_TCX
-	bool 'cgthree support' SUN_FB_CGTHREE
-	bool 'cgfourteen support' SUN_FB_CGFOURTEEN
-	bool 'bwtwo support' SUN_FB_BWTWO
-	bool 'leo/zx support' SUN_FB_LEO
-	bool 'weitek P9X00 support' TADPOLE_FB_WEITEK
-	bool 'creator support' SUN_FB_CREATOR
-	if [ "$TADPOLE_FB_WEITEK" = "n" ]; then
-		fbs=$SUN_FB_CGSIX
-		fbs=$fbs$SUN_FB_TCX
-		fbs=$fbs$SUN_FB_CGTHREE
-		fbs=$fbs$SUN_FB_BWTWO
-		fbs=$fbs$SUN_FB_CGFOURTEEN
-		fbs=$fbs$SUN_FB_LEO
-		fbs=$fbs$TADPOLE_FB_WEITEK
-		fbs=$fbs$SUN_FB_CREATOR
-		if [ "$fbs" = "nnnnnnnn" ]; then
-			echo "Warning: You have excluded ALL FB Support"
-			echo "Notice:  Enabling Generic AutoResolution"
+if [ "$CONFIG_FB" != "y" ]; then
+	bool 'Sun FB drivers appear in PROCFS' SUN_FBS_IN_PROCFS
+	bool 'Load All Supported Drivers' CONFIG_SUN_FB_DISPLAY
+
+	if [ "$CONFIG_SUN_FB_DISPLAY" = "n" ]; then
+		bool 'cgsix support' SUN_FB_CGSIX
+		bool 'tcx support' SUN_FB_TCX
+		bool 'cgthree support' SUN_FB_CGTHREE
+		bool 'cgfourteen support' SUN_FB_CGFOURTEEN
+		bool 'bwtwo support' SUN_FB_BWTWO
+		bool 'leo/zx support' SUN_FB_LEO
+		bool 'weitek P9X00 support' TADPOLE_FB_WEITEK
+		bool 'creator support' SUN_FB_CREATOR
+		if [ "$TADPOLE_FB_WEITEK" = "n" ]; then
+			fbs=$SUN_FB_CGSIX
+			fbs=$fbs$SUN_FB_TCX
+			fbs=$fbs$SUN_FB_CGTHREE
+			fbs=$fbs$SUN_FB_BWTWO
+			fbs=$fbs$SUN_FB_CGFOURTEEN
+			fbs=$fbs$SUN_FB_LEO
+			fbs=$fbs$TADPOLE_FB_WEITEK
+			fbs=$fbs$SUN_FB_CREATOR
+			if [ "$fbs" = "nnnnnnnn" ]; then
+				echo "Warning: You have excluded ALL FB Support"
+				echo "Notice:  Enabling Generic AutoResolution"
+				define_bool SUN_FB_GENERIC y
+			fi
+		else
 			define_bool SUN_FB_GENERIC y
 		fi
 	else
-		define_bool SUN_FB_GENERIC y
+		define_bool SUN_FB_CGSIX y
+		define_bool SUN_FB_TCX y
+		define_bool SUN_FB_CGTHREE y
+		define_bool SUN_FB_CGFOURTEEN y
+		define_bool SUN_FB_BWTWO y
+		define_bool SUN_FB_LEO y
+		define_bool TADPOLE_FB_WEITEK y
+		define_bool SUN_FB_CREATOR y
 	fi
-else
-	define_bool SUN_FB_CGSIX y
-	define_bool SUN_FB_TCX y
-	define_bool SUN_FB_CGTHREE y
-	define_bool SUN_FB_CGFOURTEEN y
-	define_bool SUN_FB_BWTWO y
-	define_bool SUN_FB_LEO y
-	define_bool TADPOLE_FB_WEITEK y
-	define_bool SUN_FB_CREATOR y
 fi
 
 comment 'Misc Linux/SPARC drivers'
diff -u --recursive --new-file v2.1.108/linux/drivers/sbus/char/Makefile linux/drivers/sbus/char/Makefile
--- v2.1.108/linux/drivers/sbus/char/Makefile	Thu Apr 23 20:21:34 1998
+++ linux/drivers/sbus/char/Makefile	Fri Jul 10 15:18:31 1998
@@ -50,7 +50,9 @@
 #endif
 
 O_TARGET := sunchar.o
+ifneq ($(CONFIG_FB),y)
 O_OBJ    := ${FB_OBJS} suncons.o sbuscons.o pcicons.o sunfb.o
+endif
 O_OBJS   := ${O_OBJ} sunkbd.o sunkbdmap.o sunmouse.o sunserial.o zs.o
 M_OBJS   :=
 
@@ -122,8 +124,10 @@
 
 # Add PCI console/fb drivers here.
 #
+ifneq ($(CONFIG_FB),y)
 ifeq ($(CONFIG_PCI),y)
 O_OBJS += mach64.o
+endif
 endif
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.108/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h
--- v2.1.108/linux/drivers/scsi/ncr53c8xx.h	Wed Jul  1 19:38:55 1998
+++ linux/drivers/scsi/ncr53c8xx.h	Thu Jul 16 15:12:04 1998
@@ -42,6 +42,8 @@
 #ifndef NCR53C8XX_H
 #define NCR53C8XX_H
 
+#include <linux/config.h>
+
 #if 0
 #define CONFIG_SCSI_NCR53C8XX_PROFILE
 #endif
@@ -62,7 +64,6 @@
 #if !defined(LINUX_VERSION_CODE)
 #include <linux/version.h>
 #endif
-#include <linux/config.h>
 
 #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
 
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/CHANGELOG linux/drivers/sound/CHANGELOG
--- v2.1.108/linux/drivers/sound/CHANGELOG	Sat Nov 29 10:33:20 1997
+++ linux/drivers/sound/CHANGELOG	Fri Jul 10 14:03:36 1998
@@ -41,7 +41,7 @@
 
 Since 3.5
 - Changes almost everywhere.
-- Support for OPTi 82C924 based soundcards.
+- Support for OPTi 82C924-based sound cards.
 
 Since 3.5.4-beta8
 - Fixed a bug in handling of non-fragment sized writes in 16 bit/stereo mode
@@ -79,7 +79,7 @@
 - Added the "lowlevel" subdirectory for additional low level drivers that
   are not part of USS core. See lowlevel/README for more info.
 - Included support for ACI mixer (by Markus Kuhn). ACI is a mixer used in
-  miroPCM soundcards. See lowlevel/aci.readme for more info.
+  miroPCM sound cards. See lowlevel/aci.readme for more info.
 - Support for Aztech Washington chipset (AZT2316 ASIC).
 
 Since 3.5.4-beta1
@@ -271,7 +271,7 @@
   GUS MAX, but it doesn't work yet.
 Since pre-3.0-940426
 - AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc).
-This codec chip is used in various soundcards. This version is developed
+This codec chip is used in various sound cards. This version is developed
 for the 16 bit daughtercard of GUS. It should work with other cards also
 if the following requirements are met:
 	- The I/O, IRQ and DMA settings are jumper selectable or
@@ -336,7 +336,7 @@
 - Support for the MPU emulation of the SB16.
 - Some big arrays are now allocated boot time. This makes the BSS segment
   smaller which makes it possible to use the full driver with
-  NetBSD. These arrays are not allocated if no suitable soundcard is available.
+  NetBSD. These arrays are not allocated if no suitable sound card is available.
 - Fixed a bug in the compute_and_set_volume in gus_wave.c
 - Fixed the too fast mono playback problem of SB Pro and PAS16.
 
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.1.108/linux/drivers/sound/Config.in	Tue Jun 23 10:01:24 1998
+++ linux/drivers/sound/Config.in	Fri Jul 10 14:03:36 1998
@@ -9,9 +9,37 @@
 
 # Prompt user for primary drivers.
 
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  dep_tristate 'Ensoniq AudioPCI (ES1370)' CONFIG_SOUND_ES1370 $CONFIG_SOUND
+  dep_tristate 'Creative Ensoniq AudioPCI 97 (ES1371)' CONFIG_SOUND_ES1371 $CONFIG_SOUND
+  dep_tristate 'S3 SonicVibes' CONFIG_SOUND_SONICVIBES $CONFIG_SOUND
+fi
+
+dep_tristate 'Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_MSNDCLAS $CONFIG_SOUND
+if [ "$CONFIG_SOUND_MSNDCLAS" = "y" -o "$CONFIG_SOUND_MSNDCLAS" = "m" ]; then
+   string '  Full pathname of MSNDINIT.BIN firmware file' CONFIG_MSNDCLAS_INIT_FILE "/etc/sound/msndinit.bin"
+   string '  Full pathname of MSNDPERM.BIN firmware file' CONFIG_MSNDCLAS_PERM_FILE "/etc/sound/msndperm.bin"
+fi
+if [ "$CONFIG_SOUND_MSNDCLAS" = "y" ]; then
+   int 'MSND Classic IRQ 5,7,9,10,11,12' CONFIG_MSNDCLAS_IRQ 5
+   hex 'MSND Classic Memory B0000,C8000,D0000,D8000,E0000,E8000' CONFIG_MSNDCLAS_MEM D0000
+   hex 'MSND Classic I/O 210,220,230,240,250,260,290,3E0' CONFIG_MSNDCLAS_IO 290
+fi
+
+dep_tristate 'Support for Turtle Beach MultiSound Pinnacle, Fiji' CONFIG_SOUND_MSNDPIN $CONFIG_SOUND
+if [ "$CONFIG_SOUND_MSNDPIN" = "y" -o "$CONFIG_SOUND_MSNDPIN" = "m" ]; then
+   string '  Full pathname of PNDSPINI.BIN firmware file' CONFIG_MSNDPIN_INIT_FILE "/etc/sound/pndspini.bin"
+   string '  Full pathname of PNDSPERM.BIN firmware file' CONFIG_MSNDPIN_PERM_FILE "/etc/sound/pndsperm.bin"
+fi
+if [ "$CONFIG_SOUND_MSNDPIN" = "y" ]; then
+   int 'MSND Pinnacle IRQ 5,7,9,10,11,12' CONFIG_MSNDPIN_IRQ 5
+   hex 'MSND Pinnacle Memory B0000,C8000,D0000,D8000,E0000,E8000' CONFIG_MSNDPIN_MEM D0000
+   hex 'MSND Pinnacle I/O 210,220,230,240,250,260,290,3E0' CONFIG_MSNDPIN_IO 290
+fi
+
 dep_tristate 'OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND
 
-if [ "$CONFIG_SOUND_OSS" != "n" ]; then
+if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then
   dep_tristate 'ProAudioSpectrum 16 support' CONFIG_SOUND_PAS $CONFIG_SOUND_OSS
   if [ "$CONFIG_SOUND_PAS" = "y" ]; then
       int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' CONFIG_PAS_IRQ 10
@@ -63,7 +91,7 @@
       int 'PSS audio IRQ 7, 9, 10 or 11' CONFIG_PSS_MSS_IRQ 11
       int 'PSS audio DMA 0, 1 or 3' CONFIG_PSS_MSS_DMA 3
       hex 'PSS MIDI I/O base ' CONFIG_PSS_MPU_BASE 330
-      int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' CONFIG_PSS_MPU_IRQ 9
+      int 'PSS MIDI IRQ 3, 4, 5, 7, 9, 10, 11, 12' CONFIG_PSS_MPU_IRQ 9
       bool '  Have DSPxxx.LD firmware file' CONFIG_PSS_HAVE_BOOT
       if [ "$CONFIG_PSS_HAVE_BOOT" = "y" ]; then
          string '  Full pathname of DSPxxx.LD firmware file' CONFIG_PSS_BOOT_FILE
@@ -108,7 +136,9 @@
   fi
   
   dep_tristate 'Support for OPTi MAD16 and/or Mozart based cards' CONFIG_SOUND_MAD16 $CONFIG_SOUND_OSS
-  dep_tristate 'Support MIDI in older MAD16 based cards (requires SB)' CONFIG_MAD16_OLDCARD $CONFIG_SOUND_MAD16
+  if [ "$CONFIG_SOUND_MAD16" = "y" -o "$CONFIG_SOUND_MAD16" = "m" ]; then
+    bool 'Support MIDI in older MAD16 based cards (requires SB)' CONFIG_MAD16_OLDCARD
+  fi
   if [ "$CONFIG_SOUND_MAD16" = "y" ]; then
       hex 'MAD16 audio I/O base 530, 604, E80 or F40' CONFIG_MAD16_BASE 530
       int 'MAD16 audio IRQ 7, 9, 10 or 11' CONFIG_MAD16_IRQ 11
@@ -138,18 +168,6 @@
       fi
   fi
 
-  if [ "$CONFIG_SOUND" = "m" ]; then
-    dep_tristate 'Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_MSNDCLAS $CONFIG_SOUND
-    if [ "$CONFIG_SOUND_MSNDCLAS" = "m" ]; then
-       string '  Full pathname of MSNDINIT.BIN firmware file' CONFIG_MSNDCLAS_INIT_FILE
-       string '  Full pathname of MSNDPERM.BIN firmware file' CONFIG_MSNDCLAS_PERM_FILE
-    fi
-    dep_tristate 'Support for Turtle Beach MultiSound Pinnacle, Fiji' CONFIG_SOUND_MSNDPIN $CONFIG_SOUND
-    if [ "$CONFIG_SOUND_MSNDPIN" = "m" ]; then
-       string '  Full pathname of PNDSPINI.BIN firmware file' CONFIG_MSNDPIN_INIT_FILE
-       string '  Full pathname of PNDSPERM.BIN firmware file' CONFIG_MSNDPIN_PERM_FILE
-    fi
-  fi
   
   dep_tristate 'Support for Aztech Sound Galaxy (non-PnP) cards' CONFIG_SOUND_SGALAXY $CONFIG_SOUND_OSS
   if [ "$CONFIG_SOUND_SGALAXY" = "y" ]; then
@@ -197,7 +215,7 @@
   mainmenu_option next_comment
   comment 'Additional low level sound drivers'
   bool 'Additional low level sound drivers' CONFIG_LOWLEVEL_SOUND $CONFIG_SOUND_OSS
-  if [ "$CONFIG_LOWLEVEL_SOUND" != "n" ]; then
+  if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
       source drivers/sound/lowlevel/Config.in
   fi
   endmenu
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v2.1.108/linux/drivers/sound/Makefile	Tue Jun 23 10:01:24 1998
+++ linux/drivers/sound/Makefile	Fri Jul 10 14:03:36 1998
@@ -70,6 +70,11 @@
 obj-$(CONFIG_SOUND_YM3812)	+= adlib_card.o opl3.o
 obj-$(CONFIG_VIDC_SOUND)	+= vidc_mod.o
 
+#jnx
+obj-$(CONFIG_SOUND_ES1370)	+= es1370.o
+obj-$(CONFIG_SOUND_ES1371)	+= es1371.o
+obj-$(CONFIG_SOUND_SONICVIBES)	+= sonicvibes.o
+
 endif
 
 
@@ -221,7 +226,7 @@
 
 ifeq ($(CONFIG_PSS_HAVE_BOOT),y)
     pss_boot.h: $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) bin2hex
-	bin2hex pss_synth < $(CONFIG_PSS_BOOT_FILE) > $@
+	./bin2hex pss_synth < $(CONFIG_PSS_BOOT_FILE) > $@
 else
     pss_boot.h:
 	(							\
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/Readme linux/drivers/sound/Readme
--- v2.1.108/linux/drivers/sound/Readme	Tue Dec  9 09:49:59 1997
+++ linux/drivers/sound/Readme	Fri Jul 10 14:03:36 1998
@@ -26,13 +26,13 @@
 If you are looking for the installation instructions, please
 look at Readme.linux.
 
-Supported soundcards
---------------------
+Supported sound cards
+---------------------
 
 See Readme.cards.
 
 Please check http://www.opensound.com/ossfree if you don't find
-your soundcard there.
+your sound card there.
 
 Contributors
 ------------
@@ -127,9 +127,9 @@
 contain info about some recent bug fixes.
 
 It's likely that you have some problems when trying to use the sound driver
-first time. Soundcards don't have standard configuration so there are no
+first time. Sound cards don't have standard configuration so there are no
 good default configuration to use. Please try to use same I/O, DMA and IRQ
-values for the soundcard than with DOS.
+values for the sound card than with DOS.
 
 If you get an error message when trying to use the driver, please look
 at /var/adm/messages for more verbose error message.
@@ -159,7 +159,7 @@
 	work if "digitized voice support" was not enabled during "make config".
 	
 	- "Device or resource busy". Probably the IRQ (or DMA) channel 
-	required by the soundcard is in use by some other device/driver.
+	required by the sound card is in use by some other device/driver.
 
 	- "I/O error". Almost certainly (99%) it's an IRQ or DMA conflict.
 	Look at the kernel messages in /var/adm/notice for more info.
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/Readme.cards linux/drivers/sound/Readme.cards
--- v2.1.108/linux/drivers/sound/Readme.cards	Sun Jun  7 11:16:35 1998
+++ linux/drivers/sound/Readme.cards	Fri Jul 10 14:03:36 1998
@@ -1,7 +1,7 @@
-Configuring version 3.8 (for Linux) with some most common soundcards
-====================================================================
+Configuring version 3.8 (for Linux) with some common sound cards
+================================================================
 
-This document describes configuring soundcards with freeware version of
+This document describes configuring sound cards with the freeware version of
 Open Sound Systems (OSS/Free). Information about the commercial version
 (OSS/Linux) and its configuration is available from 
 http://www.opensound.com/linux.html. Information presented here is
@@ -9,7 +9,7 @@
 
 If you are unsure about how to configure OSS/Free
 you can download the free evaluation version of OSS/Linux from the above
-address. There is a chance that it can autodetect your soundcard. In this case
+address. There is a chance that it can autodetect your sound card. In this case
 you can use the information included in soundon.log when configuring OSS/Free.
 
 
@@ -24,8 +24,8 @@
 		offered by these programs are not necessarily valid.
 
 
-THE BIGGEST MISTAKES YOU CAN DO
-===============================
+THE BIGGEST MISTAKES YOU CAN MAKE
+=================================
 
 1. Assuming that the card is Sound Blaster compatible when it's not.
 --------------------------------------------------------------------
@@ -37,12 +37,12 @@
 Jazz16. Note that SB compatibility in DOS/Windows does _NOT_ mean anything
 in Linux. 
 
-IF YOU REALLY ARE 150% SURE YOU REALLY HAVE A SOUND BLASTER YOU CAN SKIP THE
-REST OF THIS CHAPTER.
+IF YOU REALLY ARE 150% SURE YOU HAVE A SOUND BLASTER YOU CAN SKIP THE REST OF
+THIS CHAPTER.
 
-For most other "supposed to be SB compatible" cards you have use other
-than SB drivers (see below). It is possible to get most soundcards to work
-in SB mode but in general it's complete waste of time. There are several
+For most other "supposed to be SB compatible" cards you have to use other
+than SB drivers (see below).  It is possible to get most sound cards to work
+in SB mode but in general it's a complete waste of time. There are several
 problems which you will encounter by using SB mode with cards that are not
 truly SB compatible:
 
@@ -53,7 +53,7 @@
 In addition some applications require 16 bit mode and they produce just
 noise with a 8 bit only device.
 - The card may work only in some cases but refuse to work most of the
-time. The SB compatible mode always requires special intialization which is 
+time. The SB compatible mode always requires special initialization which is 
 done by the DOS/Windows drivers. This kind of cards work in Linux after
 you have warm booted it after DOS but they don't work after cold boot
 (power on or reset).
@@ -64,8 +64,8 @@
 few other reasons to the DMA timeout message but using the SB mode seems
 to be the most common cause.
 
-2. Trying to use a PnP (Plug & Play) card just like an ordinary soundcard
--------------------------------------------------------------------------
+2. Trying to use a PnP (Plug & Play) card just like an ordinary sound card
+--------------------------------------------------------------------------
 
 Plug & Play is a protocol defined by Intel and Microsoft. It lets operating
 systems to easily identify and reconfigure I/O ports, IRQs and DMAs of ISA
@@ -74,39 +74,39 @@
 to use some special tricks (see later) to get a PnP card alive. Many PnP cards
 work after they have been initialized but this is not always the case.
 
-There are usually both a non PnP and PnP versions of the same soundcard.
-The non PnP version is the original model which usually has been discontinued
-more than an year ago. The PnP version has the same name but with a PnP
-appended to it (sometimes not). This causes major confusion since even the
-non PnP model works with Linux the PnP one doesn't.
+There are sometimes both PnP and non-PnP versions of the same sound card.
+The non-PnP version is the original model which usually has been discontinued
+more than an year ago. The PnP version has the same name but with "PnP"
+appended to it (sometimes not). This causes major confusion since the non-PnP
+model works with Linux but the PnP one doesn't.
 
 You should carefully check if "Plug & Play" or "PnP" is mentioned in the name
 of the card or in the documentation or package that came with the card. 
-Everything described in the rest of this document is not necessarily valid
-for PnP models of soudcards even you have managed to wake up the card properly.
-Many PnP cards are simply too much different than their original non PnP
-ancestors which are covered by this document.
+Everything described in the rest of this document is not necessarily valid for
+PnP models of sound cards even you have managed to wake up the card properly.
+Many PnP cards are simply too different from their non-PnP ancestors which are
+covered by this document.
 
 
 Cards that are not (fully) supported by this driver
 ===================================================
 
-See http://www.opensound.com/ossfree for information about soundcards 
+See http://www.opensound.com/ossfree for information about sound cards 
 to be supported in future. 
 
 
 How to use sound without recompiling kernel and/or sound driver
 ===============================================================
 
-There is commercial sound driver which should be released during Apr 96.
-It comes in precompiled form and doesn't require recompiling of kernel. See
-http://www.4Front-tech.com/oss.html for more info.
+There is a commercial sound driver which comes in precompiled form and doesn't
+require recompiling of the kernel. See http://www.4Front-tech.com/oss.html for
+more info.
 
 
 Configuring PnP cards
 =====================
 
-New versions of most soundcards use so called ISA PnP protocol for
+New versions of most sound cards use the so-called ISA PnP protocol for
 soft configuring their I/O, IRQ, DMA and shared memory resources.
 Currently at least cards made by Creative Technology (SB32 and SB32AWE
 PnP), Gravis (GUS PnP and GUS PnP Pro), Ensoniq (Soundscape PnP) and
@@ -115,7 +115,7 @@
 motherboards) is also based on PnP technology but there is a "native" driver
 available for it (see information about CS4232 later in this document).
 
-PnP soundcards (as well as most other PnP ISA cards) are not supported
+PnP sound cards (as well as most other PnP ISA cards) are not supported
 by this version of the driver . Proper
 support for them should be released during 97 once the kernel level
 PnP support is available.
@@ -123,36 +123,34 @@
 There is a method to get most of the PnP cards to work. The basic method
 is the following:
 
-1) Boot DOS so that card's DOS drivers have chance to initialize the
-card.
-2) _Cold_ boot to Linux by using "loadlin.exe". Hitting ctrl-alt-del
-works with older machines but causes hard reset of all cards on latest
+1) Boot DOS so the card's DOS drivers have a chance to initialize it.
+2) _Cold_ boot to Linux by using "loadlin.exe".  Hitting ctrl-alt-del
+works with older machines but causes a hard reset of all cards on recent
 (Pentium) machines.
-3) If you have sound driver in Linux configured properly, the card should work
-now. "Proper" means here that I/O, IRQ and DMA settings are the same than in
-DOS. The hard part is to find which settings were used. See documentation of
+3) If you have the sound driver in Linux configured properly, the card should
+work now. "Proper" means that I/O, IRQ and DMA settings are the same as in
+DOS. The hard part is to find which settings were used. See the documentation of
 your card for more info.
 
-Windows 95 could work as well as DOS but running loadlin may be somehow 
-difficult. Probably you should "shut down" your machine to MS-DOS mode
-before running it.
+Windows 95 could work as well as DOS but running loadlin may be difficult.
+Probably you should "shut down" your machine to MS-DOS mode before running it.
 
-Some machines have BIOS utility for setting PnP resources. This is a good
+Some machines have a BIOS utility for setting PnP resources. This is a good
 way to configure some cards. In this case you don't need to boot DOS/Win95
-prior starting Linux.
+before starting Linux.
 
 Another way to initialize PnP cards without DOS/Win95 is a Linux based
 PnP isolation tool. When writing this there is a pre alpha test version
-of such tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The
+of such a tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The
 file is called isapnptools-*. Please note that this tool is just a temporary
 solution which may be incompatible with future kernel versions having proper
 support for PnP cards. There are bugs in setting DMA channels in earlier
-versions of isapnptools so at least version 1.6 is required with soundcards.
+versions of isapnptools so at least version 1.6 is required with sound cards.
 
-Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers.
-See http://www.opensound.com/linux.html for more info. This is the way
-you probably like to do it if you don't waste hours of time in recompiling
-kernel and the required tools.
+Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers.  See
+http://www.opensound.com/linux.html for more info. This is probably the way you
+should do it if you don't want to spend time recompiling the kernel and 
+required tools.
 
 
 Read this before trying to configure the driver
@@ -188,7 +186,7 @@
 		      using isapnptools before they work with OSS/Free.
 	SB16 compatible cards by other manufacturers than Creative.
 		You have been fooled since there are _no_ SB16 compatible
-		cards on the market (May 97). It's likely that your card
+		cards on the market (as of May 1997). It's likely that your card
 		is compatible just with SB Pro but there is also a non-SB-
 		compatible 16 bit mode. Usually it's MSS/WSS but it could also
 		be a proprietary one like MV Jazz16 or ESS ES688. OPTi
@@ -206,7 +204,7 @@
 		Most other "16 bit SB compatible" cards such as "OPTi/MAD16" or
 		"Crystal" are _NOT_ SB compatible in Linux.
 
-		Practically all soundcards have some kind of SB emulation mode
+		Practically all sound cards have some kind of SB emulation mode
 		in addition to their native (16 bit) mode. In most cases this
 		(8 bit only) SB compatible mode doesn't work with Linux. If
 		you get it working it may cause problems with games and
@@ -231,7 +229,7 @@
 	The driver works both with the full (intelligent mode) MPU-401
 	cards (such as MPU IPC-T and MQX-32M) and with the UART only
 	dumb MIDI ports. MPU-401 is currently the most common MIDI
-	interface. Most soundcards are compatible with it. However,
+	interface. Most sound cards are compatible with it. However,
 	don't enable MPU401 mode blindly. Many cards with native support
 	in the driver have their own MPU401 driver. Enabling the standard one
 	will cause a conflict with these cards. So check if your card is
@@ -242,7 +240,7 @@
 	they managed to make it a standard. MSS compatible cards are based on 
 	a codec chip which is easily available from at least two manufacturers
 	(AD1848 by Analog Devices and CS4231/CS4248 by Crystal Semiconductor).
-	Currently most soundcards are based on one of the MSS compatible codec
+	Currently most sound cards are based on one of the MSS compatible codec
 	chips. The CS4231 is used in the high quality cards such as GUS MAX,
 	MediaTrix AudioTrix Pro and TB Tropez (GUS MAX is not MSS compatible). 
 
@@ -256,7 +254,7 @@
 	enabling the MSS support.
 
 Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4)
-	Most soundcards have a FM synthesizer chip. The OPL2 is a 2
+	Most sound cards have a FM synthesizer chip. The OPL2 is a 2
 	operator chip used in the original AdLib card. Currently it's used
 	only in the cheapest (8 bit mono) cards. The OPL3 is a 4 operator 
 	FM chip which provides better sound quality and/or more available 
@@ -280,7 +278,7 @@
 Yamaha OPL3-SA1
 
 	Yamaha OPL3-SA1 (YMF701) is an audio controller chip used on some
-	(Intel) motherboards and on cheap soundcards. It should not be
+	(Intel) motherboards and on cheap sound cards. It should not be
 	confused with the original OPL3 chip (YMF278) which is entirely
         different chip. OPL3-SA1 has support for MSS, MPU401 and SB Pro
 	(not used in OSS/Free) in addition to the OPL3 FM synth.
@@ -293,7 +291,7 @@
 4Front Technologies SoftOSS
 
 	SoftOSS is a software based wave table emulation which works with
-	any 16 bit stereo soundcard. Due to its nature a fast CPU is
+	any 16 bit stereo sound card. Due to its nature a fast CPU is
 	required (P133 is minimum). Although SoftOSS does _not_ use MMX
 	instructions it has proven out that recent processors (which appear
 	to have MMX) perform significantly better with SoftOSS than earlier
@@ -314,8 +312,8 @@
 	higher sampling rates. However using fewer voices decreases
 	playback quality more than decreasing the sampling rate.
 
-	SoftOSS keeps the samples loaded on system's RAM so large RAM is
-	required. SoftOSS should never be used on machines with less than 16M
+	SoftOSS keeps the samples loaded on the system's RAM so much RAM is
+	required. SoftOSS should never be used on machines with less than 16 MB
 	of RAM since this is potentially dangerous (you may accidently run out
 	of memory which probably crashes the machine). 
 
@@ -330,14 +328,14 @@
 	sites.
 
         *********************************************************************
-	IMPORTANT NOTICE! The original patch set distributed with Gravis 
+	IMPORTANT NOTICE! The original patch set distributed with the Gravis 
 	Ultrasound card is not in public domain (even though it's available from
-	some ftp sites). You should contact Voice Crystal (www.voicecrystal.com)
+	some FTP sites). You should contact Voice Crystal (www.voicecrystal.com)
 	if you like to use these patches with SoftOSS included in OSS/Free.
         *********************************************************************
 
 PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC)
-	Analog Devices and Echo Speech have together defined a soundcard
+	Analog Devices and Echo Speech have together defined a sound card
 	architecture based on the above chips. The DSP chip is used
 	for emulation of SB Pro, FM and General MIDI/MT32.
 
@@ -355,19 +353,20 @@
 	General MIDI emulator. There is also a SB 1.5 compatible playback mode.
 
 Ensoniq SoundScape and compatibles
-	Ensoniq has designed a soundcard architecture based on the
+	Ensoniq has designed a sound card architecture based on the
 	OTTO synthesizer chip used in their professional MIDI synthesizers.
 	Several companies (including Ensoniq, Reveal and Spea) are selling
 	cards based on this architecture.
 
 	NOTE! The SoundScape PnP is not supported by OSS/Free. Ensoniq VIVO and
-	VIVO90 cards are not compatible with Soundscapes so the Soundscape driver
-	will not work with them. You may want to use OSS/Linux with these cards.
+	VIVO90 cards are not compatible with Soundscapes so the Soundscape
+        driver will not work with them. You may want to use OSS/Linux with these
+        cards.
 
 OPTi MAD16 and Mozart based cards
 	The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929),
 	OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface
-	chips are used in many different soundcards, including some
+	chips are used in many different sound cards, including some
 	cards by Reveal miro and Turtle Beach (Tropez). The purpose of these
 	chips is to connect other audio components to the PC bus. The
 	interface chip performs address decoding for the other chips.
@@ -394,8 +393,8 @@
 	SB and MPU401 emulations. There is support for OPL3 too.
 	Unfortunately the MPU401 mode doesn't work (I don't know how to
 	initialize it). CS4236 is an enhanced (compatible) version of CS4232.
-	NOTE! Don't ever try to use isapnptools with CS4232 since this just
-	freezes your machine (due to chip bugs). If you have problems in getting
+	NOTE! Don't ever try to use isapnptools with CS4232 since this will just
+	freeze your machine (due to chip bugs). If you have problems in getting
 	CS4232 working you could try initializing it with DOS (CS4232C.EXE) and
 	then booting Linux using loadlin. CS4232C.EXE loads a secret firmware
 	patch which is not documented by Crystal.
@@ -413,7 +412,7 @@
 Jumpers and software configuration
 ==================================
 
-Some of the earliest soundcards were jumper configurable. You have to
+Some of the earliest sound cards were jumper configurable. You have to
 configure the driver use I/O, IRQ and DMA settings
 that match the jumpers. Just few 8 bit cards are fully jumper 
 configurable (SB 1.x/2.x, SB Pro and clones).
@@ -436,7 +435,7 @@
 Sound driver sets the soft configurable parameters of the card automatically
 during boot. Usually you don't need to run any extra initialization
 programs when booting Linux but there are some exceptions. See the
-card specific instructions (below) for more info.
+card-specific instructions below for more info.
 
 The drawback of software configuration is that the driver needs to know
 how the card must be initialized. It cannot initialize unknown cards
@@ -448,7 +447,7 @@
 =======================================
 
 The first thing to do is to look at the major IC chips on the card.
-Many of the latest soundcards are based on some standard chips. If you
+Many of the latest sound cards are based on some standard chips. If you
 are lucky, all of them could be supported by the driver. The most common ones
 are the OPTi MAD16, Mozart, SoundScape (Ensoniq) and the PSS architectures
 listed above. Also look at the end of this file for list of unsupported
@@ -458,21 +457,21 @@
 to me together with a list of the major IC chips (manufactured, model) to 
 me. I could then try to check if your card looks like something familiar.
 
-There are much more cards in the word than listed above. The first thing to
-do with these cards is to check if they emulate some other card/interface
+There are many more cards in the world than listed above. The first thing to
+do with these cards is to check if they emulate some other card or interface
 such as SB, MSS and/or MPU401. In this case there is a chance to get the
 card to work by booting DOS before starting Linux (boot DOS, hit ctrl-alt-del
 and boot Linux without hard resetting the machine). In this method the
-DOS based driver initializes the hardware to use a known I/O, IRQ and DMA
-settings. If sound driver is configured to use the same settings, everything should 
-work OK.
+DOS based driver initializes the hardware to use known I/O, IRQ and DMA
+settings. If sound driver is configured to use the same settings, everything
+should work OK.
 
 
 Configuring sound driver (with Linux)
 =====================================
 
-Sound driver is currently a part of Linux kernel distribution. The 
-driver files are located in directory /usr/src/linux/drivers/sound. 
+The sound driver is currently distributed as part of the Linux kernel. The 
+files are in /usr/src/linux/drivers/sound/. 
 
 ****************************************************************************
 *	ALWAYS USE THE SOUND DRIVER VERSION WHICH IS DISTRIBUTED WITH	   *
@@ -491,9 +490,9 @@
 ****************************************************************************
 
 To configure the driver, run "make config" in the kernel source directory
-(/usr/src/linux). Answer y to the question about Sound card support (after
-questions about mouse, CD-ROM, ftape, etc. supports). Sound config options
-will then be asked after some additional questions.
+(/usr/src/linux). Answer "y" or "m" to the question about Sound card support
+(after the questions about mouse, CD-ROM, ftape, etc. support).  Questions
+about options for sound will then be asked.
 
 After configuring the kernel and sound driver, run "make dep" and compile
 the kernel following instructions in the kernel README.
@@ -501,9 +500,9 @@
 The sound driver configuration dialog
 -------------------------------------
 
-If you already have the sound driver installed, consult printout of
+If you already have the sound driver installed, consult a printout of
 "cat /dev/sndstat" when configuring the driver again. It gives the I/O,
-IRQ and DMA settings you have used earlier.
+IRQ and DMA settings you used earlier.
 
 Sound configuration starts by making some yes/no questions. Be careful
 when answering to these questions since answering y to a question may
@@ -559,7 +558,7 @@
 	  know what to answer with it.
   "MPU-401 support (NOT for SB16)",
 	- Be careful with this question. The MPU401 interface is supported
-	  by almost any soundcard today. However some natively supported cards
+	  by almost any sound card today. However some natively supported cards
 	  have their own driver for MPU401. Enabling the MPU401 option with
 	  these cards will cause a conflict. Also enabling MPU401 on a system
 	  that doesn't really have a MPU401 could cause some trouble. If your
@@ -569,7 +568,7 @@
 	  In MOST cases this MPU401 driver should only be used with "true"
 	  MIDI-only MPU401 professional cards. In most other cases there
 	  is another way to get the MPU401 compatible interface of a
-	  soundcard to work.
+	  sound card to work.
 	  Support for the MPU401 compatible MIDI port of SB16, ESS1688
 	  and MV Jazz16 cards is included in the SB driver. Use it instead
 	  of this separate MPU401 driver with these cards. As well 
@@ -607,9 +606,9 @@
 	  channels with cards that don't support this feature will prevent
 	  audio (at least recording) from working.
   "Ensoniq Soundscape support",
-	- Answer 'y' if you have a soundcard based on the Ensoniq SoundScape
+	- Answer 'y' if you have a sound card based on the Ensoniq SoundScape
 	  chipset. Such cards are being manufactured at least by Ensoniq,
-	  Spea and Reveal (note that Reveal makes other cards also). Oldest
+	  Spea and Reveal (note that Reveal makes other cards also).  The oldest
 	  cards made by Spea don't work properly with Linux. 
 	  Soundscape PnP as well as Ensoniq VIVO work only with the commercial
 	  OSS/Linux version.
@@ -695,7 +694,7 @@
 SB2_BASE, SB2_IRQ, SB2_DMA and SB2_DMA2 for the second one. You can't get
 the OPL3, MIDI and EMU8000 devices of the second card to work. If you are
 going to use two PnP Sound Blasters, ensure that they are of different model
-and have different PnP ID's. There is no way to get two cards with the same
+and have different PnP IDs. There is no way to get two cards with the same
 card ID and serial number to work. The easiest way to check this is trying 
 if isapnptools can see both cards or just one.
 
@@ -710,7 +709,7 @@
 16 bit mode which is not SB16 compatible. The most likely alternative is that
 the 16 bit mode means MSS/WSS.
 
-There are just few fully 100% hardware SB or SB Pro compatible cards.
+There are just a few fully 100% hardware SB or SB Pro compatible cards.
 I know just Thunderboard and SM Games. Other cards require some kind of
 hardware initialization before they become SB compatible. Check if your card
 was listed in the beginning of this file. In this case you should follow
@@ -1004,7 +1003,7 @@
 
 Read the above MV Jazz specific instructions first.
 
-The Logitech SoundMan Wave (don't confuse with the SM16 or SM Games) is
+The Logitech SoundMan Wave (don't confuse this with the SM16 or SM Games) is
 a MV Jazz based card which has an additional OPL4 based wave table
 synthesizer. The OPL4 chip is handled by an on board microcontroller
 which must be initialized during boot. The config program asks if
@@ -1053,7 +1052,7 @@
 NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support
 of OSS doesn't work with it.
 
-There are some ES1688/688 based soundcards and (particularily) motherboards
+There are some ES1688/688 based sound cards and (particularily) motherboards
 which use software configurable I/O port relocation feature of the chip.
 This ESS proprietary feature is supported only by OSS/Linux.
 
@@ -1070,12 +1069,12 @@
 
 There are several different cards made/marketed by Reveal. Some of them
 are compatible with SoundScape and some use the MAD16 chip. You may have
-to look at the card and try to identify origin of the card.
+to look at the card and try to identify its origin.
 
 Diamond
 -------
 
-The oldest (Sierra Aria based) soundcards made by Diamond are not supported
+The oldest (Sierra Aria based) sound cards made by Diamond are not supported
 (they may work if the card is initialized using DOS). The recent (LX?)
 models are based on the MAD16 chip which is supported by the driver.
 
@@ -1150,7 +1149,7 @@
 Others?
 -------
 
-Since there are so many different soundcards, it's likely that I have 
+Since there are so many different sound cards, it's likely that I have 
 forgotten to mention many of them. Please inform me if you know yet another
 card which works with Linux, please inform me (or is anybody else
 willing to maintain a database of supported cards (just like in XF86)?).
@@ -1158,18 +1157,18 @@
 Cards not supported yet
 =======================
 
-Please check which version of sound driver you are using before 
-complaining that your card is not supported. It's possible that you are 
+Please check the version of sound driver you are using before 
+complaining that your card is not supported. It's possible you are 
 using a driver version which was released months before your card was
-introduced. Driver's release date is listed after its version number
-in "cat /dev/sndstat" printout and in file linux/drivers/sound/soundvers.h.
+introduced.  The driver's release date is listed after its version number in a
+"cat /dev/sndstat" printout and in the file linux/drivers/sound/soundvers.h.
 
-First of all. There is an easy way to make most soundcards to work
-with Linux. Just use the DOS based driver to initialize the card
-to a _known_ state. Then use loadlin.exe to boot Linux. If Linux is configured
-to use the same I/O, IRQ and DMA numbers than DOS, the card could work.
+First of all, there is an easy way to make most sound cards work with Linux.
+Just use the DOS based driver to initialize the card to a known state, then use
+loadlin.exe to boot Linux. If Linux is configured to use the same I/O, IRQ and
+DMA numbers as DOS, the card could work.
 (ctrl-alt-del can be used in place of loadlin.exe but it doesn't work with
-new motherboards). This method works also with all/most PnP soundcards.
+new motherboards). This method works also with all/most PnP sound cards.
 
 Don't get fooled with SB compatibility. Most cards are compatible with
 SB but that may require a TSR which is not possible with Linux. If
@@ -1178,7 +1177,7 @@
 
 Then there are cards which are no longer manufactured and/or which
 are relatively rarely used (such as the 8 bit ProAudioSpectrum
-models). It's extremely unlikely that such cards never get supported.
+models). It's extremely unlikely that such cards ever get supported.
 Adding support for a new card requires much work and increases time
 required in maintaining the driver (some changes need to be done
 to all low level drivers and be tested too, maybe with multiple
@@ -1213,8 +1212,8 @@
 at the home page (http://www.opensound.com/ossfree/new_cards.html)
 for latest info.
 
-Information about unsupported soundcards and chipsets is welcome as well
-as free copies of soundcards, SDKs and operating systems.
+Information about unsupported sound cards and chipsets is welcome as well
+as free copies of sound cards, SDKs and operating systems.
 
 If you have any corrections and/or comments, please contact me.
 
@@ -1222,7 +1221,7 @@
 hannu@opensound.com
 
 Personal home page:	   http://www.compusonic.fi/~hannu
-www home page of OSS/Free: http://www.opensound.com/ossfree
+home page of OSS/Free: http://www.opensound.com/ossfree
 
-www home page of commercial OSS
+home page of commercial OSS
 (Open Sound System) drivers: http://www.opensound.com/oss.html
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/Readme.modules linux/drivers/sound/Readme.modules
--- v2.1.108/linux/drivers/sound/Readme.modules	Sun Jun  7 11:16:35 1998
+++ linux/drivers/sound/Readme.modules	Tue Jul 14 10:24:01 1998
@@ -29,7 +29,7 @@
  Then, add to your /etc/modules.conf or /etc/conf.modules something like:
 
 alias char-major-14 sb
-post-install sb modprobe  "-k" "adlib_card"
+post-install sb /sbin/modprobe "-k" "adlib_card"
 options sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330
 options adlib_card io=0x388     # FM synthesizer
 
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v2.1.108/linux/drivers/sound/ad1848.c	Sun Jun  7 11:16:35 1998
+++ linux/drivers/sound/ad1848.c	Fri Jul 10 14:03:36 1998
@@ -2280,7 +2280,10 @@
 		hw_config->card_subtype = 1;
 		return 1;
 	}
-	if (hw_config->irq > 11)
+	if ((hw_config->irq != 7)  &&
+	    (hw_config->irq != 9)  &&
+	    (hw_config->irq != 10) &&
+	    (hw_config->irq != 11))
 	{
 		printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq);
 		return 0;
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/ad1848_mixer.h linux/drivers/sound/ad1848_mixer.h
--- v2.1.108/linux/drivers/sound/ad1848_mixer.h	Wed Apr  8 19:36:27 1998
+++ linux/drivers/sound/ad1848_mixer.h	Fri Jul 10 14:03:36 1998
@@ -15,7 +15,7 @@
 
 /*
  * The AD1848 codec has generic input lines called Line, Aux1 and Aux2.
- * Soundcard manufacturers have connected actual inputs (CD, synth, line,
+ * Sound card manufacturers have connected actual inputs (CD, synth, line,
  * etc) to these inputs in different order. Therefore it's difficult
  * to assign mixer channels to to these inputs correctly. The following
  * contains two alternative mappings. The first one is for GUS MAX and
@@ -71,7 +71,7 @@
  * Most of the mixer entries work in backwards. Setting the polarity field
  * makes them to work correctly.
  *
- * The channel numbering used by individual soundcards is not fixed. Some
+ * The channel numbering used by individual sound cards is not fixed. Some
  * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs.
  * The current version doesn't try to compensate this.
  */
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/coproc.h linux/drivers/sound/coproc.h
--- v2.1.108/linux/drivers/sound/coproc.h	Tue Sep 30 08:46:44 1997
+++ linux/drivers/sound/coproc.h	Fri Jul 10 14:03:36 1998
@@ -1,5 +1,5 @@
 /*
- * Definitions for various on board processors on the soundcards. For
+ * Definitions for various on board processors on the sound cards. For
  * example DSP processors.
  */
 
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/cs4232.c linux/drivers/sound/cs4232.c
--- v2.1.108/linux/drivers/sound/cs4232.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/cs4232.c	Fri Jul 10 14:03:36 1998
@@ -34,8 +34,6 @@
 #include "sound_config.h"
 #include "soundmodule.h"
 
-#ifdef CONFIG_CS4232
-
 #define KEY_PORT	0x279	/* Same as LPT1 status port */
 #define CSN_NUM		0x99	/* Just a random number */
 
@@ -108,7 +106,7 @@
 	 * method conflicts with possible PnP support in the OS. For this reason 
 	 * driver is just a temporary kludge.
 	 *
-	 * Also the Cirrus/Crystal method doesnt always work. Try ISAPnP first ;)
+	 * Also the Cirrus/Crystal method doesnt always work. Try ISA PnP first ;)
 	 */
 
 	/*
@@ -340,5 +338,4 @@
 	SOUND_LOCK_END;
 }
 
-#endif
 #endif
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/dev_table.h linux/drivers/sound/dev_table.h
--- v2.1.108/linux/drivers/sound/dev_table.h	Sun Jun  7 11:16:35 1998
+++ linux/drivers/sound/dev_table.h	Fri Jul 10 14:03:36 1998
@@ -159,7 +159,7 @@
 
 /*
  * Structure for use with various microcontrollers and DSP processors 
- * in the recent soundcards.
+ * in the recent sound cards.
  */
 typedef struct coproc_operations 
 {
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/dm.h linux/drivers/sound/dm.h
--- v2.1.108/linux/drivers/sound/dm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/dm.h	Fri Jul 10 14:03:36 1998
@@ -0,0 +1,79 @@
+#ifndef _DRIVERS_SOUND_DM_H
+#define _DRIVERS_SOUND_DM_H
+
+/*
+ *	Definitions of the 'direct midi sound' interface used
+ *	by the newer commercial OSS package. We should export
+ *	this to userland somewhere in glibc later.
+ */
+
+/*
+ * Data structure composing an FM "note" or sound event.
+ */
+
+struct dm_fm_voice
+{
+	u8 op;
+	u8 voice;
+	u8 am;
+	u8 vibrato;
+	u8 do_sustain;
+	u8 kbd_scale;
+	u8 harmonic;
+	u8 scale_level;
+	u8 volume;
+	u8 attack;
+	u8 decay;
+	u8 sustain;
+	u8 release;
+	u8 feedback;
+	u8 connection;
+	u8 left;
+	u8 right;
+	u8 waveform;
+};
+
+/*
+ *	This describes an FM note by its voice, octave, frequency number (10bit)
+ *	and key on/off.
+ */
+
+struct dm_fm_note
+{
+	u8 voice;
+	u8 octave;
+	u32 fnum;
+	u8 key_on;
+};
+
+/*
+ * FM parameters that apply globally to all voices, and thus are not "notes"
+ */
+
+struct dm_fm_params
+{
+	u8 am_depth;
+	u8 vib_depth;
+	u8 kbd_split;
+	u8 rhythm;
+
+	/* This block is the percussion instrument data */
+	u8 bass;
+	u8 snare;
+	u8 tomtom;
+	u8 cymbal;
+	u8 hihat;
+};
+
+/*
+ *	FM mode ioctl settings
+ */
+ 
+#define FM_IOCTL_RESET        0x20
+#define FM_IOCTL_PLAY_NOTE    0x21
+#define FM_IOCTL_SET_VOICE    0x22
+#define FM_IOCTL_SET_PARAMS   0x23
+#define FM_IOCTL_SET_MODE     0x24
+#define FM_IOCTL_SET_OPL      0x25
+
+#endif
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- v2.1.108/linux/drivers/sound/dmabuf.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/dmabuf.c	Fri Jul 10 14:03:36 1998
@@ -765,6 +765,7 @@
 
 	offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP;
 	if (offs < 0 || offs >= dmap->bytes_in_use) {
+		restore_flags(flags);
 		printk(KERN_ERR "Sound: Got unexpected offs %ld. Giving up.\n", offs);
 		printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use);
 		return 0;
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/es1370.c linux/drivers/sound/es1370.c
--- v2.1.108/linux/drivers/sound/es1370.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/es1370.c	Fri Jul 10 14:03:36 1998
@@ -0,0 +1,2374 @@
+/*****************************************************************************/
+
+/*
+ *      es1370.c  --  Ensoniq ES1370/Ashai Kasei AK4531 audio driver.
+ *
+ *      Copyright (C) 1998  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Special thanks to David C. Niemi
+ *
+ *
+ * Module command line parameters:
+ *   joystick if 1 enables the joystick interface on the card; but it still
+ *            needs a separate joystick driver (presumably PC standard, although
+ *            the chip doc doesn't say anything and it looks slightly fishy from
+ *            the PCI standpoint...)
+ *   lineout  if 1 the LINE jack is used as an output instead of an input.
+ *            LINE then contains the unmixed dsp output. This can be used
+ *            to make the card a four channel one: use dsp to output two
+ *            channels to LINE and dac to output the other two channels to
+ *            SPKR. Set the mixer to only output synth to SPKR.
+ *   micz     it looks like this changes the MIC input impedance. I don't know
+ *            any detail though.
+ *
+ *  Note: sync mode is not yet supported (i.e. running dsp and dac from the same
+ *  clock source)
+ *
+ *  Supported devices:
+ *  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible
+ *  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
+ *  /dev/dsp1   additional DAC, like /dev/dsp, but output only,
+ *              only 5512, 11025, 22050 and 44100 samples/s,
+ *              outputs to mixer "SYNTH" setting
+ *  /dev/midi   simple MIDI UART interface, no ioctl
+ *
+ *  NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed
+ *  to be done in software. That is what /dev/dac is for. By now (Q2 1998)
+ *  there are several MIDI to PCM (WAV) packages, one of them is timidity.
+ *
+ *  Revision history
+ *    26.03.98   0.1   Initial release
+ *    31.03.98   0.2   Fix bug in GETOSPACE
+ *    04.04.98   0.3   Make it work (again) under 2.0.33
+ *                     Fix mixer write operation not returning the actual
+ *                     settings
+ *    05.04.98   0.4   First attempt at using the new PCI stuff
+ *    29.04.98   0.5   Fix hang when ^C is pressed on amp
+ *    07.05.98   0.6   Don't double lock around stop_*() in *_release()
+ *    10.05.98   0.7   First stab at a simple midi interface (no bells&whistles)
+ *    14.05.98   0.8   Don't allow excessive interrupt rates
+ *    08.06.98   0.9   First release using Alan Cox' soundcore instead of
+ *                     miscdevice
+ *    05.07.98   0.10  Fixed the driver to correctly maintin OSS style volume
+ *                     settings (not sure if this should be standard)
+ *                     Fixed many references: f_flags should be f_mode
+ *                     -- Gerald Britton <gbritton@mit.edu>
+ *
+ * some important things missing in Ensoniq documentation:
+ *
+ * Experimental PCLKDIV results:  play the same waveforms on both DAC1 and DAC2
+ * and vary PCLKDIV to obtain zero beat.
+ *  5512sps:  254
+ * 44100sps:   30
+ * seems to be fs = 1411200/(PCLKDIV+2)
+ *
+ * should find out when curr_sample_ct is cleared and
+ * where exactly the CCB fetches data
+ *
+ * The card uses a 22.5792 MHz crystal.
+ * The LINEIN jack may be converted to an AOUT jack by
+ * setting pin 47 (XCTL0) of the ES1370 to high.
+ * Pin 48 (XCTL1) of the ES1370 presumably changes the input impedance of the
+ * MIC jack.
+ *
+ */
+
+/*****************************************************************************/
+      
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/modversions.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/malloc.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <asm/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+
+/* --------------------------------------------------------------------- */
+
+#ifndef PCI_VENDOR_ID_ENSONIQ
+#define PCI_VENDOR_ID_ENSONIQ        0x1274    
+#endif
+#ifndef PCI_DEVICE_ID_ENSONIQ_ES1370
+#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000
+#endif
+
+#define ES1370_MAGIC  ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1370)
+
+#define ES1370_EXTENT             0x40
+#define JOY_EXTENT                8
+
+#define ES1370_REG_CONTROL        0x00
+#define ES1370_REG_STATUS         0x04
+#define ES1370_REG_UART_DATA      0x08
+#define ES1370_REG_UART_STATUS    0x09
+#define ES1370_REG_UART_CONTROL   0x09
+#define ES1370_REG_UART_TEST      0x0a
+#define ES1370_REG_MEMPAGE        0x0c
+#define ES1370_REG_CODEC          0x10
+#define ES1370_REG_SERIAL_CONTROL 0x20
+#define ES1370_REG_DAC1_SCOUNT    0x24
+#define ES1370_REG_DAC2_SCOUNT    0x28
+#define ES1370_REG_ADC_SCOUNT     0x2c
+
+#define ES1370_REG_DAC1_FRAMEADR  0xc30
+#define ES1370_REG_DAC1_FRAMECNT  0xc34
+#define ES1370_REG_DAC2_FRAMEADR  0xc38
+#define ES1370_REG_DAC2_FRAMECNT  0xc3c
+#define ES1370_REG_ADC_FRAMEADR   0xd30
+#define ES1370_REG_ADC_FRAMECNT   0xd34
+
+#define ES1370_FMT_U8_MONO     0
+#define ES1370_FMT_U8_STEREO   1
+#define ES1370_FMT_S16_MONO    2
+#define ES1370_FMT_S16_STEREO  3
+#define ES1370_FMT_STEREO      1
+#define ES1370_FMT_S16         2
+#define ES1370_FMT_MASK        3
+
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 };
+
+#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2)
+#define DAC2_DIVTOSR(x) (1411200/((x)+2))
+
+#define CTRL_ADC_STOP   0x80000000  /* 1 = ADC stopped */
+#define CTRL_XCTL1      0x40000000  /* ? mic impedance */
+#define CTRL_OPEN       0x20000000  /* no function, can be read and written */
+#define CTRL_PCLKDIV    0x1fff0000  /* ADC/DAC2 clock divider */
+#define CTRL_SH_PCLKDIV 16
+#define CTRL_MSFMTSEL   0x00008000  /* MPEG serial data fmt: 0 = Sony, 1 = I2S */
+#define CTRL_M_SBB      0x00004000  /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */
+#define CTRL_WTSRSEL    0x00003000  /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */
+#define CTRL_SH_WTSRSEL 12
+#define CTRL_DAC_SYNC   0x00000800  /* 1 = DAC2 runs off DAC1 clock */
+#define CTRL_CCB_INTRM  0x00000400  /* 1 = CCB "voice" ints enabled */
+#define CTRL_M_CB       0x00000200  /* recording source: 0 = ADC, 1 = MPEG */
+#define CTRL_XCTL0      0x00000100  /* 0 = Line in, 1 = Line out */
+#define CTRL_BREQ       0x00000080  /* 1 = test mode (internal mem test) */
+#define CTRL_DAC1_EN    0x00000040  /* enable DAC1 */
+#define CTRL_DAC2_EN    0x00000020  /* enable DAC2 */
+#define CTRL_ADC_EN     0x00000010  /* enable ADC */
+#define CTRL_UART_EN    0x00000008  /* enable MIDI uart */
+#define CTRL_JYSTK_EN   0x00000004  /* enable Joystick port (presumably at address 0x200) */
+#define CTRL_CDC_EN     0x00000002  /* enable serial (CODEC) interface */
+#define CTRL_SERR_DIS   0x00000001  /* 1 = disable PCI SERR signal */
+
+#define STAT_INTR       0x80000000  /* wired or of all interrupt bits */
+#define STAT_CSTAT      0x00000400  /* 1 = codec busy or codec write in progress */
+#define STAT_CBUSY      0x00000200  /* 1 = codec busy */
+#define STAT_CWRIP      0x00000100  /* 1 = codec write in progress */
+#define STAT_VC         0x00000060  /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */
+#define STAT_SH_VC      5
+#define STAT_MCCB       0x00000010  /* CCB int pending */
+#define STAT_UART       0x00000008  /* UART int pending */
+#define STAT_DAC1       0x00000004  /* DAC1 int pending */
+#define STAT_DAC2       0x00000002  /* DAC2 int pending */
+#define STAT_ADC        0x00000001  /* ADC int pending */
+
+#define USTAT_RXINT     0x80        /* UART rx int pending */
+#define USTAT_TXINT     0x04        /* UART tx int pending */
+#define USTAT_TXRDY     0x02        /* UART tx ready */
+#define USTAT_RXRDY     0x01        /* UART rx ready */
+
+#define UCTRL_RXINTEN   0x80        /* 1 = enable RX ints */
+#define UCTRL_TXINTEN   0x60        /* TX int enable field mask */
+#define UCTRL_ENA_TXINT 0x20        /* enable TX int */
+#define UCTRL_CNTRL     0x03        /* control field */
+#define UCTRL_CNTRL_SWR 0x03        /* software reset command */
+
+#define SCTRL_P2ENDINC    0x00380000  /*  */
+#define SCTRL_SH_P2ENDINC 19
+#define SCTRL_P2STINC     0x00070000  /*  */
+#define SCTRL_SH_P2STINC  16
+#define SCTRL_R1LOOPSEL   0x00008000  /* 0 = loop mode */
+#define SCTRL_P2LOOPSEL   0x00004000  /* 0 = loop mode */
+#define SCTRL_P1LOOPSEL   0x00002000  /* 0 = loop mode */
+#define SCTRL_P2PAUSE     0x00001000  /* 1 = pause mode */
+#define SCTRL_P1PAUSE     0x00000800  /* 1 = pause mode */
+#define SCTRL_R1INTEN     0x00000400  /* enable interrupt */
+#define SCTRL_P2INTEN     0x00000200  /* enable interrupt */
+#define SCTRL_P1INTEN     0x00000100  /* enable interrupt */
+#define SCTRL_P1SCTRLD    0x00000080  /* reload sample count register for DAC1 */
+#define SCTRL_P2DACSEN    0x00000040  /* 1 = DAC2 play back last sample when disabled */
+#define SCTRL_R1SEB       0x00000020  /* 1 = 16bit */
+#define SCTRL_R1SMB       0x00000010  /* 1 = stereo */
+#define SCTRL_R1FMT       0x00000030  /* format mask */
+#define SCTRL_SH_R1FMT    4
+#define SCTRL_P2SEB       0x00000008  /* 1 = 16bit */
+#define SCTRL_P2SMB       0x00000004  /* 1 = stereo */
+#define SCTRL_P2FMT       0x0000000c  /* format mask */
+#define SCTRL_SH_P2FMT    2
+#define SCTRL_P1SEB       0x00000002  /* 1 = 16bit */
+#define SCTRL_P1SMB       0x00000001  /* 1 = stereo */
+#define SCTRL_P1FMT       0x00000003  /* format mask */
+#define SCTRL_SH_P1FMT    0
+
+/* misc stuff */
+
+#define FMODE_DAC         4           /* slight misuse of mode_t */
+
+/* MIDI buffer sizes */
+
+#define MIDIINBUF  256
+#define MIDIOUTBUF 256
+
+#define FMODE_MIDI_SHIFT 3
+#define FMODE_MIDI_READ  (FMODE_READ << FMODE_MIDI_SHIFT)
+#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
+
+#define SND_DEV_DSP16   5 
+
+/* --------------------------------------------------------------------- */
+
+struct es1370_state {
+	/* magic */
+	unsigned int magic;
+
+	/* we keep sb cards in a linked list */
+	struct es1370_state *next;
+
+	/* soundcore stuff */
+	int dev_audio;
+	int dev_mixer;
+	int dev_dac;
+	int dev_midi;
+	
+	/* hardware resources */
+	unsigned int io, irq;
+
+	/* mixer registers; there is no HW readback */
+	struct {
+		unsigned short vol[10];
+		unsigned int recsrc;
+		unsigned int modcnt;
+		unsigned short micpreamp;
+	} mix;
+
+	/* wave stuff */
+	unsigned ctrl;
+	unsigned sctrl;
+
+	spinlock_t lock;
+	struct semaphore open_sem;
+	mode_t open_mode;
+	struct wait_queue *open_wait;
+
+	struct dmabuf {
+		void *rawbuf;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+		unsigned hwptr, swptr;
+		unsigned total_bytes;
+		int count;
+		unsigned error; /* over/underrun */
+		struct wait_queue *wait;
+		/* redundant, but makes calculations easier */
+		unsigned fragsize;
+		unsigned dmasize;
+		unsigned fragsamples;
+		/* OSS stuff */
+		unsigned mapped:1;
+		unsigned ready:1;
+		unsigned endcleared:1;
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+	} dma_dac1, dma_dac2, dma_adc;
+
+	/* midi stuff */
+	struct {
+		unsigned ird, iwr, icnt;
+		unsigned ord, owr, ocnt;
+		struct wait_queue *iwait;
+		struct wait_queue *owait;
+		unsigned char ibuf[MIDIINBUF];
+		unsigned char obuf[MIDIOUTBUF];
+	} midi;
+};
+
+/* --------------------------------------------------------------------- */
+
+struct es1370_state *devs = NULL;
+
+/* --------------------------------------------------------------------- */
+
+extern inline unsigned ld2(unsigned int x)
+{
+	unsigned r = 0;
+	
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
+	}
+	if (x >= 2)
+		r++;
+	return r;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wrcodec(struct es1370_state *s, unsigned char idx, unsigned char data)
+{
+	unsigned long tmo = jiffies + HZ/10;
+	
+	do {
+		if (!(inl(s->io+ES1370_REG_STATUS) & STAT_CSTAT)) {
+			outw((((unsigned short)idx)<<8)|data, s->io+ES1370_REG_CODEC);
+			return;
+		}
+		schedule();
+	} while ((signed)(tmo-jiffies) > 0);
+	printk(KERN_ERR "es1370: write to codec register timeout\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+extern inline void stop_adc(struct es1370_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl &= ~CTRL_ADC_EN;
+	outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+extern inline void stop_dac1(struct es1370_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl &= ~CTRL_DAC1_EN;
+	outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+extern inline void stop_dac2(struct es1370_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl &= ~CTRL_DAC2_EN;
+	outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac1(struct es1370_state *s)
+{
+	unsigned long flags;
+	unsigned fragremain, fshift;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0)
+	    && s->dma_dac1.ready) {
+		s->ctrl |= CTRL_DAC1_EN;
+		s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN;
+		outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+		fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1));
+		fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
+		if (fragremain < 2*fshift)
+			fragremain = s->dma_dac1.fragsize;
+		outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT);
+		outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+		outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC1_SCOUNT);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac2(struct es1370_state *s)
+{
+	unsigned long flags;
+	unsigned fragremain, fshift;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0)
+	    && s->dma_dac2.ready) {
+		s->ctrl |= CTRL_DAC2_EN;
+		s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | 
+					 SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN |
+			(((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | 
+			(0 << SCTRL_SH_P2STINC);
+		outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+		fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1));
+		fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
+		if (fragremain < 2*fshift)
+			fragremain = s->dma_dac2.fragsize;
+		outl((fragremain >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT);
+		outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+		outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1370_REG_DAC2_SCOUNT);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_adc(struct es1370_state *s)
+{
+	unsigned long flags;
+	unsigned fragremain, fshift;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < s->dma_adc.dmasize - 2*s->dma_adc.fragsize)
+	    && s->dma_adc.ready) {
+		s->ctrl |= CTRL_ADC_EN;
+		s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN;
+		outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+		fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1));
+		fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT];
+		if (fragremain < 2*fshift)
+			fragremain = s->dma_adc.fragsize;
+		outl((fragremain >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT);
+		outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+		outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1370_REG_ADC_SCOUNT);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER 8
+#define DMABUF_MINORDER 1
+
+
+extern inline void dealloc_dmabuf(struct dmabuf *db)
+{
+	unsigned long map, mapend;
+
+	if (db->rawbuf) {
+		/* undo marking the pages as reserved */
+		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+			clear_bit(PG_reserved, &mem_map[map].flags);	
+		free_pages((unsigned long)db->rawbuf, db->buforder);
+	}
+	db->rawbuf = NULL;
+	db->mapped = db->ready = 0;
+}
+
+static int prog_dmabuf(struct es1370_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg)
+{
+	int order;
+	unsigned bytepersec;
+	unsigned bufs;
+	unsigned long map, mapend;
+
+	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+	if (!db->rawbuf) {
+		db->ready = db->mapped = 0;
+		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--)
+			db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order);
+		if (!db->rawbuf)
+			return -ENOMEM;
+		db->buforder = order;
+		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+			set_bit(PG_reserved, &mem_map[map].flags);
+	}
+	fmt &= ES1370_FMT_MASK;
+	bytepersec = rate << sample_shift[fmt];
+	bufs = PAGE_SIZE << db->buforder;
+	if (db->ossfragshift) {
+		if ((1000 << db->ossfragshift) < bytepersec)
+			db->fragshift = ld2(bytepersec/1000);
+		else
+			db->fragshift = db->ossfragshift;
+	} else {
+		db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+		if (db->fragshift < 3)
+			db->fragshift = 3;
+	}
+	db->numfrag = bufs >> db->fragshift;
+	while (db->numfrag < 4 && db->fragshift > 3) {
+		db->fragshift--;
+		db->numfrag = bufs >> db->fragshift;
+	}
+	db->fragsize = 1 << db->fragshift;
+	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+		db->numfrag = db->ossmaxfrags;
+	db->fragsamples = db->fragsize >> sample_shift[fmt];
+	db->dmasize = db->numfrag << db->fragshift;
+	memset(db->rawbuf, (fmt & ES1370_FMT_S16) ? 0 : 0x80, db->dmasize);
+	outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE);
+	outl(virt_to_bus(db->rawbuf), s->io+(reg & 0xff));
+	outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff));
+	db->ready = 1;
+	return 0;
+}
+
+extern inline int prog_dmabuf_adc(struct es1370_state *s)
+{
+	stop_adc(s);
+	return prog_dmabuf(s, &s->dma_adc, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV),
+			   (s->sctrl >> SCTRL_SH_R1FMT) & ES1370_FMT_MASK, ES1370_REG_ADC_FRAMEADR);
+}
+
+extern inline int prog_dmabuf_dac2(struct es1370_state *s)
+{
+	stop_dac2(s);
+	return prog_dmabuf(s, &s->dma_dac2, DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV),
+			   (s->sctrl >> SCTRL_SH_P2FMT) & ES1370_FMT_MASK, ES1370_REG_DAC2_FRAMEADR);
+}
+
+extern inline int prog_dmabuf_dac1(struct es1370_state *s)
+{
+	stop_dac1(s);
+	return prog_dmabuf(s, &s->dma_dac1, dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL],
+			   (s->sctrl >> SCTRL_SH_P1FMT) & ES1370_FMT_MASK, ES1370_REG_DAC1_FRAMEADR);
+}
+
+extern inline unsigned get_hwptr(struct es1370_state *s, struct dmabuf *db, unsigned reg)
+{
+	unsigned hwptr, diff;
+
+	outl((reg >> 8) & 15, s->io+ES1370_REG_MEMPAGE);
+	hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc;
+	diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize;
+	db->hwptr = hwptr;
+	return diff;
+}
+
+extern inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c)
+{
+	if (bptr + len > bsize) {
+		unsigned x = bsize - bptr;
+		memset(((char *)buf) + bptr, c, x);
+		bptr = 0;
+		len -= x;
+	}
+	memset(((char *)buf) + bptr, c, len);
+}
+
+/* call with spinlock held! */
+static void es1370_update_ptr(struct es1370_state *s)
+{
+	int diff;
+
+	/* update ADC pointer */
+	if (s->ctrl & CTRL_ADC_EN) {
+		diff = get_hwptr(s, &s->dma_adc, ES1370_REG_ADC_FRAMECNT);
+		s->dma_adc.total_bytes += diff;
+		s->dma_adc.count += diff;
+		if (s->dma_adc.mapped) {
+			if (s->dma_adc.count >= s->dma_adc.fragsize) 
+				wake_up(&s->dma_adc.wait);
+		} else {
+			if (s->dma_adc.count > s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1)) {
+				s->ctrl &= ~CTRL_ADC_EN;
+				outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+				s->dma_adc.error++;
+			}
+			if (s->dma_adc.count > 0)
+				wake_up(&s->dma_adc.wait);
+		}
+	}
+	/* update DAC1 pointer */
+	if (s->ctrl & CTRL_DAC1_EN) {
+		diff = get_hwptr(s, &s->dma_dac1, ES1370_REG_DAC1_FRAMECNT);
+		s->dma_dac1.total_bytes += diff;
+		if (s->dma_dac1.mapped) {
+			s->dma_dac1.count += diff;
+			if (s->dma_dac1.count >= s->dma_dac1.fragsize)
+				wake_up(&s->dma_dac1.wait);
+		} else {
+			s->dma_dac1.count -= diff;
+			if (s->dma_dac1.count <= 0) {
+				s->ctrl &= ~CTRL_DAC1_EN;
+				outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+				s->dma_dac1.error++;
+			} else if (s->dma_dac1.count <= s->dma_dac1.fragsize && !s->dma_dac1.endcleared) {
+				clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, 
+					      s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80);
+				s->dma_dac1.endcleared = 1;
+			}
+			if (s->dma_dac1.count < s->dma_dac1.dmasize)
+				wake_up(&s->dma_dac1.wait);
+		}
+	}
+	/* update DAC2 pointer */
+	if (s->ctrl & CTRL_DAC2_EN) {
+		diff = get_hwptr(s, &s->dma_dac2, ES1370_REG_DAC2_FRAMECNT);
+		s->dma_dac2.total_bytes += diff;
+		if (s->dma_dac2.mapped) {
+			s->dma_dac2.count += diff;
+			if (s->dma_dac2.count >= s->dma_dac2.fragsize)
+				wake_up(&s->dma_dac2.wait);
+		} else {
+			s->dma_dac2.count -= diff;
+			if (s->dma_dac2.count <= 0) {
+				s->ctrl &= ~CTRL_DAC2_EN;
+				outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+				s->dma_dac2.error++;
+			} else if (s->dma_dac2.count <= s->dma_dac2.fragsize && !s->dma_dac2.endcleared) {
+				clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, 
+					      s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80);
+				s->dma_dac2.endcleared = 1;
+			}
+			if (s->dma_dac2.count < s->dma_dac2.dmasize)
+				wake_up(&s->dma_dac2.wait);
+		}
+	}
+}
+
+/* hold spinlock for the following! */
+static void es1370_handle_midi(struct es1370_state *s)
+{
+	unsigned char ch;
+	int wake;
+
+	if (!(s->ctrl & CTRL_UART_EN))
+		return;
+	wake = 0;
+	while (inb(s->io+ES1370_REG_UART_STATUS) & USTAT_RXRDY) {
+		ch = inb(s->io+ES1370_REG_UART_DATA);
+		if (s->midi.icnt < MIDIINBUF) {
+			s->midi.ibuf[s->midi.iwr] = ch;
+			s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF;
+			s->midi.icnt++;
+		}
+		wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.iwait);
+	wake = 0;
+	while ((inb(s->io+ES1370_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) {
+		outb(s->midi.obuf[s->midi.ord], s->io+ES1370_REG_UART_DATA);
+		s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF;
+		s->midi.ocnt--;
+		if (s->midi.ocnt < MIDIOUTBUF-16)
+			wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.owait);
+	outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1370_REG_UART_CONTROL);
+}
+
+static void es1370_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        struct es1370_state *s = (struct es1370_state *)dev_id;
+	unsigned int intsrc, sctl;
+	
+	/* fastpath out, to ease interrupt sharing */
+	intsrc = inl(s->io+ES1370_REG_STATUS);
+	if (!(intsrc & 0x80000000))
+		return;
+	spin_lock(&s->lock);
+	/* clear audio interrupts first */
+	sctl = s->sctrl;
+	if (intsrc & STAT_ADC)
+		sctl &= ~SCTRL_R1INTEN;
+	if (intsrc & STAT_DAC1)
+		sctl &= ~SCTRL_P1INTEN;
+	if (intsrc & STAT_DAC2)
+		sctl &= ~SCTRL_P2INTEN;
+	outl(sctl, s->io+ES1370_REG_SERIAL_CONTROL);
+	outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+	es1370_update_ptr(s);
+	es1370_handle_midi(s);
+	spin_unlock(&s->lock);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT "es1370: invalid magic value\n";
+
+#define VALIDATE_STATE(s)                         \
+({                                                \
+	if (!(s) || (s)->magic != ES1370_MAGIC) { \
+		printk(invalid_magic);            \
+		return -ENXIO;                    \
+	}                                         \
+})
+
+/* --------------------------------------------------------------------- */
+
+static const struct {
+	unsigned volidx:4;
+	unsigned left:4;
+	unsigned right:4;
+	unsigned stereo:1;
+	unsigned recmask:13;
+	unsigned avail:1;
+} mixtable[SOUND_MIXER_NRDEVICES] = {
+	[SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 },   /* master */
+	[SOUND_MIXER_PCM]    = { 1, 0x2, 0x3, 1, 0x0400, 1 },   /* voice */
+	[SOUND_MIXER_SYNTH]  = { 2, 0x4, 0x5, 1, 0x0060, 1 },   /* FM */
+	[SOUND_MIXER_CD]     = { 3, 0x6, 0x7, 1, 0x0006, 1 },   /* CD */
+	[SOUND_MIXER_LINE]   = { 4, 0x8, 0x9, 1, 0x0018, 1 },   /* Line */
+	[SOUND_MIXER_LINE1]  = { 5, 0xa, 0xb, 1, 0x1800, 1 },   /* AUX */
+	[SOUND_MIXER_LINE2]  = { 6, 0xc, 0x0, 1, 0x0100, 1 },   /* Mono1 */
+	[SOUND_MIXER_LINE3]  = { 7, 0xd, 0x0, 1, 0x0200, 1 },   /* Mono2 */
+	[SOUND_MIXER_MIC]    = { 8, 0xe, 0x0, 1, 0x0001, 1 },   /* Mic */
+	[SOUND_MIXER_OGAIN]  = { 9, 0xf, 0x0, 1, 0x0000, 1 }    /* mono out */
+};
+
+static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg)
+{
+	int i, val, j;
+	unsigned char l, r, rl, rr, sr, sl;
+
+	VALIDATE_STATE(s);
+
+	if (cmd == SOUND_MIXER_PRIVATE1) {
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != -1) {
+			s->mix.micpreamp = !!val;
+			wrcodec(s, 0x19, s->mix.micpreamp);
+		}
+		return put_user(s->mix.micpreamp, (int *)arg);
+	}
+        if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, "ES1370", sizeof(info.id));
+		strncpy(info.name, "Ensoniq ES1370", sizeof(info.name));
+		info.modify_counter = s->mix.modcnt;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		strncpy(info.id, "ES1370", sizeof(info.id));
+		strncpy(info.name, "Ensoniq ES1370", sizeof(info.name));
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *)arg);
+	if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))
+                return -EINVAL;
+        if (_IOC_DIR(cmd) == _IOC_READ) {
+                switch (_IOC_NR(cmd)) {
+                case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+			return put_user(s->mix.recsrc, (int *)arg);
+			
+                case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].avail)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+
+                case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].recmask)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].stereo)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                case SOUND_MIXER_CAPS:
+			return put_user(0, (int *)arg);
+
+		default:
+			i = _IOC_NR(cmd);
+                        if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail)
+                                return -EINVAL;
+			return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg);
+		}
+	}
+        if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) 
+		return -EINVAL;
+	s->mix.modcnt++;
+	switch (_IOC_NR(cmd)) {
+	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+		get_user_ret(val, (int *)arg, -EFAULT);
+		for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+			if (!(val & (1 << i)))
+				continue;
+			if (!mixtable[i].recmask) {
+				val &= ~(1 << i);
+				continue;
+			}
+			j |= mixtable[i].recmask;
+		}
+		s->mix.recsrc = val;
+		wrcodec(s, 0x12, j & 0xd5);
+		wrcodec(s, 0x13, j & 0xaa);
+		wrcodec(s, 0x14, (j >> 8) & 0x17);
+		wrcodec(s, 0x15, (j >> 8) & 0x0f);
+		i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc30;
+		wrcodec(s, 0x10, i);
+		wrcodec(s, 0x11, i >> 8);
+		return 0;
+
+	default:
+		i = _IOC_NR(cmd);
+		if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail)
+			return -EINVAL;
+		get_user_ret(val, (int *)arg, -EFAULT);
+		l = val & 0xff;
+		if (l > 100)
+			l = 100;
+		sl = sr = l;
+		if (mixtable[i].stereo) {
+			r = (val >> 8) & 0xff;
+			if (r > 100)
+				r = 100;
+			sr = r;
+			if (l < 10) {
+				rl = 0x80;
+				l = 0;
+			} else {
+				rl = 15 - ((l - 10) / 6);
+				l = (15 - rl) * 6 + 10;
+			}
+			if (r < 10) {
+				rr = 0x80;
+				r = 0;
+			} else {
+				rr =  15 - ((r - 10) / 6);
+				r = (15 - rr) * 6 + 10;
+			}
+			wrcodec(s, mixtable[i].right, rr);
+		} else { 
+			if (mixtable[i].left == 15) {
+				if (l < 2) {
+					rr = rl = 0x80;
+					r = l = 0;
+				} else {
+					rl = 7 - ((l - 2) / 14);
+					r = l = (7 - rl) * 14 + 2;
+				}
+			} else {
+				if (l < 10) {
+					rl = 0x80;
+					r = l = 0;
+				} else {
+					rl = 15 - ((l - 10) / 6);
+					r = l = (15 - rl) * 6 + 10;
+				}
+			}
+		}			
+		wrcodec(s, mixtable[i].left, rl);
+		s->mix.vol[mixtable[i].volidx] = ((unsigned int)sr << 8) | sl;
+                return put_user(s->mix.vol[mixtable[i].volidx], (int *)arg);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static loff_t es1370_llseek(struct file *file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int es1370_open_mixdev(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct es1370_state *s = devs;
+
+	while (s && s->dev_mixer != minor)
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int es1370_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	
+	VALIDATE_STATE(s);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int es1370_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	return mixer_ioctl((struct es1370_state *)file->private_data, cmd, arg);
+}
+
+static /*const*/ struct file_operations es1370_mixer_fops = {
+	&es1370_llseek,
+	NULL,  /* read */
+	NULL,  /* write */
+	NULL,  /* readdir */
+	NULL,  /* poll */
+	&es1370_ioctl_mixdev,
+	NULL,  /* mmap */
+	&es1370_open_mixdev,
+	&es1370_release_mixdev,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+#if 0
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+#endif
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac1(struct es1370_state *s, int nonblock)
+{
+        struct wait_queue wait = { current, NULL };
+	unsigned long flags;
+	int count, tmo;
+	
+	if (s->dma_dac1.mapped)
+		return 0;
+        current->state = TASK_INTERRUPTIBLE;
+        add_wait_queue(&s->dma_dac1.wait, &wait);
+        for (;;) {
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac1.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac1.wait, &wait);
+                        current->state = TASK_RUNNING;
+                        return -EBUSY;
+                }
+		tmo = (count * HZ) / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
+		tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
+		current->timeout = jiffies + (tmo ? tmo : 1);
+                schedule();
+		if (tmo && !current->timeout)
+			printk(KERN_DEBUG "es1370: dma timed out??\n");
+		current->timeout = 0;
+        }
+        remove_wait_queue(&s->dma_dac1.wait, &wait);
+        current->state = TASK_RUNNING;
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+static int drain_dac2(struct es1370_state *s, int nonblock)
+{
+        struct wait_queue wait = { current, NULL };
+	unsigned long flags;
+	int count, tmo;
+
+	if (s->dma_dac2.mapped)
+		return 0;
+        current->state = TASK_INTERRUPTIBLE;
+        add_wait_queue(&s->dma_dac2.wait, &wait);
+        for (;;) {
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac2.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac2.wait, &wait);
+                        current->state = TASK_RUNNING;
+                        return -EBUSY;
+                }
+		tmo = (count * HZ) / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV);
+		tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
+		current->timeout = jiffies + (tmo ? tmo : 1);
+                schedule();
+		if (tmo && !current->timeout)
+			printk(KERN_DEBUG "es1370: dma timed out??\n");
+		current->timeout = 0;
+        }
+        remove_wait_queue(&s->dma_dac2.wait, &wait);
+        current->state = TASK_RUNNING;
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t es1370_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_adc.mapped)
+		return -ENXIO;
+	if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
+		return ret;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		swptr = s->dma_adc.swptr;
+		cnt = s->dma_adc.dmasize-swptr;
+		if (s->dma_adc.count < cnt)
+			cnt = s->dma_adc.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_adc(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->dma_adc.wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_adc.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_adc.swptr = swptr;
+		s->dma_adc.count -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_adc(s);
+	}
+	return ret;
+}
+
+static ssize_t es1370_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac2.mapped)
+		return -ENXIO;
+	if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac2.count < 0) {
+			s->dma_dac2.count = 0;
+			s->dma_dac2.swptr = s->dma_dac2.hwptr;
+		}
+		swptr = s->dma_dac2.swptr;
+		cnt = s->dma_dac2.dmasize-swptr;
+		if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize)
+			cnt = s->dma_dac2.dmasize - s->dma_dac2.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_dac2(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->dma_dac2.wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_dac2.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac2.swptr = swptr;
+		s->dma_dac2.count += cnt;
+		s->dma_dac2.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_dac2(s);
+	}
+	return ret;
+}
+
+static unsigned int es1370_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &s->dma_dac2.wait, wait);
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &s->dma_adc.wait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	es1370_update_ptr(s);
+	if (file->f_mode & FMODE_READ) {
+		if (s->dma_adc.mapped) {
+			if (s->dma_adc.count >= s->dma_adc.fragsize)
+				mask |= POLLIN | POLLRDNORM;
+		} else {
+			if (s->dma_adc.count > 0)
+				mask |= POLLIN | POLLRDNORM;
+		}
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->dma_dac2.mapped) {
+			if (s->dma_dac2.count >= s->dma_dac2.fragsize) 
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			if (s->dma_dac2.dmasize > s->dma_dac2.count)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int es1370_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	struct dmabuf *db;
+	int ret;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf_dac2(s)) != 0)
+			return ret;
+		db = &s->dma_dac2;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf_adc(s)) != 0)
+			return ret;
+		db = &s->dma_adc;
+	} else 
+		return -EINVAL;
+	if (vma->vm_offset != 0)
+		return -EINVAL;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << db->buforder))
+		return -EINVAL;
+	if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+		return -EAGAIN;
+	db->mapped = 1;
+	vma->vm_file = file;
+	file->f_count++;
+	return 0;
+}
+
+static int es1370_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int val, mapped, ret;
+
+	VALIDATE_STATE(s);
+        mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) ||
+		((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/);
+		return 0;
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac2(s);
+			synchronize_irq();
+			s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0;
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			synchronize_irq();
+			s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val >= 0) {
+			if (s->open_mode & (~file->f_mode) & (FMODE_READ|FMODE_WRITE))
+				return -EINVAL;
+			if (val < 4000)
+				val = 4000;
+			if (val > 50000)
+				val = 50000;
+			stop_adc(s);
+			stop_dac2(s);
+			s->dma_adc.ready = s->dma_dac2.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(val) << CTRL_SH_PCLKDIV);
+			outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user(DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV), (int *)arg);
+		
+        case SNDCTL_DSP_STEREO:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			s->dma_adc.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val)
+				s->sctrl |= SCTRL_R1SMB;
+			else
+				s->sctrl &= ~SCTRL_R1SMB;
+			outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac2(s);
+			s->dma_dac2.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val)
+				s->sctrl |= SCTRL_P2SMB;
+			else
+				s->sctrl &= ~SCTRL_P2SMB;
+			outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+                }
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 0) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val >= 2)
+					s->sctrl |= SCTRL_R1SMB;
+				else
+					s->sctrl &= ~SCTRL_R1SMB;
+				outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac2(s);
+				s->dma_dac2.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val >= 2)
+					s->sctrl |= SCTRL_P2SMB;
+				else
+					s->sctrl &= ~SCTRL_P2SMB;
+				outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+		}
+		return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg);
+		
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != AFMT_QUERY) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val == AFMT_S16_LE)
+					s->sctrl |= SCTRL_R1SEB;
+				else
+					s->sctrl &= ~SCTRL_R1SEB;
+				outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac2(s);
+				s->dma_dac2.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val == AFMT_S16_LE)
+					s->sctrl |= SCTRL_P2SEB;
+				else
+					s->sctrl &= ~SCTRL_P2SEB;
+				outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+		}
+		return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? 
+				AFMT_S16_LE : AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) 
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) 
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_SETTRIGGER:
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
+					return ret;
+				start_adc(s);
+			} else
+				stop_adc(s);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s)))
+					return ret;
+				start_dac2(s);
+			} else
+				stop_dac2(s);
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac2(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+		abinfo.fragsize = s->dma_dac2.fragsize;
+                abinfo.bytes = s->dma_dac2.dmasize - s->dma_dac2.count;
+                abinfo.fragstotal = s->dma_dac2.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!(s->ctrl & CTRL_ADC_EN) && (val = prog_dmabuf_adc(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+		abinfo.fragsize = s->dma_adc.fragsize;
+                abinfo.bytes = s->dma_adc.count;
+                abinfo.fragstotal = s->dma_adc.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+                val = s->dma_dac2.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return put_user(val, (int *)arg);
+
+        case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+                cinfo.bytes = s->dma_adc.total_bytes;
+                cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift;
+                cinfo.ptr = s->dma_adc.hwptr;
+		if (s->dma_adc.mapped)
+			s->dma_adc.count &= s->dma_adc.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+                cinfo.bytes = s->dma_dac2.total_bytes;
+                cinfo.blocks = s->dma_dac2.total_bytes >> s->dma_dac2.fragshift;
+                cinfo.ptr = s->dma_dac2.hwptr;
+		if (s->dma_dac2.mapped)
+			s->dma_dac2.count &= s->dma_dac2.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf_dac2(s)))
+				return val;
+			return put_user(s->dma_dac2.fragsize, (int *)arg);
+		}
+		if ((val = prog_dmabuf_adc(s)))
+			return val;
+		return put_user(s->dma_adc.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (file->f_mode & FMODE_READ) {
+			s->dma_adc.ossfragshift = val & 0xffff;
+			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_adc.ossfragshift < 4)
+				s->dma_adc.ossfragshift = 4;
+			if (s->dma_adc.ossfragshift > 15)
+				s->dma_adc.ossfragshift = 15;
+			if (s->dma_adc.ossmaxfrags < 4)
+				s->dma_adc.ossmaxfrags = 4;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			s->dma_dac2.ossfragshift = val & 0xffff;
+			s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_dac2.ossfragshift < 4)
+				s->dma_dac2.ossfragshift = 4;
+			if (s->dma_dac2.ossfragshift > 15)
+				s->dma_dac2.ossfragshift = 15;
+			if (s->dma_dac2.ossmaxfrags < 4)
+				s->dma_dac2.ossmaxfrags = 4;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+		    (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision))
+			return -EINVAL;
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			s->dma_adc.subdivision = val;
+		if (file->f_mode & FMODE_WRITE)
+			s->dma_dac2.subdivision = val;
+		return 0;
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_RATE:
+        case SOUND_PCM_READ_CHANNELS:
+        case SOUND_PCM_READ_BITS:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return mixer_ioctl(s, cmd, arg);
+}
+
+static int es1370_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct es1370_state *s = devs;
+	unsigned long flags;
+
+	while (s && ((s->dev_audio ^ minor) & ~0xf))
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & file->f_mode) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_READ|FMODE_WRITE)))
+		s->ctrl = (s->ctrl & ~CTRL_PCLKDIV) | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV);
+	if (file->f_mode & FMODE_READ) {
+		s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
+		s->sctrl &= ~SCTRL_R1FMT;
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_R1FMT;
+		else
+			s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_R1FMT;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0;
+		s->sctrl &= ~SCTRL_P2FMT;
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P2FMT;
+		else
+			s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P2FMT;
+	}
+	outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+	outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int es1370_release(struct inode *inode, struct file *file)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE)
+		drain_dac2(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	if (file->f_mode & FMODE_WRITE) {
+		stop_dac2(s);
+		dealloc_dmabuf(&s->dma_dac2);
+	}
+	if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
+		dealloc_dmabuf(&s->dma_adc);
+	}
+	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static /*const*/ struct file_operations es1370_audio_fops = {
+	&es1370_llseek,
+	&es1370_read,
+	&es1370_write,
+	NULL,  /* readdir */
+	&es1370_poll,
+	&es1370_ioctl,
+	&es1370_mmap,
+	&es1370_open,
+	&es1370_release,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t es1370_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	ssize_t ret = 0;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac1.mapped)
+		return -ENXIO;
+	if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac1.count < 0) {
+			s->dma_dac1.count = 0;
+			s->dma_dac1.swptr = s->dma_dac1.hwptr;
+		}
+		swptr = s->dma_dac1.swptr;
+		cnt = s->dma_dac1.dmasize-swptr;
+		if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize)
+			cnt = s->dma_dac1.dmasize - s->dma_dac1.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_dac1(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->dma_dac1.wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_dac1.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac1.swptr = swptr;
+		s->dma_dac1.count += cnt;
+		s->dma_dac1.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_dac1(s);
+	}
+	return ret;
+}
+
+static unsigned int es1370_poll_dac(struct file *file, struct poll_table_struct *wait)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	poll_wait(file, &s->dma_dac1.wait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	es1370_update_ptr(s);
+	if (s->dma_dac1.mapped) {
+		if (s->dma_dac1.count >= s->dma_dac1.fragsize)
+			mask |= POLLOUT | POLLWRNORM;
+	} else {
+		if (s->dma_dac1.dmasize > s->dma_dac1.count)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int es1370_mmap_dac(struct file *file, struct vm_area_struct *vma)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	int ret;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	if (!(vma->vm_flags & VM_WRITE))
+		return -EINVAL;
+	if ((ret = prog_dmabuf_dac1(s)) != 0)
+		return ret;
+	if (vma->vm_offset != 0)
+		return -EINVAL;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << s->dma_dac1.buforder))
+		return -EINVAL;
+	if (remap_page_range(vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot))
+		return -EAGAIN;
+	s->dma_dac1.mapped = 1;
+	vma->vm_file = file;
+	file->f_count++;
+	return 0;
+}
+
+static int es1370_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	unsigned ctrl;
+	int val, ret;
+
+	VALIDATE_STATE(s);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/);
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return -EINVAL;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		stop_dac1(s);
+		synchronize_irq();
+		s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0;
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val >= 0) {
+			stop_dac1(s);
+			s->dma_dac1.ready = 0;
+			for (ctrl = 0; ctrl <= 2; ctrl++)
+				if (val < (dac1_samplerate[ctrl] + dac1_samplerate[ctrl+1]) / 2)
+					break;
+			spin_lock_irqsave(&s->lock, flags);
+			s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (ctrl << CTRL_SH_WTSRSEL);
+			outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user(dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL], (int *)arg);
+		
+        case SNDCTL_DSP_STEREO:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		stop_dac1(s);
+		s->dma_dac1.ready = 0;
+		spin_lock_irqsave(&s->lock, flags);
+		if (val)
+			s->sctrl |= SCTRL_P1SMB;
+		else
+			s->sctrl &= ~SCTRL_P1SMB;
+		outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+		spin_unlock_irqrestore(&s->lock, flags);
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 0) {
+			if (s->dma_dac1.mapped)
+				return -EINVAL;
+			stop_dac1(s);
+			s->dma_dac1.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val >= 2)
+				s->sctrl |= SCTRL_P1SMB;
+			else
+				s->sctrl &= ~SCTRL_P1SMB;
+			outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg);
+		
+        case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+		
+        case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != AFMT_QUERY) {
+			stop_dac1(s);
+			s->dma_dac1.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val == AFMT_S16_LE)
+				s->sctrl |= SCTRL_P1SEB;
+			else
+				s->sctrl &= ~SCTRL_P1SEB;
+			outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg);
+
+        case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg);
+						
+	case SNDCTL_DSP_SETTRIGGER:
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val & PCM_ENABLE_OUTPUT) {
+			if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
+				return ret;
+			start_dac1(s);
+		} else
+			stop_dac1(s);
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac1(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+		abinfo.fragsize = s->dma_dac1.fragsize;
+                abinfo.bytes = s->dma_dac1.dmasize - s->dma_dac1.count;
+                abinfo.fragstotal = s->dma_dac1.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+                val = s->dma_dac1.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return put_user(val, (int *)arg);
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_update_ptr(s);
+                cinfo.bytes = s->dma_dac1.total_bytes;
+                cinfo.blocks = s->dma_dac1.total_bytes >> s->dma_dac1.fragshift;
+                cinfo.ptr = s->dma_dac1.hwptr;
+		if (s->dma_dac1.mapped)
+			s->dma_dac1.count &= s->dma_dac1.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if ((val = prog_dmabuf_dac1(s)))
+			return val;
+                return put_user(s->dma_dac1.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		s->dma_dac1.ossfragshift = val & 0xffff;
+		s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff;
+		if (s->dma_dac1.ossfragshift < 4)
+			s->dma_dac1.ossfragshift = 4;
+		if (s->dma_dac1.ossfragshift > 15)
+			s->dma_dac1.ossfragshift = 15;
+		if (s->dma_dac1.ossmaxfrags < 4)
+			s->dma_dac1.ossmaxfrags = 4;
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if (s->dma_dac1.subdivision)
+			return -EINVAL;
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		s->dma_dac1.subdivision = val;
+		return 0;
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_RATE:
+        case SOUND_PCM_READ_CHANNELS:
+        case SOUND_PCM_READ_BITS:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return mixer_ioctl(s, cmd, arg);
+}
+
+static int es1370_open_dac(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct es1370_state *s = devs;
+	unsigned long flags;
+
+	while (s && ((s->dev_dac ^ minor) & ~0xf))
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+       	/* we allow opening with O_RDWR, most programs do it although they will only write */
+#if 0
+	if (file->f_mode & FMODE_READ)
+		return -EPERM;
+#endif
+	if (!(file->f_mode & FMODE_WRITE))
+		return -EINVAL;
+       	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & FMODE_DAC) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0;
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl = (s->ctrl & ~CTRL_WTSRSEL) | (1 << CTRL_SH_WTSRSEL);
+      	s->sctrl &= ~SCTRL_P1FMT;
+	if ((minor & 0xf) == SND_DEV_DSP16)
+		s->sctrl |= ES1370_FMT_S16_MONO << SCTRL_SH_P1FMT;
+	else
+		s->sctrl |= ES1370_FMT_U8_MONO << SCTRL_SH_P1FMT;
+	outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+	outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= FMODE_DAC;
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int es1370_release_dac(struct inode *inode, struct file *file)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	drain_dac1(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	stop_dac1(s);
+	dealloc_dmabuf(&s->dma_dac1);
+	s->open_mode &= ~FMODE_DAC;
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static /*const*/ struct file_operations es1370_dac_fops = {
+	&es1370_llseek,
+	NULL,  /* read */
+	&es1370_write_dac,
+	NULL,  /* readdir */
+	&es1370_poll_dac,
+	&es1370_ioctl_dac,
+	&es1370_mmap_dac,
+	&es1370_open_dac,
+	&es1370_release_dac,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.ird;
+		cnt = MIDIINBUF - ptr;
+		if (s->midi.icnt < cnt)
+			cnt = s->midi.icnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->midi.iwait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt))
+			return ret ? ret : -EFAULT;
+		ptr = (ptr + cnt) % MIDIINBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.ird = ptr;
+		s->midi.icnt -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+	}
+	return ret;
+}
+
+static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.owr;
+		cnt = MIDIOUTBUF - ptr;
+		if (s->midi.ocnt + cnt > MIDIOUTBUF)
+			cnt = MIDIOUTBUF - s->midi.ocnt;
+		if (cnt <= 0)
+			es1370_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->midi.owait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt))
+			return ret ? ret : -EFAULT;
+		ptr = (ptr + cnt) % MIDIOUTBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.owr = ptr;
+		s->midi.ocnt += cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		spin_lock_irqsave(&s->lock, flags);
+		es1370_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+	}
+	return ret;
+}
+
+static unsigned int es1370_midi_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &s->midi.owait, wait);
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &s->midi.iwait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_mode & FMODE_READ) {
+		if (s->midi.icnt > 0)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		if (s->midi.ocnt < MIDIOUTBUF)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int es1370_midi_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct es1370_state *s = devs;
+	unsigned long flags;
+
+	while (s && s->dev_midi != minor)
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+		outb(UCTRL_CNTRL_SWR, s->io+ES1370_REG_UART_CONTROL);
+		outb(0, s->io+ES1370_REG_UART_CONTROL);
+		outb(0, s->io+ES1370_REG_UART_TEST);
+	}
+	if (file->f_mode & FMODE_READ) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+	}
+	s->ctrl |= CTRL_UART_EN;
+	outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	es1370_handle_midi(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int es1370_midi_release(struct inode *inode, struct file *file)
+{
+	struct es1370_state *s = (struct es1370_state *)file->private_data;
+        struct wait_queue wait = { current, NULL };
+	unsigned long flags;
+	unsigned count, tmo;
+
+	VALIDATE_STATE(s);
+
+	if (file->f_mode & FMODE_WRITE) {
+		current->state = TASK_INTERRUPTIBLE;
+		add_wait_queue(&s->midi.owait, &wait);
+		for (;;) {
+			spin_lock_irqsave(&s->lock, flags);
+			count = s->midi.ocnt;
+			spin_unlock_irqrestore(&s->lock, flags);
+			if (count <= 0)
+				break;
+			if (signal_pending(current))
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				remove_wait_queue(&s->midi.owait, &wait);
+				current->state = TASK_RUNNING;
+				return -EBUSY;
+			}
+			tmo = (count * HZ) / 3100;
+			current->timeout = tmo ? jiffies + tmo : 0;
+			schedule();
+			if (tmo && !current->timeout)
+				printk(KERN_DEBUG "es1370: midi timed out??\n");
+			current->timeout = 0;
+		}
+		remove_wait_queue(&s->midi.owait, &wait);
+		current->state = TASK_RUNNING;
+	}
+	down(&s->open_sem);
+	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->ctrl &= ~CTRL_UART_EN;
+		outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static /*const*/ struct file_operations es1370_midi_fops = {
+	&es1370_llseek,
+	&es1370_midi_read,
+	&es1370_midi_write,
+	NULL,  /* readdir */
+	&es1370_midi_poll,
+	NULL,  /* ioctl */
+	NULL,  /* mmap */
+	&es1370_midi_open,
+	&es1370_midi_release,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+/* maximum number of devices */
+#define NR_DEVICE 5
+
+static int joystick[NR_DEVICE] = { 0, };
+static int lineout[NR_DEVICE] = { 0, };
+static int micz[NR_DEVICE] = { 0, };
+
+/* --------------------------------------------------------------------- */
+
+static const struct initvol {
+	int mixch;
+	int vol;
+} initvol[] __initdata = {
+	{ SOUND_MIXER_WRITE_VOLUME, 0x4040 },
+	{ SOUND_MIXER_WRITE_PCM, 0x4040 },
+	{ SOUND_MIXER_WRITE_SYNTH, 0x4040 },
+	{ SOUND_MIXER_WRITE_CD, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE1, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE2, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE3, 0x4040 },
+	{ SOUND_MIXER_WRITE_MIC, 0x4040 },
+	{ SOUND_MIXER_WRITE_OGAIN, 0x4040 }
+};
+
+#ifdef MODULE
+__initfunc(int init_module(void))
+#else
+__initfunc(int init_es1370(void))
+#endif
+{
+	struct es1370_state *s;
+	struct pci_dev *pcidev = NULL;
+	mm_segment_t fs;
+	int i, val, index = 0;
+
+	if (!pci_present())   /* No PCI bus in this machine! */
+		return -ENODEV;
+	printk(KERN_INFO "es1370: version v0.10 time " __TIME__ " " __DATE__ "\n");
+	while (index < NR_DEVICE && 
+	       (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) {
+		if (pcidev->base_address[0] == 0 || 
+		    (pcidev->base_address[0] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
+			continue;
+		if (pcidev->irq == 0) 
+			continue;
+		if (!(s = kmalloc(sizeof(struct es1370_state), GFP_KERNEL))) {
+			printk(KERN_WARNING "es1370: out of memory\n");
+			continue;
+		}
+		memset(s, 0, sizeof(struct es1370_state));
+		init_waitqueue(&s->dma_adc.wait);
+		init_waitqueue(&s->dma_dac1.wait);
+		init_waitqueue(&s->dma_dac2.wait);
+		init_waitqueue(&s->open_wait);
+		init_waitqueue(&s->midi.iwait);
+		init_waitqueue(&s->midi.owait);
+		s->open_sem = MUTEX;
+		s->magic = ES1370_MAGIC;
+		s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+		s->irq = pcidev->irq;
+		if (check_region(s->io, ES1370_EXTENT)) {
+			printk(KERN_ERR "es1370: io ports %#x-%#x in use\n", s->io, s->io+ES1370_EXTENT-1);
+			goto err_region;
+		}
+		request_region(s->io, ES1370_EXTENT, "es1370");
+		if (request_irq(s->irq, es1370_interrupt, SA_INTERRUPT|SA_SHIRQ, "es1370", s)) {
+			printk(KERN_ERR "es1370: irq %u in use\n", s->irq);
+			goto err_irq;
+		}
+		/* initialize codec registers */
+		s->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV);
+		if (joystick[index]) {
+			if (check_region(0x200, JOY_EXTENT))
+				printk(KERN_ERR "es1370: io port 0x200 in use\n");
+			else
+				s->ctrl |= CTRL_JYSTK_EN;
+		}
+		if (lineout[index])
+			s->ctrl |= CTRL_XCTL0;
+		if (micz[index])
+			s->ctrl |= CTRL_XCTL1;
+		s->sctrl = 0;
+		printk(KERN_INFO "es1370: found adapter at io %#06x irq %u\n"
+		       KERN_INFO "es1370: features: joystick %s, line %s, mic impedance %s\n",
+		       s->io, s->irq, (s->ctrl & CTRL_JYSTK_EN) ? "on" : "off",
+		       (s->ctrl & CTRL_XCTL0) ? "out" : "in",
+		       (s->ctrl & CTRL_XCTL1) ? "1" : "0");
+		/* register devices */
+		if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops)) < 0)
+			goto err_dev1;
+		if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops)) < 0)
+			goto err_dev2;
+		if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops)) < 0)
+			goto err_dev3;
+		if ((s->dev_midi = register_sound_midi(&es1370_midi_fops)) < 0)
+			goto err_dev4;
+		if (s->ctrl & CTRL_JYSTK_EN)
+			request_region(0x200, JOY_EXTENT, "es1370");
+		/* initialize the chips */
+		outl(s->ctrl, s->io+ES1370_REG_CONTROL);
+		outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL);
+		wrcodec(s, 0x16, 3); /* no RST, PD */
+		wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!!  */
+		wrcodec(s, 0x18, 0); /* recording source is mixer */
+		fs = get_fs();
+		set_fs(KERNEL_DS);
+		val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD;
+		mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+		for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
+			val = initvol[i].vol;
+			mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
+		}
+		set_fs(fs);
+		/* queue it for later freeing */
+		s->next = devs;
+		devs = s;
+		index++;
+		continue;
+
+	err_dev4:
+		unregister_sound_dsp(s->dev_dac);
+	err_dev3:
+		unregister_sound_mixer(s->dev_mixer);
+	err_dev2:
+		unregister_sound_dsp(s->dev_audio);
+	err_dev1:
+		printk(KERN_ERR "es1370: cannot register misc device\n");
+		free_irq(s->irq, s);
+	err_irq:
+		release_region(s->io, ES1370_EXTENT);
+	err_region:
+		kfree_s(s, sizeof(struct es1370_state));
+	}
+	if (!devs)
+		return -ENODEV;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)");
+MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out");
+MODULE_PARM(micz, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(micz, "changes (??) the microphone impedance");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("ES1370 AudioPCI Driver");
+
+void cleanup_module(void)
+{
+	struct es1370_state *s;
+
+	while ((s = devs)) {
+		devs = devs->next;
+		outl(CTRL_SERR_DIS, s->io+ES1370_REG_CONTROL); /* switch everything off */
+		outl(0, s->io+ES1370_REG_SERIAL_CONTROL); /* clear serial interrupts */
+		synchronize_irq();
+		free_irq(s->irq, s);
+		release_region(s->io, ES1370_EXTENT);
+		if (s->ctrl & CTRL_JYSTK_EN)
+			release_region(0x200, JOY_EXTENT);
+		unregister_sound_dsp(s->dev_audio);
+		unregister_sound_mixer(s->dev_mixer);
+		unregister_sound_dsp(s->dev_dac);
+		unregister_sound_midi(s->dev_midi);
+		kfree_s(s, sizeof(struct es1370_state));
+	}
+	printk(KERN_INFO "es1370: unloading\n");
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/es1371.c linux/drivers/sound/es1371.c
--- v2.1.108/linux/drivers/sound/es1371.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/es1371.c	Fri Jul 10 14:03:36 1998
@@ -0,0 +1,2852 @@
+/*****************************************************************************/
+
+/*
+ *      es1371.c  --  Creative Ensoniq ES1371.
+ *
+ *      Copyright (C) 1998  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Special thanks to Ensoniq
+ *
+ *
+ * Module command line parameters:
+ *   joystick if 1 enables the joystick interface on the card; but it still
+ *            needs a separate joystick driver (presumably PC standard, although
+ *            the chip doc doesn't say anything and it looks slightly fishy from
+ *            the PCI standpoint...)
+ *
+ *
+ *  Supported devices:
+ *  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible
+ *  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
+ *  /dev/dsp1   additional DAC, like /dev/dsp, but outputs to mixer "SYNTH" setting
+ *  /dev/midi   simple MIDI UART interface, no ioctl
+ *
+ *  NOTE: the card does not have any FM/Wavetable synthesizer, it is supposed
+ *  to be done in software. That is what /dev/dac is for. By now (Q2 1998)
+ *  there are several MIDI to PCM (WAV) packages, one of them is timidity.
+ *
+ *  Revision history
+ *    04.06.98   0.1   Initial release
+ *                     Mixer stuff should be overhauled; especially optional AC97 mixer bits
+ *                     should be detected. This results in strange behaviour of some mixer
+ *                     settings, like master volume and mic.
+ *    08.06.98   0.2   First release using Alan Cox' soundcore instead of miscdevice
+ *
+ *
+ *
+ */
+
+/*****************************************************************************/
+      
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/modversions.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/malloc.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <asm/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+
+/* --------------------------------------------------------------------- */
+
+#ifndef PCI_VENDOR_ID_ENSONIQ
+#define PCI_VENDOR_ID_ENSONIQ        0x1274    
+#endif
+#ifndef PCI_DEVICE_ID_ENSONIQ_ES1371
+#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371
+#endif
+
+#define ES1371_MAGIC  ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1371)
+
+#define ES1371_EXTENT             0x40
+#define JOY_EXTENT                8
+
+#define ES1371_REG_CONTROL        0x00
+#define ES1371_REG_STATUS         0x04
+#define ES1371_REG_UART_DATA      0x08
+#define ES1371_REG_UART_STATUS    0x09
+#define ES1371_REG_UART_CONTROL   0x09
+#define ES1371_REG_UART_TEST      0x0a
+#define ES1371_REG_MEMPAGE        0x0c
+#define ES1371_REG_SRCONV         0x10
+#define ES1371_REG_CODEC          0x14
+#define ES1371_REG_LEGACY         0x18
+#define ES1371_REG_SERIAL_CONTROL 0x20
+#define ES1371_REG_DAC1_SCOUNT    0x24
+#define ES1371_REG_DAC2_SCOUNT    0x28
+#define ES1371_REG_ADC_SCOUNT     0x2c
+
+#define ES1371_REG_DAC1_FRAMEADR  0xc30
+#define ES1371_REG_DAC1_FRAMECNT  0xc34
+#define ES1371_REG_DAC2_FRAMEADR  0xc38
+#define ES1371_REG_DAC2_FRAMECNT  0xc3c
+#define ES1371_REG_ADC_FRAMEADR   0xd30
+#define ES1371_REG_ADC_FRAMECNT   0xd34
+
+#define ES1371_FMT_U8_MONO     0
+#define ES1371_FMT_U8_STEREO   1
+#define ES1371_FMT_S16_MONO    2
+#define ES1371_FMT_S16_STEREO  3
+#define ES1371_FMT_STEREO      1
+#define ES1371_FMT_S16         2
+#define ES1371_FMT_MASK        3
+
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+#define CTRL_JOY_SHIFT  24
+#define CTRL_JOY_MASK   3
+#define CTRL_JOY_200    0x00000000  /* joystick base address */
+#define CTRL_JOY_208    0x01000000
+#define CTRL_JOY_210    0x02000000
+#define CTRL_JOY_218    0x03000000
+#define CTRL_GPIO_IN0   0x00100000  /* general purpose inputs/outputs */
+#define CTRL_GPIO_IN1   0x00200000
+#define CTRL_GPIO_IN2   0x00400000
+#define CTRL_GPIO_IN3   0x00800000
+#define CTRL_GPIO_OUT0  0x00010000
+#define CTRL_GPIO_OUT1  0x00020000
+#define CTRL_GPIO_OUT2  0x00040000
+#define CTRL_GPIO_OUT3  0x00080000
+#define CTRL_MSFMTSEL   0x00008000  /* MPEG serial data fmt: 0 = Sony, 1 = I2S */
+#define CTRL_SYNCRES    0x00004000  /* AC97 warm reset */
+#define CTRL_ADCSTOP    0x00002000  /* stop ADC transfers */
+#define CTRL_PWR_INTRM  0x00001000  /* 1 = power level ints enabled */
+#define CTRL_M_CB       0x00000800  /* recording source: 0 = ADC, 1 = MPEG */
+#define CTRL_CCB_INTRM  0x00000400  /* 1 = CCB "voice" ints enabled */
+#define CTRL_PDLEV0     0x00000000  /* power down level */
+#define CTRL_PDLEV1     0x00000100
+#define CTRL_PDLEV2     0x00000200
+#define CTRL_PDLEV3     0x00000300
+#define CTRL_BREQ       0x00000080  /* 1 = test mode (internal mem test) */
+#define CTRL_DAC1_EN    0x00000040  /* enable DAC1 */
+#define CTRL_DAC2_EN    0x00000020  /* enable DAC2 */
+#define CTRL_ADC_EN     0x00000010  /* enable ADC */
+#define CTRL_UART_EN    0x00000008  /* enable MIDI uart */
+#define CTRL_JYSTK_EN   0x00000004  /* enable Joystick port */
+#define CTRL_XTALCLKDIS 0x00000002  /* 1 = disable crystal clock input */
+#define CTRL_PCICLKDIS  0x00000001  /* 1 = disable PCI clock distribution */
+
+
+#define STAT_INTR       0x80000000  /* wired or of all interrupt bits */
+#define STAT_SYNC_ERR   0x00000100  /* 1 = codec sync error */
+#define STAT_VC         0x000000c0  /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */
+#define STAT_SH_VC      6
+#define STAT_MPWR       0x00000020  /* power level interrupt */
+#define STAT_MCCB       0x00000010  /* CCB int pending */
+#define STAT_UART       0x00000008  /* UART int pending */
+#define STAT_DAC1       0x00000004  /* DAC1 int pending */
+#define STAT_DAC2       0x00000002  /* DAC2 int pending */
+#define STAT_ADC        0x00000001  /* ADC int pending */
+
+#define USTAT_RXINT     0x80        /* UART rx int pending */
+#define USTAT_TXINT     0x04        /* UART tx int pending */
+#define USTAT_TXRDY     0x02        /* UART tx ready */
+#define USTAT_RXRDY     0x01        /* UART rx ready */
+
+#define UCTRL_RXINTEN   0x80        /* 1 = enable RX ints */
+#define UCTRL_TXINTEN   0x60        /* TX int enable field mask */
+#define UCTRL_ENA_TXINT 0x20        /* enable TX int */
+#define UCTRL_CNTRL     0x03        /* control field */
+#define UCTRL_CNTRL_SWR 0x03        /* software reset command */
+
+/* sample rate converter */
+#define SRC_RAMADDR_MASK   0xfe000000
+#define SRC_RAMADDR_SHIFT  25
+#define SRC_WE             0x01000000  /* read/write control for SRC RAM */
+#define SRC_BUSY           0x00800000  /* SRC busy */
+#define SRC_DIS            0x00400000  /* 1 = disable SRC */
+#define SRC_DDAC1          0x00200000  /* 1 = disable accum update for DAC1 */
+#define SRC_DDAC2          0x00100000  /* 1 = disable accum update for DAC2 */
+#define SRC_DADC           0x00080000  /* 1 = disable accum update for ADC2 */
+#define SRC_RAMDATA_MASK   0x0000ffff
+#define SRC_RAMDATA_SHIFT  0
+
+#define SRCREG_ADC      0x78
+#define SRCREG_DAC1     0x70
+#define SRCREG_DAC2     0x74
+#define SRCREG_VOL_ADC  0x6c
+#define SRCREG_VOL_DAC1 0x7c
+#define SRCREG_VOL_DAC2 0x7e
+
+#define SRCREG_TRUNC_N     0x00
+#define SRCREG_INT_REGS    0x01
+#define SRCREG_ACCUM_FRAC  0x02
+#define SRCREG_VFREQ_FRAC  0x03
+
+#define CODEC_PIRD        0x00800000  /* 0 = write AC97 register */
+#define CODEC_PIADD_MASK  0x007f0000
+#define CODEC_PIADD_SHIFT 16
+#define CODEC_PIDAT_MASK  0x0000ffff
+#define CODEC_PIDAT_SHIFT 0
+
+#define CODEC_RDY         0x80000000  /* AC97 read data valid */
+#define CODEC_WIP         0x40000000  /* AC97 write in progress */
+#define CODEC_PORD        0x00800000  /* 0 = write AC97 register */
+#define CODEC_POADD_MASK  0x007f0000
+#define CODEC_POADD_SHIFT 16
+#define CODEC_PODAT_MASK  0x0000ffff
+#define CODEC_PODAT_SHIFT 0
+
+
+#define LEGACY_JFAST      0x80000000  /* fast joystick timing */
+#define LEGACY_FIRQ       0x01000000  /* force IRQ */
+
+#define SCTRL_DACTEST     0x00400000  /* 1 = DAC test, test vector generation purposes */
+#define SCTRL_P2ENDINC    0x00380000  /*  */
+#define SCTRL_SH_P2ENDINC 19
+#define SCTRL_P2STINC     0x00070000  /*  */
+#define SCTRL_SH_P2STINC  16
+#define SCTRL_R1LOOPSEL   0x00008000  /* 0 = loop mode */
+#define SCTRL_P2LOOPSEL   0x00004000  /* 0 = loop mode */
+#define SCTRL_P1LOOPSEL   0x00002000  /* 0 = loop mode */
+#define SCTRL_P2PAUSE     0x00001000  /* 1 = pause mode */
+#define SCTRL_P1PAUSE     0x00000800  /* 1 = pause mode */
+#define SCTRL_R1INTEN     0x00000400  /* enable interrupt */
+#define SCTRL_P2INTEN     0x00000200  /* enable interrupt */
+#define SCTRL_P1INTEN     0x00000100  /* enable interrupt */
+#define SCTRL_P1SCTRLD    0x00000080  /* reload sample count register for DAC1 */
+#define SCTRL_P2DACSEN    0x00000040  /* 1 = DAC2 play back last sample when disabled */
+#define SCTRL_R1SEB       0x00000020  /* 1 = 16bit */
+#define SCTRL_R1SMB       0x00000010  /* 1 = stereo */
+#define SCTRL_R1FMT       0x00000030  /* format mask */
+#define SCTRL_SH_R1FMT    4
+#define SCTRL_P2SEB       0x00000008  /* 1 = 16bit */
+#define SCTRL_P2SMB       0x00000004  /* 1 = stereo */
+#define SCTRL_P2FMT       0x0000000c  /* format mask */
+#define SCTRL_SH_P2FMT    2
+#define SCTRL_P1SEB       0x00000002  /* 1 = 16bit */
+#define SCTRL_P1SMB       0x00000001  /* 1 = stereo */
+#define SCTRL_P1FMT       0x00000003  /* format mask */
+#define SCTRL_SH_P1FMT    0
+
+/* codec constants */
+
+#define CODEC_ID_DEDICATEDMIC    0x001
+#define CODEC_ID_MODEMCODEC      0x002
+#define CODEC_ID_BASSTREBLE      0x004
+#define CODEC_ID_SIMULATEDSTEREO 0x008
+#define CODEC_ID_HEADPHONEOUT    0x010
+#define CODEC_ID_LOUDNESS        0x020
+#define CODEC_ID_18BITDAC        0x040
+#define CODEC_ID_20BITDAC        0x080
+#define CODEC_ID_18BITADC        0x100
+#define CODEC_ID_20BITADC        0x200
+
+#define CODEC_ID_SESHIFT    10
+#define CODEC_ID_SEMASK     0x1f
+
+
+/* misc stuff */
+
+#define FMODE_DAC         4           /* slight misuse of mode_t */
+
+/* MIDI buffer sizes */
+
+#define MIDIINBUF  256
+#define MIDIOUTBUF 256
+
+#define FMODE_MIDI_SHIFT 3
+#define FMODE_MIDI_READ  (FMODE_READ << FMODE_MIDI_SHIFT)
+#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
+
+#define SND_DEV_DSP16   5 
+
+/* --------------------------------------------------------------------- */
+
+static const char *stereo_enhancement[] __initdata = 
+{
+	"no 3D stereo enhancement",
+	"Analog Devices Phat Stereo",
+	"Creative Stereo Enhancement",
+	"National Semiconductor 3D Stereo Enhancement",
+	"YAMAHA Ymersion",
+	"BBE 3D Stereo Enhancement",
+	"Crystal Semiconductor 3D Stereo Enhancement",
+	"Qsound QXpander",
+	"Spatializer 3D Stereo Enhancement",
+	"SRS 3D Stereo Enhancement",
+	"Platform Technologies 3D Stereo Enhancement", 
+	"AKM 3D Audio",
+	"Aureal Stereo Enhancement",
+	"AZTECH  3D Enhancement",
+	"Binaura 3D Audio Enhancement",
+	"ESS Technology Stereo Enhancement",
+	"Harman International VMAx",
+	"NVidea 3D Stereo Enhancement",
+	"Philips Incredible Sound",
+	"Texas Instruments 3D Stereo Enhancement",
+	"VLSI Technology 3D Stereo Enhancement"
+};
+
+/* --------------------------------------------------------------------- */
+
+struct es1371_state {
+	/* magic */
+	unsigned int magic;
+
+	/* we keep sb cards in a linked list */
+	struct es1371_state *next;
+
+	/* soundcore stuff */
+	int dev_audio;
+	int dev_mixer;
+	int dev_dac;
+	int dev_midi;
+	
+	/* hardware resources */
+	unsigned int io, irq;
+
+	/* mixer registers; there is no HW readback */
+	struct {
+		unsigned short codec_id;
+		unsigned int modcnt;
+	} mix;
+
+	/* wave stuff */
+	unsigned ctrl;
+	unsigned sctrl;
+	unsigned dac1rate, dac2rate, adcrate;
+
+	spinlock_t lock;
+	struct semaphore open_sem;
+	mode_t open_mode;
+	struct wait_queue *open_wait;
+
+	struct dmabuf {
+		void *rawbuf;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+		unsigned hwptr, swptr;
+		unsigned total_bytes;
+		int count;
+		unsigned error; /* over/underrun */
+		struct wait_queue *wait;
+		/* redundant, but makes calculations easier */
+		unsigned fragsize;
+		unsigned dmasize;
+		unsigned fragsamples;
+		/* OSS stuff */
+		unsigned mapped:1;
+		unsigned ready:1;
+		unsigned endcleared:1;
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+	} dma_dac1, dma_dac2, dma_adc;
+
+	/* midi stuff */
+	struct {
+		unsigned ird, iwr, icnt;
+		unsigned ord, owr, ocnt;
+		struct wait_queue *iwait;
+		struct wait_queue *owait;
+		unsigned char ibuf[MIDIINBUF];
+		unsigned char obuf[MIDIOUTBUF];
+	} midi;
+};
+
+/* --------------------------------------------------------------------- */
+
+struct es1371_state *devs = NULL;
+
+/* --------------------------------------------------------------------- */
+
+extern inline unsigned ld2(unsigned int x)
+{
+	unsigned r = 0;
+	
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
+	}
+	if (x >= 2)
+		r++;
+	return r;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * hweightN: returns the hamming weight (i.e. the number
+ * of bits set) of a N-bit word
+ */
+
+#ifdef hweight32
+#undef hweight32
+#endif
+
+extern __inline__ unsigned int hweight32(unsigned int w)
+{
+        unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
+        res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+        res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
+        res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
+        return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned wait_src_ready(struct es1371_state *s)
+{
+	unsigned int t, r;
+
+	for (t = 0; t < 1000; t++) {
+		if (!((r = inl(s->io + ES1371_REG_SRCONV)) & SRC_BUSY))
+			return r;
+		udelay(1);
+	}
+	printk(KERN_DEBUG "es1371: sample rate converter timeout r = 0x%08x\n", r);
+	return r;
+}
+
+static unsigned src_read(struct es1371_state *s, unsigned reg)
+{
+	unsigned int r;
+
+	r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC);
+	r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK;
+	outl(r, s->io + ES1371_REG_SRCONV);
+	return (wait_src_ready(s) & SRC_RAMDATA_MASK) >> SRC_RAMDATA_SHIFT;
+}
+
+
+static void src_write(struct es1371_state *s, unsigned reg, unsigned data)
+{
+	unsigned int r;
+
+	r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC);
+	r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK;
+	r |= (data << SRC_RAMDATA_SHIFT) & SRC_RAMDATA_MASK;
+	outl(r | SRC_WE, s->io + ES1371_REG_SRCONV);
+}
+
+/* --------------------------------------------------------------------- */
+
+/* most of the following here is black magic */
+
+static void set_adc_rate(struct es1371_state *s, unsigned rate)
+{
+	unsigned long flags;
+	unsigned int n, truncm, freq;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+	n = rate / 3000;
+	if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9)))
+		n--;
+	truncm = (21 * n - 1) | 1;
+        freq = ((48000UL << 15) / rate) * n;
+	s->adcrate = (48000UL << 15) / (freq / n);
+	spin_lock_irqsave(&s->lock, flags);
+	if (rate >= 24000) {
+		if (truncm > 239)
+			truncm = 239;
+		src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, 
+			  (((239 - truncm) >> 1) << 9) | (n << 4));
+	} else {
+		if (truncm > 119)
+			truncm = 119;
+		src_write(s, SRCREG_ADC+SRCREG_TRUNC_N, 
+			  0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4));
+	}		
+	src_write(s, SRCREG_ADC+SRCREG_INT_REGS, 
+		  (src_read(s, SRCREG_ADC+SRCREG_INT_REGS) & 0x00ff) |
+		  ((freq >> 5) & 0xfc00));
+	src_write(s, SRCREG_ADC+SRCREG_VFREQ_FRAC, freq & 0x7fff);
+	src_write(s, SRCREG_VOL_ADC, n << 8);
+	src_write(s, SRCREG_VOL_ADC+1, n << 8);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void set_dac1_rate(struct es1371_state *s, unsigned rate)
+{
+	unsigned long flags;
+	unsigned int freq, r;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+        freq = (rate << 15) / 3000;
+	s->dac1rate = (freq * 3000) >> 15;
+	spin_lock_irqsave(&s->lock, flags);
+	r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)) | SRC_DDAC1;
+	outl(r, s->io + ES1371_REG_SRCONV);
+	src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 
+		  (src_read(s, SRCREG_DAC1+SRCREG_INT_REGS) & 0x00ff) |
+		  ((freq >> 5) & 0xfc00));
+	src_write(s, SRCREG_DAC1+SRCREG_VFREQ_FRAC, freq & 0x7fff);
+	r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC));
+	outl(r, s->io + ES1371_REG_SRCONV);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void set_dac2_rate(struct es1371_state *s, unsigned rate)
+{
+	unsigned long flags;
+	unsigned int freq, r;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+        freq = (rate << 15) / 3000;
+	s->dac2rate = (freq * 3000) >> 15;
+	spin_lock_irqsave(&s->lock, flags);
+	r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)) | SRC_DDAC2;
+	outl(r, s->io + ES1371_REG_SRCONV);
+	src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 
+		  (src_read(s, SRCREG_DAC2+SRCREG_INT_REGS) & 0x00ff) |
+		  ((freq >> 5) & 0xfc00));
+	src_write(s, SRCREG_DAC2+SRCREG_VFREQ_FRAC, freq & 0x7fff);
+	r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC));
+	outl(r, s->io + ES1371_REG_SRCONV);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wrcodec(struct es1371_state *s, unsigned addr, unsigned data)
+{
+	unsigned long flags;
+	unsigned t, x;
+
+	for (t = 0; t < 0x1000; t++)
+		if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP))
+			break;
+	spin_lock_irqsave(&s->lock, flags);
+	/* save the current state for later */
+	x = inl(s->io+ES1371_REG_SRCONV);
+	/* enable SRC state data in SRC mux */
+	outl((wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000,
+	     s->io+ES1371_REG_SRCONV);
+	/* wait for a SAFE time to write addr/data and then do it, dammit */
+	for (t = 0; t < 0x1000; t++)
+		if ((inl(s->io+ES1371_REG_SRCONV) & 0x00070000) == 0x00010000)
+			break;
+	outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) |
+	     ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), s->io+ES1371_REG_CODEC);
+	/* restore SRC reg */
+	wait_src_ready(s);
+	outl(x, s->io+ES1371_REG_SRCONV);
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static unsigned rdcodec(struct es1371_state *s, unsigned addr)
+{
+	unsigned long flags;
+	unsigned t, x;
+
+	for (t = 0; t < 0x1000; t++)
+		if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP))
+			break;
+	spin_lock_irqsave(&s->lock, flags);
+	/* save the current state for later */
+	x = inl(s->io+ES1371_REG_SRCONV);
+	/* enable SRC state data in SRC mux */
+	outl((wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000,
+	     s->io+ES1371_REG_SRCONV);
+	/* wait for a SAFE time to write addr/data and then do it, dammit */
+	for (t = 0; t < 0x1000; t++)
+		if ((inl(s->io+ES1371_REG_SRCONV) & 0x00070000) == 0x00010000)
+			break;
+	outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD, s->io+ES1371_REG_CODEC);
+	/* restore SRC reg */
+	wait_src_ready(s);
+	outl(x, s->io+ES1371_REG_SRCONV);
+	spin_unlock_irqrestore(&s->lock, flags);
+	/* now wait for the stinkin' data (RDY) */
+	for (t = 0; t < 0x1000; t++)
+		if ((x = inl(s->io+ES1371_REG_CODEC)) & CODEC_RDY)
+			break;
+	return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT);
+}
+
+/* --------------------------------------------------------------------- */
+
+extern inline void stop_adc(struct es1371_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl &= ~CTRL_ADC_EN;
+	outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+extern inline void stop_dac1(struct es1371_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl &= ~CTRL_DAC1_EN;
+	outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+extern inline void stop_dac2(struct es1371_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->ctrl &= ~CTRL_DAC2_EN;
+	outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac1(struct es1371_state *s)
+{
+	unsigned long flags;
+	unsigned fragremain, fshift;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ctrl & CTRL_DAC1_EN) && (s->dma_dac1.mapped || s->dma_dac1.count > 0)
+	    && s->dma_dac1.ready) {
+		s->ctrl |= CTRL_DAC1_EN;
+		s->sctrl = (s->sctrl & ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD)) | SCTRL_P1INTEN;
+		outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+		fragremain = ((- s->dma_dac1.hwptr) & (s->dma_dac1.fragsize-1));
+		fshift = sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
+		if (fragremain < 2*fshift)
+			fragremain = s->dma_dac1.fragsize;
+		outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT);
+		outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+		outl((s->dma_dac1.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC1_SCOUNT);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac2(struct es1371_state *s)
+{
+	unsigned long flags;
+	unsigned fragremain, fshift;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ctrl & CTRL_DAC2_EN) && (s->dma_dac2.mapped || s->dma_dac2.count > 0)
+	    && s->dma_dac2.ready) {
+		s->ctrl |= CTRL_DAC2_EN;
+		s->sctrl = (s->sctrl & ~(SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN | 
+					 SCTRL_P2ENDINC | SCTRL_P2STINC)) | SCTRL_P2INTEN |
+			(((s->sctrl & SCTRL_P2FMT) ? 2 : 1) << SCTRL_SH_P2ENDINC) | 
+			(0 << SCTRL_SH_P2STINC);
+		outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+		fragremain = ((- s->dma_dac2.hwptr) & (s->dma_dac2.fragsize-1));
+		fshift = sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
+		if (fragremain < 2*fshift)
+			fragremain = s->dma_dac2.fragsize;
+		outl((fragremain >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT);
+		outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+		outl((s->dma_dac2.fragsize >> fshift) - 1, s->io+ES1371_REG_DAC2_SCOUNT);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_adc(struct es1371_state *s)
+{
+	unsigned long flags;
+	unsigned fragremain, fshift;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->ctrl & CTRL_ADC_EN) && (s->dma_adc.mapped || s->dma_adc.count < s->dma_adc.dmasize - 2*s->dma_adc.fragsize)
+	    && s->dma_adc.ready) {
+		s->ctrl |= CTRL_ADC_EN;
+		s->sctrl = (s->sctrl & ~SCTRL_R1LOOPSEL) | SCTRL_R1INTEN;
+		outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+		fragremain = ((- s->dma_adc.hwptr) & (s->dma_adc.fragsize-1));
+		fshift = sample_shift[(s->sctrl & SCTRL_R1FMT) >> SCTRL_SH_R1FMT];
+		if (fragremain < 2*fshift)
+			fragremain = s->dma_adc.fragsize;
+		outl((fragremain >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT);
+		outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+		outl((s->dma_adc.fragsize >> fshift) - 1, s->io+ES1371_REG_ADC_SCOUNT);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER 8
+#define DMABUF_MINORDER 1
+
+
+extern inline void dealloc_dmabuf(struct dmabuf *db)
+{
+	unsigned long map, mapend;
+
+	if (db->rawbuf) {
+		/* undo marking the pages as reserved */
+		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+			clear_bit(PG_reserved, &mem_map[map].flags);	
+		free_pages((unsigned long)db->rawbuf, db->buforder);
+	}
+	db->rawbuf = NULL;
+	db->mapped = db->ready = 0;
+}
+
+static int prog_dmabuf(struct es1371_state *s, struct dmabuf *db, unsigned rate, unsigned fmt, unsigned reg)
+{
+	int order;
+	unsigned bytepersec;
+	unsigned bufs;
+	unsigned long map, mapend;
+
+	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+	if (!db->rawbuf) {
+		db->ready = db->mapped = 0;
+		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--)
+			db->rawbuf = (void *)__get_free_pages(GFP_KERNEL, order);
+		if (!db->rawbuf)
+			return -ENOMEM;
+		db->buforder = order;
+		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+			set_bit(PG_reserved, &mem_map[map].flags);
+	}
+	fmt &= ES1371_FMT_MASK;
+	bytepersec = rate << sample_shift[fmt];
+	bufs = PAGE_SIZE << db->buforder;
+	if (db->ossfragshift) {
+		if ((1000 << db->ossfragshift) < bytepersec)
+			db->fragshift = ld2(bytepersec/1000);
+		else
+			db->fragshift = db->ossfragshift;
+	} else {
+		db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+		if (db->fragshift < 3)
+			db->fragshift = 3;
+	}
+	db->numfrag = bufs >> db->fragshift;
+	while (db->numfrag < 4 && db->fragshift > 3) {
+		db->fragshift--;
+		db->numfrag = bufs >> db->fragshift;
+	}
+	db->fragsize = 1 << db->fragshift;
+	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+		db->numfrag = db->ossmaxfrags;
+	db->fragsamples = db->fragsize >> sample_shift[fmt];
+	db->dmasize = db->numfrag << db->fragshift;
+	memset(db->rawbuf, (fmt & ES1371_FMT_S16) ? 0 : 0x80, db->dmasize);
+	outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE);
+	outl(virt_to_bus(db->rawbuf), s->io+(reg & 0xff));
+	outl((db->dmasize >> 2)-1, s->io+((reg + 4) & 0xff));
+	db->ready = 1;
+	return 0;
+}
+
+extern inline int prog_dmabuf_adc(struct es1371_state *s)
+{
+	stop_adc(s);
+	return prog_dmabuf(s, &s->dma_adc, s->adcrate, (s->sctrl >> SCTRL_SH_R1FMT) & ES1371_FMT_MASK, 
+			   ES1371_REG_ADC_FRAMEADR);
+}
+
+extern inline int prog_dmabuf_dac2(struct es1371_state *s)
+{
+	stop_dac2(s);
+	return prog_dmabuf(s, &s->dma_dac2, s->dac2rate, (s->sctrl >> SCTRL_SH_P2FMT) & ES1371_FMT_MASK, 
+			   ES1371_REG_DAC2_FRAMEADR);
+}
+
+extern inline int prog_dmabuf_dac1(struct es1371_state *s)
+{
+	stop_dac1(s);
+	return prog_dmabuf(s, &s->dma_dac1, s->dac1rate, (s->sctrl >> SCTRL_SH_P1FMT) & ES1371_FMT_MASK,
+			   ES1371_REG_DAC1_FRAMEADR);
+}
+
+extern inline unsigned get_hwptr(struct es1371_state *s, struct dmabuf *db, unsigned reg)
+{
+	unsigned hwptr, diff;
+
+	outl((reg >> 8) & 15, s->io+ES1371_REG_MEMPAGE);
+	hwptr = (inl(s->io+(reg & 0xff)) >> 14) & 0x3fffc;
+	diff = (db->dmasize + hwptr - db->hwptr) % db->dmasize;
+	db->hwptr = hwptr;
+	return diff;
+}
+
+extern inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c)
+{
+	if (bptr + len > bsize) {
+		unsigned x = bsize - bptr;
+		memset(((char *)buf) + bptr, c, x);
+		bptr = 0;
+		len -= x;
+	}
+	memset(((char *)buf) + bptr, c, len);
+}
+
+/* call with spinlock held! */
+static void es1371_update_ptr(struct es1371_state *s)
+{
+	int diff;
+
+	/* update ADC pointer */
+	if (s->ctrl & CTRL_ADC_EN) {
+		diff = get_hwptr(s, &s->dma_adc, ES1371_REG_ADC_FRAMECNT);
+		s->dma_adc.total_bytes += diff;
+		s->dma_adc.count += diff;
+		if (s->dma_adc.mapped) {
+			if (s->dma_adc.count >= s->dma_adc.fragsize) 
+				wake_up(&s->dma_adc.wait);
+		} else {
+			if (s->dma_adc.count > s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1)) {
+				s->ctrl &= ~CTRL_ADC_EN;
+				outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+				s->dma_adc.error++;
+			}
+			if (s->dma_adc.count > 0)
+				wake_up(&s->dma_adc.wait);
+		}
+	}
+	/* update DAC1 pointer */
+	if (s->ctrl & CTRL_DAC1_EN) {
+		diff = get_hwptr(s, &s->dma_dac1, ES1371_REG_DAC1_FRAMECNT);
+		s->dma_dac1.total_bytes += diff;
+		if (s->dma_dac1.mapped) {
+			s->dma_dac1.count += diff;
+			if (s->dma_dac1.count >= s->dma_dac1.fragsize)
+				wake_up(&s->dma_dac1.wait);
+		} else {
+			s->dma_dac1.count -= diff;
+			if (s->dma_dac1.count <= 0) {
+				s->ctrl &= ~CTRL_DAC1_EN;
+				outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+				s->dma_dac1.error++;
+			} else if (s->dma_dac1.count <= s->dma_dac1.fragsize && !s->dma_dac1.endcleared) {
+				clear_advance(s->dma_dac1.rawbuf, s->dma_dac1.dmasize, s->dma_dac1.swptr, 
+					      s->dma_dac1.fragsize, (s->sctrl & SCTRL_P1SEB) ? 0 : 0x80);
+				s->dma_dac1.endcleared = 1;
+			}
+			if (s->dma_dac1.count < s->dma_dac1.dmasize)
+				wake_up(&s->dma_dac1.wait);
+		}
+	}
+	/* update DAC2 pointer */
+	if (s->ctrl & CTRL_DAC2_EN) {
+		diff = get_hwptr(s, &s->dma_dac2, ES1371_REG_DAC2_FRAMECNT);
+		s->dma_dac2.total_bytes += diff;
+		if (s->dma_dac2.mapped) {
+			s->dma_dac2.count += diff;
+			if (s->dma_dac2.count >= s->dma_dac2.fragsize)
+				wake_up(&s->dma_dac2.wait);
+		} else {
+			s->dma_dac2.count -= diff;
+			if (s->dma_dac2.count <= 0) {
+				s->ctrl &= ~CTRL_DAC2_EN;
+				outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+				s->dma_dac2.error++;
+			} else if (s->dma_dac2.count <= s->dma_dac2.fragsize && !s->dma_dac2.endcleared) {
+				clear_advance(s->dma_dac2.rawbuf, s->dma_dac2.dmasize, s->dma_dac2.swptr, 
+					      s->dma_dac2.fragsize, (s->sctrl & SCTRL_P2SEB) ? 0 : 0x80);
+				s->dma_dac2.endcleared = 1;
+			}
+			if (s->dma_dac2.count < s->dma_dac2.dmasize)
+				wake_up(&s->dma_dac2.wait);
+		}
+	}
+}
+
+/* hold spinlock for the following! */
+static void es1371_handle_midi(struct es1371_state *s)
+{
+	unsigned char ch;
+	int wake;
+
+	if (!(s->ctrl & CTRL_UART_EN))
+		return;
+	wake = 0;
+	while (inb(s->io+ES1371_REG_UART_STATUS) & USTAT_RXRDY) {
+		ch = inb(s->io+ES1371_REG_UART_DATA);
+		if (s->midi.icnt < MIDIINBUF) {
+			s->midi.ibuf[s->midi.iwr] = ch;
+			s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF;
+			s->midi.icnt++;
+		}
+		wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.iwait);
+	wake = 0;
+	while ((inb(s->io+ES1371_REG_UART_STATUS) & USTAT_TXRDY) && s->midi.ocnt > 0) {
+		outb(s->midi.obuf[s->midi.ord], s->io+ES1371_REG_UART_DATA);
+		s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF;
+		s->midi.ocnt--;
+		if (s->midi.ocnt < MIDIOUTBUF-16)
+			wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.owait);
+	outb((s->midi.ocnt > 0) ? UCTRL_RXINTEN | UCTRL_ENA_TXINT : UCTRL_RXINTEN, s->io+ES1371_REG_UART_CONTROL);
+}
+
+static void es1371_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        struct es1371_state *s = (struct es1371_state *)dev_id;
+	unsigned int intsrc, sctl;
+	
+	/* fastpath out, to ease interrupt sharing */
+	intsrc = inl(s->io+ES1371_REG_STATUS);
+	if (!(intsrc & 0x80000000))
+		return;
+	spin_lock(&s->lock);
+	/* clear audio interrupts first */
+	sctl = s->sctrl;
+	if (intsrc & STAT_ADC)
+		sctl &= ~SCTRL_R1INTEN;
+	if (intsrc & STAT_DAC1)
+		sctl &= ~SCTRL_P1INTEN;
+	if (intsrc & STAT_DAC2)
+		sctl &= ~SCTRL_P2INTEN;
+	outl(sctl, s->io+ES1371_REG_SERIAL_CONTROL);
+	outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+	es1371_update_ptr(s);
+	es1371_handle_midi(s);
+	spin_unlock(&s->lock);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT "es1371: invalid magic value\n";
+
+#define VALIDATE_STATE(s)                         \
+({                                                \
+	if (!(s) || (s)->magic != ES1371_MAGIC) { \
+		printk(invalid_magic);            \
+		return -ENXIO;                    \
+	}                                         \
+})
+
+/* --------------------------------------------------------------------- */
+
+#define AC97_PESSIMISTIC
+
+/*
+ * this define causes the driver to assume that all optional
+ * AC97 bits are missing. This is what Ensoniq does too in their
+ * Windows driver. Maybe we should one day autoprobe for these
+ * bits. But anyway I have to see an AC97 codec that implements
+ * one of those optional (volume) bits.
+ */
+
+static const unsigned int recsrc[8] = 
+{
+	SOUND_MASK_MIC,
+	SOUND_MASK_CD,
+	SOUND_MASK_VIDEO,
+	SOUND_MASK_LINE1,
+	SOUND_MASK_LINE,
+	SOUND_MASK_VOLUME,
+	SOUND_MASK_PHONEOUT,
+	SOUND_MASK_PHONEIN
+};
+
+static const unsigned char volreg[] = 
+{
+	/* 5 bit stereo */
+	[SOUND_MIXER_LINE] = 0x10,
+	[SOUND_MIXER_CD] = 0x12,
+	[SOUND_MIXER_VIDEO] = 0x14,
+	[SOUND_MIXER_LINE1] = 0x16,
+	[SOUND_MIXER_PCM] = 0x18,
+	/* 6 bit stereo */
+	[SOUND_MIXER_VOLUME] = 0x02,
+	[SOUND_MIXER_PHONEOUT] = 0x04,
+	/* 6 bit mono */
+	[SOUND_MIXER_OGAIN] = 0x06,
+	[SOUND_MIXER_PHONEIN] = 0x0c,
+	/* 4 bit mono but shifted by 1 */
+	[SOUND_MIXER_SPEAKER] = 0x08,
+	/* 6 bit mono + preamp */
+	[SOUND_MIXER_MIC] = 0x0e,
+	/* 4 bit stereo */
+	[SOUND_MIXER_RECLEV] = 0x1c,
+	/* 4 bit mono */
+	[SOUND_MIXER_IGAIN] = 0x1e
+};
+
+#define swab(x) ((((x) >> 8) & 0xff) | (((x) << 8) & 0xff00))
+
+static int mixer_rdch(struct es1371_state *s, unsigned int ch, int *arg)
+{
+	int j;
+
+	switch (ch) {
+	case SOUND_MIXER_MIC:
+		j = rdcodec(s, 0x0e);
+		if (j & 0x8000)
+			return put_user(0, (int *)arg);
+#ifdef AC97_PESSIMISTIC
+		return put_user(0x4949 - 0x202 * (j & 0x1f) + ((j & 0x40) ? 0x1b1b : 0), (int *)arg);
+#else /* AC97_PESSIMISTIC */
+		return put_user(0x5757 - 0x101 * ((j & 0x3f) * 5 / 4) + ((j & 0x40) ? 0x0d0d : 0), (int *)arg);
+#endif /* AC97_PESSIMISTIC */
+
+	case SOUND_MIXER_OGAIN:
+	case SOUND_MIXER_PHONEIN:
+		j = rdcodec(s, volreg[ch]);
+		if (j & 0x8000)
+			return put_user(0, (int *)arg);
+#ifdef AC97_PESSIMISTIC
+		return put_user(0x6464 - 0x303 * (j & 0x1f), (int *)arg);
+#else /* AC97_PESSIMISTIC */
+		return put_user((0x6464 - 0x303 * (j & 0x3f) / 2) & 0x7f7f, (int *)arg);
+#endif /* AC97_PESSIMISTIC */
+
+	case SOUND_MIXER_PHONEOUT:
+		if (!(s->mix.codec_id & CODEC_ID_HEADPHONEOUT))
+			return -EINVAL;
+		/* fall through */
+	case SOUND_MIXER_VOLUME:
+		j = rdcodec(s, volreg[ch]);
+		if (j & 0x8000)
+			return put_user(0, (int *)arg);
+#ifdef AC97_PESSIMISTIC
+		return put_user(0x6464 - (swab(j) & 0x1f1f) * 3, (int *)arg);
+#else /* AC97_PESSIMISTIC */
+		return put_user((0x6464 - (swab(j) & 0x3f3f) * 3 / 2) & 0x7f7f, (int *)arg);
+#endif /* AC97_PESSIMISTIC */
+		
+	case SOUND_MIXER_SPEAKER:
+		j = rdcodec(s, 0x0a);
+		if (j & 0x8000)
+			return put_user(0, (int *)arg);
+		return put_user(0x6464 - ((j >> 1) & 0xf) * 0x606, (int *)arg);
+		
+	case SOUND_MIXER_LINE:
+	case SOUND_MIXER_CD:
+	case SOUND_MIXER_VIDEO:
+	case SOUND_MIXER_LINE1:
+	case SOUND_MIXER_PCM:
+		j = rdcodec(s, volreg[ch]);
+		if (j & 0x8000)
+			return put_user(0, (int *)arg);
+		return put_user(0x6464 - (swab(j) & 0x1f1f) * 3, (int *)arg);
+
+	case SOUND_MIXER_BASS:
+	case SOUND_MIXER_TREBLE:
+		if (!(s->mix.codec_id & CODEC_ID_BASSTREBLE))
+			return -EINVAL;
+		j = rdcodec(s, 0x08);
+		if (ch == SOUND_MIXER_BASS)
+			j >>= 8;
+		return put_user((((j & 15) * 100) / 15) * 0x101, (int *)arg);
+	
+		/* SOUND_MIXER_RECLEV and SOUND_MIXER_IGAIN specify gain */
+	case SOUND_MIXER_RECLEV:
+		j = rdcodec(s, 0x1c);
+		if (j & 0x8000)
+			return put_user(0, (int *)arg);
+		return put_user((swab(j)  & 0xf0f) * 6 + 0xa0a, (int *)arg);
+		
+	case SOUND_MIXER_IGAIN:
+		if (!(s->mix.codec_id & CODEC_ID_DEDICATEDMIC))
+			return -EINVAL;
+		j = rdcodec(s, 0x1e);
+		if (j & 0x8000)
+			return put_user(0, (int *)arg);
+		return put_user((j & 0xf) * 0x606 + 0xa0a, (int *)arg);
+	
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mixer_wrch(struct es1371_state *s, unsigned int ch, int val)
+{
+	int i;
+	unsigned l1, r1;
+
+	l1 = val & 0xff;
+	r1 = (val >> 8) & 0xff;
+	if (l1 > 100)
+		l1 = 100;
+	if (r1 > 100)
+		r1 = 100;
+	switch (ch) {
+	case SOUND_MIXER_LINE:
+	case SOUND_MIXER_CD:
+	case SOUND_MIXER_VIDEO:
+	case SOUND_MIXER_LINE1:
+	case SOUND_MIXER_PCM:
+		if (l1 < 7 && r1 < 7) {
+			wrcodec(s, volreg[ch], 0x8000);
+			return 0;
+		}
+		if (l1 < 7)
+			l1 = 7;
+		if (r1 < 7)
+			r1 = 7;
+		wrcodec(s, volreg[ch], (((100 - l1) / 3) << 8) | ((100 - r1) / 3));
+		return 0;
+
+	case SOUND_MIXER_PHONEOUT:
+		if (!(s->mix.codec_id & CODEC_ID_HEADPHONEOUT))
+			return -EINVAL;
+		/* fall through */
+	case SOUND_MIXER_VOLUME:
+#ifdef AC97_PESSIMISTIC
+		if (l1 < 7 && r1 < 7) {
+			wrcodec(s, volreg[ch], 0x8000);
+			return 0;
+		}
+		if (l1 < 7)
+			l1 = 7;
+		if (r1 < 7)
+			r1 = 7;
+		wrcodec(s, volreg[ch], (((100 - l1) / 3) << 8) | ((100 - r1) / 3));
+		return 0;
+#else /* AC97_PESSIMISTIC */
+		if (l1 < 4 && r1 < 4) {
+			wrcodec(s, volreg[ch], 0x8000);
+			return 0;
+		}
+		if (l1 < 4)
+			l1 = 4;
+		if (r1 < 4)
+			r1 = 4;
+		wrcodec(s, volreg[ch], ((2 * (100 - l1) / 3) << 8) | (2 * (100 - r1) / 3));
+		return 0;
+#endif /* AC97_PESSIMISTIC */
+
+	case SOUND_MIXER_OGAIN:
+	case SOUND_MIXER_PHONEIN:
+#ifdef AC97_PESSIMISTIC
+		wrcodec(s, volreg[ch], (l1 < 7) ? 0x8000 : (100 - l1) / 3);
+		return 0;
+#else /* AC97_PESSIMISTIC */
+		wrcodec(s, volreg[ch], (l1 < 4) ? 0x8000 : (2 * (100 - l1) / 3));
+		return 0;
+#endif /* AC97_PESSIMISTIC */
+			
+	case SOUND_MIXER_SPEAKER:
+		wrcodec(s, 0x0a, (l1 < 10) ? 0x8000 : ((100 - l1) / 6) << 1);
+		return 0;
+
+	case SOUND_MIXER_MIC:
+#ifdef AC97_PESSIMISTIC
+		if (l1 < 11) {
+			wrcodec(s, 0x0e, 0x8000);
+			return 0;
+		}
+		i = 0;
+		if (l1 >= 27) {
+			l1 -= 27;
+			i = 0x40;
+		}
+		if (l1 < 11) 
+			l1 = 11;
+		wrcodec(s, 0x0e, ((73 - l1) / 2) | i);
+		return 0;
+#else /* AC97_PESSIMISTIC */
+		if (l1 < 9) {
+			wrcodec(s, 0x0e, 0x8000);
+			return 0;
+		}
+		i = 0;
+		if (l1 >= 13) {
+			l1 -= 13;
+			i = 0x40;
+		}
+		if (l1 < 9) 
+			l1 = 9;
+		wrcodec(s, 0x0e, (((87 - l1) * 4) / 5) | i);
+		return 0;
+#endif /* AC97_PESSIMISTIC */
+		
+	case SOUND_MIXER_BASS:
+		val = ((l1 * 15) / 100) & 0xf;
+		wrcodec(s, 0x08, (rdcodec(s, 0x08) & 0x00ff) | (val << 8));
+		return 0;
+
+	case SOUND_MIXER_TREBLE:
+		val = ((l1 * 15) / 100) & 0xf;
+		wrcodec(s, 0x08, (rdcodec(s, 0x08) & 0xff00) | val);
+		return 0;
+		
+		/* SOUND_MIXER_RECLEV and SOUND_MIXER_IGAIN specify gain */
+	case SOUND_MIXER_RECLEV:
+		if (l1 < 10 || r1 < 10) {
+			wrcodec(s, 0x1c, 0x8000);
+			return 0;
+		}
+		if (l1 < 10)
+			l1 = 10;
+		if (r1 < 10)
+			r1 = 10;
+		wrcodec(s, 0x1c, (((l1 - 10) / 6) << 8) | ((r1 - 10) / 6));
+		return 0;
+
+	case SOUND_MIXER_IGAIN:
+		if (!(s->mix.codec_id & CODEC_ID_DEDICATEDMIC))
+			return -EINVAL;
+		wrcodec(s, 0x1e, (l1 < 10) ? 0x8000 : ((l1 - 10) / 6) & 0xf);
+		return 0;
+		
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mixer_ioctl(struct es1371_state *s, unsigned int cmd, unsigned long arg)
+{
+	int i, val;
+
+	VALIDATE_STATE(s);
+	if (cmd == SOUND_MIXER_PRIVATE1) {
+		if (!(s->mix.codec_id & (CODEC_ID_SEMASK << CODEC_ID_SESHIFT)))
+			return -EINVAL;
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val & 1)
+			wrcodec(s, 0x22, ((val << 3) & 0xf00) | ((val >> 1) & 0xf));
+		val = rdcodec(s, 0x22);
+		return put_user(((val & 0xf) << 1) | ((val & 0xf00) >> 3), (int *)arg);
+	}
+        if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, "ES1371", sizeof(info.id));
+		strncpy(info.name, "Ensoniq ES1371", sizeof(info.name));
+		info.modify_counter = s->mix.modcnt;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		strncpy(info.id, "ES1371", sizeof(info.id));
+		strncpy(info.name, "Ensoniq ES1371", sizeof(info.name));
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *)arg);
+	if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))
+                return -EINVAL;
+        if (_IOC_DIR(cmd) == _IOC_READ) {
+                switch (_IOC_NR(cmd)) {
+                case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+			return put_user(recsrc[rdcodec(s, 0x1a) & 7], (int *)arg);
+			
+                case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
+			return put_user(SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_VIDEO |
+					SOUND_MASK_LINE1 | SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+					SOUND_MASK_OGAIN | SOUND_MASK_PHONEIN | SOUND_MASK_SPEAKER |
+					SOUND_MASK_MIC | SOUND_MASK_RECLEV |
+					((s->mix.codec_id & CODEC_ID_BASSTREBLE) ? (SOUND_MASK_BASS | SOUND_MASK_TREBLE) : 0) |
+					((s->mix.codec_id & CODEC_ID_HEADPHONEOUT) ? SOUND_MASK_PHONEOUT : 0) |
+					((s->mix.codec_id & CODEC_ID_DEDICATEDMIC) ? SOUND_MASK_IGAIN : 0), (int *)arg);
+
+		case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+			return put_user(SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VIDEO | SOUND_MASK_LINE1 |
+					SOUND_MASK_LINE | SOUND_MASK_VOLUME | SOUND_MASK_PHONEOUT |
+					SOUND_MASK_PHONEIN, (int *)arg);
+
+                case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+			return put_user(SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_VIDEO |
+					SOUND_MASK_LINE1 | SOUND_MASK_PCM | SOUND_MASK_VOLUME |
+					SOUND_MASK_PHONEOUT | SOUND_MASK_RECLEV, (int *)arg);
+
+                case SOUND_MIXER_CAPS:
+			return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg);
+
+		default:
+			i = _IOC_NR(cmd);
+                        if (i >= SOUND_MIXER_NRDEVICES)
+                                return -EINVAL;
+			return mixer_rdch(s, i, (int *)arg);
+		}
+	}
+        if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) 
+		return -EINVAL;
+	s->mix.modcnt++;
+	switch (_IOC_NR(cmd)) {
+	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+                get_user_ret(val, (int *)arg, -EFAULT);
+                i = hweight32(val);
+                if (i == 0)
+                        return 0; /*val = mixer_recmask(s);*/
+                else if (i > 1) 
+                        val &= ~recsrc[rdcodec(s, 0x1a) & 7];
+                for (i = 0; i < 8; i++) {
+			if (val & recsrc[i]) {
+				wrcodec(s, 0x1a, 0x101 * i);
+				return 0;
+			}
+		}
+                return 0;
+
+	default:
+		i = _IOC_NR(cmd);
+		if (i >= SOUND_MIXER_NRDEVICES)
+			return -EINVAL;
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (mixer_wrch(s, i, val))
+			return -EINVAL;
+		return mixer_rdch(s, i, (int *)arg);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static loff_t es1371_llseek(struct file *file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int es1371_open_mixdev(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct es1371_state *s = devs;
+
+	while (s && s->dev_mixer != minor)
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int es1371_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	
+	VALIDATE_STATE(s);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int es1371_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	return mixer_ioctl((struct es1371_state *)file->private_data, cmd, arg);
+}
+
+static /*const*/ struct file_operations es1371_mixer_fops = {
+	&es1371_llseek,
+	NULL,  /* read */
+	NULL,  /* write */
+	NULL,  /* readdir */
+	NULL,  /* poll */
+	&es1371_ioctl_mixdev,
+	NULL,  /* mmap */
+	&es1371_open_mixdev,
+	&es1371_release_mixdev,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac1(struct es1371_state *s, int nonblock)
+{
+        struct wait_queue wait = { current, NULL };
+	unsigned long flags;
+	int count, tmo;
+	
+	if (s->dma_dac1.mapped)
+		return 0;
+        current->state = TASK_INTERRUPTIBLE;
+        add_wait_queue(&s->dma_dac1.wait, &wait);
+        for (;;) {
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac1.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac1.wait, &wait);
+                        current->state = TASK_RUNNING;
+                        return -EBUSY;
+                }
+		tmo = (count * HZ) / s->dac1rate;
+		tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT];
+		current->timeout = jiffies + (tmo ? tmo : 1);
+                schedule();
+		if (tmo && !current->timeout)
+			printk(KERN_DEBUG "es1371: dma timed out??\n");
+		current->timeout = 0;
+        }
+        remove_wait_queue(&s->dma_dac1.wait, &wait);
+        current->state = TASK_RUNNING;
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+static int drain_dac2(struct es1371_state *s, int nonblock)
+{
+        struct wait_queue wait = { current, NULL };
+	unsigned long flags;
+	int count, tmo;
+
+	if (s->dma_dac2.mapped)
+		return 0;
+        current->state = TASK_INTERRUPTIBLE;
+        add_wait_queue(&s->dma_dac2.wait, &wait);
+        for (;;) {
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac2.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac2.wait, &wait);
+                        current->state = TASK_RUNNING;
+                        return -EBUSY;
+                }
+		tmo = (count * HZ) / s->dac2rate;
+		tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT];
+		current->timeout = jiffies + (tmo ? tmo : 1);
+                schedule();
+		if (tmo && !current->timeout)
+			printk(KERN_DEBUG "es1371: dma timed out??\n");
+		current->timeout = 0;
+        }
+        remove_wait_queue(&s->dma_dac2.wait, &wait);
+        current->state = TASK_RUNNING;
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t es1371_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_adc.mapped)
+		return -ENXIO;
+	if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
+		return ret;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		swptr = s->dma_adc.swptr;
+		cnt = s->dma_adc.dmasize-swptr;
+		if (s->dma_adc.count < cnt)
+			cnt = s->dma_adc.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_adc(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->dma_adc.wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_adc.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_adc.swptr = swptr;
+		s->dma_adc.count -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_adc(s);
+	}
+	return ret;
+}
+
+static ssize_t es1371_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac2.mapped)
+		return -ENXIO;
+	if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac2.count < 0) {
+			s->dma_dac2.count = 0;
+			s->dma_dac2.swptr = s->dma_dac2.hwptr;
+		}
+		swptr = s->dma_dac2.swptr;
+		cnt = s->dma_dac2.dmasize-swptr;
+		if (s->dma_dac2.count + cnt > s->dma_dac2.dmasize)
+			cnt = s->dma_dac2.dmasize - s->dma_dac2.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_dac2(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->dma_dac2.wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_from_user(s->dma_dac2.rawbuf + swptr, buffer, cnt))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_dac2.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac2.swptr = swptr;
+		s->dma_dac2.count += cnt;
+		s->dma_dac2.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_dac2(s);
+	}
+	return ret;
+}
+
+static unsigned int es1371_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_flags & FMODE_WRITE)
+		poll_wait(file, &s->dma_dac2.wait, wait);
+	if (file->f_flags & FMODE_READ)
+		poll_wait(file, &s->dma_adc.wait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	es1371_update_ptr(s);
+	if (file->f_flags & FMODE_READ) {
+		if (s->dma_adc.mapped) {
+			if (s->dma_adc.count >= s->dma_adc.fragsize)
+				mask |= POLLIN | POLLRDNORM;
+		} else {
+			if (s->dma_adc.count > 0)
+				mask |= POLLIN | POLLRDNORM;
+		}
+	}
+	if (file->f_flags & FMODE_WRITE) {
+		if (s->dma_dac2.mapped) {
+			if (s->dma_dac2.count >= s->dma_dac2.fragsize) 
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			if (s->dma_dac2.dmasize > s->dma_dac2.count)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int es1371_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	struct dmabuf *db;
+	int ret;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf_dac2(s)) != 0)
+			return ret;
+		db = &s->dma_dac2;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf_adc(s)) != 0)
+			return ret;
+		db = &s->dma_adc;
+	} else 
+		return -EINVAL;
+	if (vma->vm_offset != 0)
+		return -EINVAL;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << db->buforder))
+		return -EINVAL;
+	if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+		return -EAGAIN;
+	db->mapped = 1;
+	vma->vm_file = file;
+	file->f_count++;
+	return 0;
+}
+
+static int es1371_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int val, mapped, ret;
+
+	VALIDATE_STATE(s);
+        mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac2.mapped) ||
+		((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac2(s, 0/*file->f_flags & O_NONBLOCK*/);
+		return 0;
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac2(s);
+			synchronize_irq();
+			s->dma_dac2.swptr = s->dma_dac2.hwptr = s->dma_dac2.count = s->dma_dac2.total_bytes = 0;
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			synchronize_irq();
+			s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val >= 0) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				set_adc_rate(s, val);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac2(s);
+				s->dma_dac2.ready = 0;
+				set_dac2_rate(s, val);
+			}
+		}
+		return put_user((file->f_mode & FMODE_READ) ? s->adcrate : s->dac2rate, (int *)arg);
+
+        case SNDCTL_DSP_STEREO:
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			s->dma_adc.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val)
+				s->sctrl |= SCTRL_R1SMB;
+			else
+				s->sctrl &= ~SCTRL_R1SMB;
+			outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac2(s);
+			s->dma_dac2.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val)
+				s->sctrl |= SCTRL_P2SMB;
+			else
+				s->sctrl &= ~SCTRL_P2SMB;
+			outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+                }
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 0) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val >= 2)
+					s->sctrl |= SCTRL_R1SMB;
+				else
+					s->sctrl &= ~SCTRL_R1SMB;
+				outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac2(s);
+				s->dma_dac2.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val >= 2)
+					s->sctrl |= SCTRL_P2SMB;
+				else
+					s->sctrl &= ~SCTRL_P2SMB;
+				outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+		}
+		return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SMB : SCTRL_P2SMB)) ? 2 : 1, (int *)arg);
+		
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != AFMT_QUERY) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val == AFMT_S16_LE)
+					s->sctrl |= SCTRL_R1SEB;
+				else
+					s->sctrl &= ~SCTRL_R1SEB;
+				outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac2(s);
+				s->dma_dac2.ready = 0;
+				spin_lock_irqsave(&s->lock, flags);
+				if (val == AFMT_S16_LE)
+					s->sctrl |= SCTRL_P2SEB;
+				else
+					s->sctrl &= ~SCTRL_P2SEB;
+				outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+				spin_unlock_irqrestore(&s->lock, flags);
+			}
+		}
+		return put_user((s->sctrl & ((file->f_mode & FMODE_READ) ? SCTRL_R1SEB : SCTRL_P2SEB)) ? 
+				AFMT_S16_LE : AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if (file->f_mode & FMODE_READ && s->ctrl & CTRL_ADC_EN) 
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & FMODE_WRITE && s->ctrl & CTRL_DAC2_EN) 
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_SETTRIGGER:
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
+					return ret;
+				start_adc(s);
+			} else
+				stop_adc(s);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!s->dma_dac2.ready && (ret = prog_dmabuf_dac2(s)))
+					return ret;
+				start_dac2(s);
+			} else
+				stop_dac2(s);
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac2(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+		abinfo.fragsize = s->dma_dac2.fragsize;
+                abinfo.bytes = s->dma_dac2.dmasize - s->dma_dac2.count;
+                abinfo.fragstotal = s->dma_dac2.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac2.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!(s->ctrl & CTRL_ADC_EN) && (val = prog_dmabuf_adc(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+		abinfo.fragsize = s->dma_adc.fragsize;
+                abinfo.bytes = s->dma_adc.count;
+                abinfo.fragstotal = s->dma_adc.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+                val = s->dma_dac2.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return put_user(val, (int *)arg);
+
+        case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+                cinfo.bytes = s->dma_adc.total_bytes;
+                cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift;
+                cinfo.ptr = s->dma_adc.hwptr;
+		if (s->dma_adc.mapped)
+			s->dma_adc.count &= s->dma_adc.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+                cinfo.bytes = s->dma_dac2.total_bytes;
+                cinfo.blocks = s->dma_dac2.total_bytes >> s->dma_dac2.fragshift;
+                cinfo.ptr = s->dma_dac2.hwptr;
+		if (s->dma_dac2.mapped)
+			s->dma_dac2.count &= s->dma_dac2.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf_dac2(s)))
+				return val;
+			return put_user(s->dma_dac2.fragsize, (int *)arg);
+		}
+		if ((val = prog_dmabuf_adc(s)))
+			return val;
+		return put_user(s->dma_adc.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (file->f_mode & FMODE_READ) {
+			s->dma_adc.ossfragshift = val & 0xffff;
+			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_adc.ossfragshift < 4)
+				s->dma_adc.ossfragshift = 4;
+			if (s->dma_adc.ossfragshift > 15)
+				s->dma_adc.ossfragshift = 15;
+			if (s->dma_adc.ossmaxfrags < 4)
+				s->dma_adc.ossmaxfrags = 4;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			s->dma_dac2.ossfragshift = val & 0xffff;
+			s->dma_dac2.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_dac2.ossfragshift < 4)
+				s->dma_dac2.ossfragshift = 4;
+			if (s->dma_dac2.ossfragshift > 15)
+				s->dma_dac2.ossfragshift = 15;
+			if (s->dma_dac2.ossmaxfrags < 4)
+				s->dma_dac2.ossmaxfrags = 4;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+		    (file->f_mode & FMODE_WRITE && s->dma_dac2.subdivision))
+			return -EINVAL;
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			s->dma_adc.subdivision = val;
+		if (file->f_mode & FMODE_WRITE)
+			s->dma_dac2.subdivision = val;
+		return 0;
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_RATE:
+        case SOUND_PCM_READ_CHANNELS:
+        case SOUND_PCM_READ_BITS:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return mixer_ioctl(s, cmd, arg);
+}
+
+static int es1371_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct es1371_state *s = devs;
+	unsigned long flags;
+
+	while (s && ((s->dev_audio ^ minor) & ~0xf))
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & file->f_mode) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	if (file->f_mode & FMODE_READ) {
+		s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
+		set_adc_rate(s, 8000);
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->dma_dac2.ossfragshift = s->dma_dac2.ossmaxfrags = s->dma_dac2.subdivision = 0;
+		set_dac2_rate(s, 8000);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_mode & FMODE_READ) {
+		s->sctrl &= ~SCTRL_R1FMT;
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_R1FMT;
+		else
+			s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_R1FMT;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->sctrl &= ~SCTRL_P2FMT;
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P2FMT;
+		else
+			s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P2FMT;
+	}
+	outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int es1371_release(struct inode *inode, struct file *file)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE)
+		drain_dac2(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	if (file->f_flags & FMODE_WRITE) {
+		stop_dac2(s);
+		dealloc_dmabuf(&s->dma_dac2);
+	}
+	if (file->f_flags & FMODE_READ) {
+		stop_adc(s);
+		dealloc_dmabuf(&s->dma_adc);
+	}
+	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static /*const*/ struct file_operations es1371_audio_fops = {
+	&es1371_llseek,
+	&es1371_read,
+	&es1371_write,
+	NULL,  /* readdir */
+	&es1371_poll,
+	&es1371_ioctl,
+	&es1371_mmap,
+	&es1371_open,
+	&es1371_release,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t es1371_write_dac(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	ssize_t ret = 0;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac1.mapped)
+		return -ENXIO;
+	if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac1.count < 0) {
+			s->dma_dac1.count = 0;
+			s->dma_dac1.swptr = s->dma_dac1.hwptr;
+		}
+		swptr = s->dma_dac1.swptr;
+		cnt = s->dma_dac1.dmasize-swptr;
+		if (s->dma_dac1.count + cnt > s->dma_dac1.dmasize)
+			cnt = s->dma_dac1.dmasize - s->dma_dac1.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_dac1(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->dma_dac1.wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_from_user(s->dma_dac1.rawbuf + swptr, buffer, cnt))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_dac1.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac1.swptr = swptr;
+		s->dma_dac1.count += cnt;
+		s->dma_dac1.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_dac1(s);
+	}
+	return ret;
+}
+
+static unsigned int es1371_poll_dac(struct file *file, struct poll_table_struct *wait)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	poll_wait(file, &s->dma_dac1.wait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	es1371_update_ptr(s);
+	if (s->dma_dac1.mapped) {
+		if (s->dma_dac1.count >= s->dma_dac1.fragsize)
+			mask |= POLLOUT | POLLWRNORM;
+	} else {
+		if (s->dma_dac1.dmasize > s->dma_dac1.count)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int es1371_mmap_dac(struct file *file, struct vm_area_struct *vma)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	int ret;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	if (!(vma->vm_flags & VM_WRITE))
+		return -EINVAL;
+	if ((ret = prog_dmabuf_dac1(s)) != 0)
+		return ret;
+	if (vma->vm_offset != 0)
+		return -EINVAL;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << s->dma_dac1.buforder))
+		return -EINVAL;
+	if (remap_page_range(vma->vm_start, virt_to_phys(s->dma_dac1.rawbuf), size, vma->vm_page_prot))
+		return -EAGAIN;
+	s->dma_dac1.mapped = 1;
+	vma->vm_file = file;
+	file->f_count++;
+	return 0;
+}
+
+static int es1371_ioctl_dac(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int val, ret;
+
+	VALIDATE_STATE(s);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		return drain_dac1(s, 0/*file->f_flags & O_NONBLOCK*/);
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return -EINVAL;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		stop_dac1(s);
+		synchronize_irq();
+		s->dma_dac1.swptr = s->dma_dac1.hwptr = s->dma_dac1.count = s->dma_dac1.total_bytes = 0;
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val >= 0) {
+			stop_dac1(s);
+			s->dma_dac1.ready = 0;
+			set_dac1_rate(s, val);
+		}
+		return put_user(s->dac1rate, (int *)arg);
+
+        case SNDCTL_DSP_STEREO:
+		get_user_ret(val, (int *)arg, -EFAULT);
+		stop_dac1(s);
+		s->dma_dac1.ready = 0;
+		spin_lock_irqsave(&s->lock, flags);
+		if (val)
+			s->sctrl |= SCTRL_P1SMB;
+		else
+			s->sctrl &= ~SCTRL_P1SMB;
+		outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+		spin_unlock_irqrestore(&s->lock, flags);
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 0) {
+			stop_dac1(s);
+			s->dma_dac1.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val >= 2)
+				s->sctrl |= SCTRL_P1SMB;
+			else
+				s->sctrl &= ~SCTRL_P1SMB;
+			outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user((s->sctrl & SCTRL_P1SMB) ? 2 : 1, (int *)arg);
+		
+        case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+		
+        case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != AFMT_QUERY) {
+			stop_dac1(s);
+			s->dma_dac1.ready = 0;
+			spin_lock_irqsave(&s->lock, flags);
+			if (val == AFMT_S16_LE)
+				s->sctrl |= SCTRL_P1SEB;
+			else
+				s->sctrl &= ~SCTRL_P1SEB;
+			outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+			spin_unlock_irqrestore(&s->lock, flags);
+		}
+		return put_user((s->sctrl & SCTRL_P1SEB) ? AFMT_S16_LE : AFMT_U8, (int *)arg);
+
+        case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		return put_user((s->ctrl & CTRL_DAC1_EN) ? PCM_ENABLE_OUTPUT : 0, (int *)arg);
+						
+	case SNDCTL_DSP_SETTRIGGER:
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val & PCM_ENABLE_OUTPUT) {
+			if (!s->dma_dac1.ready && (ret = prog_dmabuf_dac1(s)))
+				return ret;
+			start_dac1(s);
+		} else
+			stop_dac1(s);
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(s->ctrl & CTRL_DAC2_EN) && (val = prog_dmabuf_dac1(s)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+		abinfo.fragsize = s->dma_dac1.fragsize;
+                abinfo.bytes = s->dma_dac1.dmasize - s->dma_dac1.count;
+                abinfo.fragstotal = s->dma_dac1.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac1.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+                val = s->dma_dac1.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return put_user(val, (int *)arg);
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_update_ptr(s);
+                cinfo.bytes = s->dma_dac1.total_bytes;
+                cinfo.blocks = s->dma_dac1.total_bytes >> s->dma_dac1.fragshift;
+                cinfo.ptr = s->dma_dac1.hwptr;
+		if (s->dma_dac1.mapped)
+			s->dma_dac1.count &= s->dma_dac1.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if ((val = prog_dmabuf_dac1(s)))
+			return val;
+                return put_user(s->dma_dac1.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		s->dma_dac1.ossfragshift = val & 0xffff;
+		s->dma_dac1.ossmaxfrags = (val >> 16) & 0xffff;
+		if (s->dma_dac1.ossfragshift < 4)
+			s->dma_dac1.ossfragshift = 4;
+		if (s->dma_dac1.ossfragshift > 15)
+			s->dma_dac1.ossfragshift = 15;
+		if (s->dma_dac1.ossmaxfrags < 4)
+			s->dma_dac1.ossmaxfrags = 4;
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if (s->dma_dac1.subdivision)
+			return -EINVAL;
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		s->dma_dac1.subdivision = val;
+		return 0;
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_RATE:
+        case SOUND_PCM_READ_CHANNELS:
+        case SOUND_PCM_READ_BITS:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return mixer_ioctl(s, cmd, arg);
+}
+
+static int es1371_open_dac(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct es1371_state *s = devs;
+	unsigned long flags;
+
+	while (s && ((s->dev_dac ^ minor) & ~0xf))
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+       	/* we allow opening with O_RDWR, most programs do it although they will only write */
+#if 0
+	if (file->f_mode & FMODE_READ)
+		return -EPERM;
+#endif
+	if (!(file->f_mode & FMODE_WRITE))
+		return -EINVAL;
+       	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & FMODE_DAC) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	s->dma_dac1.ossfragshift = s->dma_dac1.ossmaxfrags = s->dma_dac1.subdivision = 0;
+	set_dac1_rate(s, 8000);
+	spin_lock_irqsave(&s->lock, flags);
+	s->sctrl &= ~SCTRL_P1FMT;
+	if ((minor & 0xf) == SND_DEV_DSP16)
+		s->sctrl |= ES1371_FMT_S16_MONO << SCTRL_SH_P1FMT;
+	else
+		s->sctrl |= ES1371_FMT_U8_MONO << SCTRL_SH_P1FMT;
+	outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= FMODE_DAC;
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int es1371_release_dac(struct inode *inode, struct file *file)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	drain_dac1(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	stop_dac1(s);
+	dealloc_dmabuf(&s->dma_dac1);
+	s->open_mode &= ~FMODE_DAC;
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static /*const*/ struct file_operations es1371_dac_fops = {
+	&es1371_llseek,
+	NULL,  /* read */
+	&es1371_write_dac,
+	NULL,  /* readdir */
+	&es1371_poll_dac,
+	&es1371_ioctl_dac,
+	&es1371_mmap_dac,
+	&es1371_open_dac,
+	&es1371_release_dac,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.ird;
+		cnt = MIDIINBUF - ptr;
+		if (s->midi.icnt < cnt)
+			cnt = s->midi.icnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->midi.iwait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt))
+			return ret ? ret : -EFAULT;
+		ptr = (ptr + cnt) % MIDIINBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.ird = ptr;
+		s->midi.icnt -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+	}
+	return ret;
+}
+
+static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.owr;
+		cnt = MIDIOUTBUF - ptr;
+		if (s->midi.ocnt + cnt > MIDIOUTBUF)
+			cnt = MIDIOUTBUF - s->midi.ocnt;
+		if (cnt <= 0)
+			es1371_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->midi.owait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt))
+			return ret ? ret : -EFAULT;
+		ptr = (ptr + cnt) % MIDIOUTBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.owr = ptr;
+		s->midi.ocnt += cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		spin_lock_irqsave(&s->lock, flags);
+		es1371_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+	}
+	return ret;
+}
+
+static unsigned int es1371_midi_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_flags & FMODE_WRITE)
+		poll_wait(file, &s->midi.owait, wait);
+	if (file->f_flags & FMODE_READ)
+		poll_wait(file, &s->midi.iwait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_flags & FMODE_READ) {
+		if (s->midi.icnt > 0)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_flags & FMODE_WRITE) {
+		if (s->midi.ocnt < MIDIOUTBUF)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int es1371_midi_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct es1371_state *s = devs;
+	unsigned long flags;
+
+	while (s && s->dev_midi != minor)
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+		outb(UCTRL_CNTRL_SWR, s->io+ES1371_REG_UART_CONTROL);
+		outb(0, s->io+ES1371_REG_UART_CONTROL);
+		outb(0, s->io+ES1371_REG_UART_TEST);
+	}
+	if (file->f_mode & FMODE_READ) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+	}
+	s->ctrl |= CTRL_UART_EN;
+	outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	es1371_handle_midi(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int es1371_midi_release(struct inode *inode, struct file *file)
+{
+	struct es1371_state *s = (struct es1371_state *)file->private_data;
+        struct wait_queue wait = { current, NULL };
+	unsigned long flags;
+	unsigned count, tmo;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE) {
+		current->state = TASK_INTERRUPTIBLE;
+		add_wait_queue(&s->midi.owait, &wait);
+		for (;;) {
+			spin_lock_irqsave(&s->lock, flags);
+			count = s->midi.ocnt;
+			spin_unlock_irqrestore(&s->lock, flags);
+			if (count <= 0)
+				break;
+			if (signal_pending(current))
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				remove_wait_queue(&s->midi.owait, &wait);
+				current->state = TASK_RUNNING;
+				return -EBUSY;
+			}
+			tmo = (count * HZ) / 3100;
+			current->timeout = tmo ? jiffies + tmo : 0;
+			schedule();
+			if (tmo && !current->timeout)
+				printk(KERN_DEBUG "es1371: midi timed out??\n");
+			current->timeout = 0;
+		}
+		remove_wait_queue(&s->midi.owait, &wait);
+		current->state = TASK_RUNNING;
+	}
+	down(&s->open_sem);
+	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->ctrl &= ~CTRL_UART_EN;
+		outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static /*const*/ struct file_operations es1371_midi_fops = {
+	&es1371_llseek,
+	&es1371_midi_read,
+	&es1371_midi_write,
+	NULL,  /* readdir */
+	&es1371_midi_poll,
+	NULL,  /* ioctl */
+	NULL,  /* mmap */
+	&es1371_midi_open,
+	&es1371_midi_release,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+/* maximum number of devices */
+#define NR_DEVICE 5
+
+static int joystick[NR_DEVICE] = { 0, };
+
+/* --------------------------------------------------------------------- */
+
+static const struct initvol {
+	int mixch;
+	int vol;
+} initvol[] __initdata = {
+	{ SOUND_MIXER_WRITE_LINE, 0x4040 },
+	{ SOUND_MIXER_WRITE_CD, 0x4040 },
+	{ MIXER_WRITE(SOUND_MIXER_VIDEO), 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE1, 0x4040 },
+	{ SOUND_MIXER_WRITE_PCM, 0x4040 },
+	{ SOUND_MIXER_WRITE_VOLUME, 0x4040 },
+	{ MIXER_WRITE(SOUND_MIXER_PHONEOUT), 0x4040 },
+	{ SOUND_MIXER_WRITE_OGAIN, 0x4040 },
+	{ MIXER_WRITE(SOUND_MIXER_PHONEIN), 0x4040 },
+	{ SOUND_MIXER_WRITE_SPEAKER, 0x4040 },
+	{ SOUND_MIXER_WRITE_MIC, 0x4040 },
+	{ SOUND_MIXER_WRITE_RECLEV, 0x4040 },
+	{ SOUND_MIXER_WRITE_IGAIN, 0x4040 }
+};
+
+#ifdef MODULE
+__initfunc(int init_module(void))
+#else
+__initfunc(int init_es1371(void))
+#endif
+{
+	struct es1371_state *s;
+	struct pci_dev *pcidev = NULL;
+	mm_segment_t fs;
+	int i, val, val2, index = 0;
+
+	if (!pci_present())   /* No PCI bus in this machine! */
+		return -ENODEV;
+	printk(KERN_INFO "es1371: version v0.2 time " __TIME__ " " __DATE__ "\n");
+	while (index < NR_DEVICE && 
+	       (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) {
+		if (pcidev->base_address[0] == 0 || 
+		    (pcidev->base_address[0] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
+			continue;
+		if (pcidev->irq == 0) 
+			continue;
+		if (!(s = kmalloc(sizeof(struct es1371_state), GFP_KERNEL))) {
+			printk(KERN_WARNING "es1371: out of memory\n");
+			continue;
+		}
+		memset(s, 0, sizeof(struct es1371_state));
+		init_waitqueue(&s->dma_adc.wait);
+		init_waitqueue(&s->dma_dac1.wait);
+		init_waitqueue(&s->dma_dac2.wait);
+		init_waitqueue(&s->open_wait);
+		init_waitqueue(&s->midi.iwait);
+		init_waitqueue(&s->midi.owait);
+		s->open_sem = MUTEX;
+		s->magic = ES1371_MAGIC;
+		s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+		s->irq = pcidev->irq;
+		if (check_region(s->io, ES1371_EXTENT)) {
+			printk(KERN_ERR "es1371: io ports %#x-%#x in use\n", s->io, s->io+ES1371_EXTENT-1);
+			goto err_region;
+		}
+		request_region(s->io, ES1371_EXTENT, "es1371");
+		if (request_irq(s->irq, es1371_interrupt, SA_INTERRUPT|SA_SHIRQ, "es1371", s)) {
+			printk(KERN_ERR "es1371: irq %u in use\n", s->irq);
+			goto err_irq;
+		}
+		printk(KERN_INFO "es1371: found adapter at io %#06x irq %u\n"
+		       KERN_INFO "es1371: features: joystick 0x%x\n", s->io, s->irq, joystick[index]);
+		/* register devices */
+		if ((s->dev_audio = register_sound_dsp(&es1371_audio_fops)) < 0)
+			goto err_dev1;
+		if ((s->dev_mixer = register_sound_mixer(&es1371_mixer_fops)) < 0)
+			goto err_dev2;
+		if ((s->dev_dac = register_sound_dsp(&es1371_dac_fops)) < 0)
+			goto err_dev3;
+		if ((s->dev_midi = register_sound_midi(&es1371_midi_fops)) < 0)
+			goto err_dev4;
+		/* initialize codec registers */
+		s->ctrl = 0;
+		if ((joystick[index] & ~0x18) == 0x200) {
+			if (check_region(joystick[index], JOY_EXTENT))
+				printk(KERN_ERR "es1371: joystick address 0x%x already in use\n", joystick[index]);
+			else {
+				s->ctrl |= CTRL_JYSTK_EN | (((joystick[index] >> 3) & CTRL_JOY_MASK) << CTRL_JOY_SHIFT);
+				request_region(joystick[index], JOY_EXTENT, "es1371");
+			}
+		}
+		s->sctrl = 0;
+		/* initialize the chips */
+		outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+		outl(s->sctrl, s->io+ES1371_REG_SERIAL_CONTROL);
+		outl(0, s->io+ES1371_REG_LEGACY);
+		/* AC97 warm reset to start the bitclk */
+		outl(s->ctrl | CTRL_SYNCRES, s->io+ES1371_REG_CONTROL);
+		udelay(2);
+		outl(s->ctrl, s->io+ES1371_REG_CONTROL);
+		/* init the sample rate converter */
+		outl(SRC_DIS, s->io + ES1371_REG_SRCONV);
+		for (val = 0; val < 0x80; val++)
+			src_write(s, val, 0);
+		src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4);
+		src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10);
+		src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4);
+		src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10);
+		src_write(s, SRCREG_VOL_ADC, 1 << 12);
+		src_write(s, SRCREG_VOL_ADC+1, 1 << 12);
+		src_write(s, SRCREG_VOL_DAC1, 1 << 12);
+		src_write(s, SRCREG_VOL_DAC1+1, 1 << 12);
+		src_write(s, SRCREG_VOL_DAC2, 1 << 12);
+		src_write(s, SRCREG_VOL_DAC2+1, 1 << 12);
+		set_adc_rate(s, 22050);
+		set_dac1_rate(s, 22050);
+		set_dac2_rate(s, 22050);
+		/* WARNING:
+		 * enabling the sample rate converter without properly programming
+		 * its parameters causes the chip to lock up (the SRC busy bit will
+		 * be stuck high, and I've found no way to rectify this other than
+		 * power cycle)
+		 */
+		outl(0, s->io+ES1371_REG_SRCONV);
+		/* codec init */
+		wrcodec(s, 0x00, 0); /* reset codec */
+		s->mix.codec_id = rdcodec(s, 0x00);  /* get codec ID */
+		val = rdcodec(s, 0x7c);
+		val2 = rdcodec(s, 0x7e);
+		printk(KERN_INFO "es1371: codec vendor %c%c%c revision %d\n", 
+		       (val >> 8) & 0xff, val & 0xff, (val2 >> 8) & 0xff, val2 & 0xff);
+		printk(KERN_INFO "es1371: codec features");
+		if (s->mix.codec_id & CODEC_ID_DEDICATEDMIC)
+			printk(" dedicated MIC PCM in");
+		if (s->mix.codec_id & CODEC_ID_MODEMCODEC)
+			printk(" Modem Line Codec");
+		if (s->mix.codec_id & CODEC_ID_BASSTREBLE)
+			printk(" Bass & Treble");
+		if (s->mix.codec_id & CODEC_ID_SIMULATEDSTEREO)
+			printk(" Simulated Stereo");
+		if (s->mix.codec_id & CODEC_ID_HEADPHONEOUT)
+			printk(" Headphone out");
+		if (s->mix.codec_id & CODEC_ID_LOUDNESS)
+			printk(" Loudness");
+		if (s->mix.codec_id & CODEC_ID_18BITDAC)
+			printk(" 18bit DAC");
+		if (s->mix.codec_id & CODEC_ID_20BITDAC)
+			printk(" 20bit DAC");
+		if (s->mix.codec_id & CODEC_ID_18BITADC)
+			printk(" 18bit ADC");
+		if (s->mix.codec_id & CODEC_ID_20BITADC)
+			printk(" 20bit ADC");
+		printk("%s\n", (s->mix.codec_id & 0x3ff) ? "" : " none");
+		val = (s->mix.codec_id >> CODEC_ID_SESHIFT) & CODEC_ID_SEMASK;
+		printk(KERN_INFO "es1371: stereo enhancement: %s\n", (val <= 20) ? stereo_enhancement[val] : "unknown");
+		fs = get_fs();
+		set_fs(KERNEL_DS);
+		val = SOUND_MASK_LINE;
+		mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+		for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
+			val = initvol[i].vol;
+			mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
+		}
+		set_fs(fs);
+		/* queue it for later freeing */
+		s->next = devs;
+		devs = s;
+		index++;
+		continue;
+
+	err_dev4:
+		unregister_sound_dsp(s->dev_dac);
+	err_dev3:
+		unregister_sound_mixer(s->dev_mixer);
+	err_dev2:
+		unregister_sound_dsp(s->dev_audio);
+	err_dev1:
+		printk(KERN_ERR "es1371: cannot register misc device\n");
+		free_irq(s->irq, s);
+	err_irq:
+		release_region(s->io, ES1371_EXTENT);
+	err_region:
+		kfree_s(s, sizeof(struct es1371_state));
+	}
+	if (!devs)
+		return -ENODEV;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+MODULE_PARM(joystick, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)");
+MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out");
+MODULE_PARM(micz, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(micz, "changes (??) the microphone impedance");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("ES1371 AudioPCI97 Driver");
+
+void cleanup_module(void)
+{
+	struct es1371_state *s;
+
+	while ((s = devs)) {
+		devs = devs->next;
+		outl(0, s->io+ES1371_REG_CONTROL); /* switch everything off */
+		outl(0, s->io+ES1371_REG_SERIAL_CONTROL); /* clear serial interrupts */
+		synchronize_irq();
+		free_irq(s->irq, s);
+		release_region(s->io, ES1371_EXTENT);
+		if (s->ctrl & CTRL_JYSTK_EN)
+			release_region(((((s->ctrl >> CTRL_JOY_SHIFT) & CTRL_JOY_MASK) << 3) | 0x200), JOY_EXTENT);
+		unregister_sound_dsp(s->dev_audio);
+		unregister_sound_mixer(s->dev_mixer);
+		unregister_sound_dsp(s->dev_dac);
+		unregister_sound_midi(s->dev_midi);
+		kfree_s(s, sizeof(struct es1371_state));
+	}
+	printk(KERN_INFO "es1371: unloading\n");
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/lowlevel/README.awe linux/drivers/sound/lowlevel/README.awe
--- v2.1.108/linux/drivers/sound/lowlevel/README.awe	Sat Nov 29 10:33:20 1997
+++ linux/drivers/sound/lowlevel/README.awe	Fri Jul 10 14:03:36 1998
@@ -1,12 +1,12 @@
 ================================================================
-	AWE32 Sound Driver for Linux / FreeBSD
+	AWE32 Sound Driver for Linux and FreeBSD
 		version 0.4.2c; Oct. 7, 1997
 ================================================================
 
 * GENERAL NOTES
 
-This is a sound driver extension for SoundBlaster AWE32 and other
-compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64 & etc) to enable
+This is a sound driver extension for the Sound Blaster AWE32 and other
+compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64, etc.) to enable
 the wave synth operations.  The driver is provided for both Linux
 1.2.x and 2.[01].x kernels, and also FreeBSD on Intel x86 and DEC
 Alpha systems.   See INSTALL.awe (or INSTALL.fbsd) document for
@@ -120,8 +120,8 @@
 [Sample Table Size]
 From ver.0.4.0, sample tables are allocated dynamically (except
 Linux-1.2.x system), so you need NOT to touch these parameters.
-Linux-1.2.x users may need to increase these values to apropriate size 
-if larger DRAM is equipped with the soundcard.
+Linux-1.2.x users may need to increase these values for sound 
+cards equipped with more DRAM.
 
 - AWE_MAX_SF_LISTS, AWE_MAX_SAMPLES, AWE_MAX_INFOS
 
@@ -135,7 +135,7 @@
     passthrough channels. 
 
 - AWE_DEBUG_ON			(default: defined)
-    turns on debuggin messages if defined.
+    turns on debugging messages if defined.
 
 - AWE_HAS_GUS_COMPATIBILITY	(default: defined)
     Enables GUS compatibility mode if defined, reading GUS patches and 
@@ -166,26 +166,26 @@
 
 * ACKNOWLEDGMENTS
 
-Thanks to Witold Jachimczyk (witek@xfactor.wpi.edu) for many advices
-to programming of AWE32.  Many codes are brought from his AWE32-native 
+Thanks to Witold Jachimczyk (witek@xfactor.wpi.edu) for much advice
+on programming of AWE32.  Much code is brought from his AWE32-native 
 MOD player, ALMP.
-The port of awedrv to FreeBSD is done by Randall Hopper
+The port of awedrv to FreeBSD was done by Randall Hopper
 (rhh@ct.picker.com).
 I also thank linux-awe-ml members for their efforts
-to reboot their system many times :-)
+to reboot their systems many times. :-)
 
 
 * BUGS & TODO'S
 
-- Can't detect DRAM size on some card
-- More smart patch management
-- More smart DRAM memory control
-- etc, etc, etc.
+- can't detect DRAM size on some cards
+- smarter patch management
+- smarter DRAM memory control
+- etc., etc., etc.
 
 
 * COPYRIGHT
 
-Copyright (C) 1996,1997 Takashi Iwai
+Copyright (C) 1996, 1997 Takashi Iwai
 
 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
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/lowlevel/aci.c linux/drivers/sound/lowlevel/aci.c
--- v2.1.108/linux/drivers/sound/lowlevel/aci.c	Mon Feb 23 18:12:09 1998
+++ linux/drivers/sound/lowlevel/aci.c	Fri Jul 10 14:03:36 1998
@@ -13,7 +13,7 @@
  * software.
  * 
  * This Voxware ACI driver currently only supports the ACI functions
- * on the miroSOUND PCM12 card. Support for miro soundcards with
+ * on the miroSOUND PCM12 card. Support for miro sound cards with
  * additional ACI functions can easily be added later.
  *
  * Revision history:
@@ -76,6 +76,7 @@
 
 #ifdef MODULE                  /* Whether the aci mixer is to be reset.    */
 int aci_reset = 0;             /* Default: don't reset if the driver is a  */
+MODULE_PARM(aci_reset,"i");
 #else                          /* module; use "insmod sound.o aci_reset=1" */
 int aci_reset = 1;             /* to override.                             */
 #endif
@@ -88,7 +89,7 @@
 /*
  * Wait until the ACI microcontroller has set the READYFLAG in the
  * Busy/IRQ Source Register to 0. This is required to avoid
- * overrunning the soundcard microcontroller. We do a busy wait here,
+ * overrunning the sound card microcontroller. We do a busy wait here,
  * because the microcontroller is not supposed to signal a busy
  * condition for more than a few clock cycles. In case of a time-out,
  * this function returns -1.
@@ -491,7 +492,7 @@
   }
 
   if (aci_idcode[0] == 0x6d) {
-    /* it looks like a miro soundcard */
+    /* It looks like a miro sound card. */
     switch (aci_idcode[1]) {
     case 0x41:
       boardname = "PCM1 pro / early PCM12";
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/lowlevel/awe_wave.c linux/drivers/sound/lowlevel/awe_wave.c
--- v2.1.108/linux/drivers/sound/lowlevel/awe_wave.c	Wed Dec 10 09:45:16 1997
+++ linux/drivers/sound/lowlevel/awe_wave.c	Fri Jul 10 14:03:36 1998
@@ -48,7 +48,7 @@
 #endif
 
 #ifdef AWE_HAS_GUS_COMPATIBILITY
-/* include finetune table */
+/* include fine tuning table */
 #ifdef AWE_OBSOLETE_VOXWARE
 #  ifdef __FreeBSD__
 #    define SEQUENCER_C
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/mad16.c linux/drivers/sound/mad16.c
--- v2.1.108/linux/drivers/sound/mad16.c	Tue Jun  9 11:57:30 1998
+++ linux/drivers/sound/mad16.c	Fri Jul 10 14:03:36 1998
@@ -16,7 +16,7 @@
  *      OAK OTI-601D    Mozart
  *      OPTi 82C929     MAD16 Pro
  *      OPTi 82C930
- *      OPTi 82C924     (in non PnP mode)
+ *      OPTi 82C924
  *
  * These audio interface chips don't produce sound themselves. They just
  * connect some other components (OPL-[234] and a WSS compatible codec)
@@ -60,6 +60,11 @@
  *	Changes
  *	
  *	Alan Cox		Clean up, added module selections.
+ *
+ *	A. Wik			Added support for Opti924 PnP.
+ *				Improved debugging support.	16-May-1998
+ *				Fixed bug.			16-Jun-1998
+ *
  */
 
 #include "sound_config.h"
@@ -116,11 +121,14 @@
 static int      board_type = C928;
 
 static int     *mad16_osp;
-static int	c931_detected;	/* minor diferences from C930 */
+static int	c931_detected;	/* minor differences from C930 */
+static char	c924pnp = 0;	/* "     "           "    C924 */
+static int	debug = 0;	/* debugging output */
 
-#ifndef DDB
-#define DDB(x)
+#ifdef DDB
+#undef DDB
 #endif
+#define DDB(x) {if (debug) x;}
 
 static unsigned char mad_read(int port)
 {
@@ -146,7 +154,11 @@
 			break;
 
 		case C924:
-			outb((0xE5), PASSWD_REG);
+			/* the c924 has its ports relocated by -128 if
+			   PnP is enabled  -aw */
+			if (!c924pnp)
+				outb((0xE5), PASSWD_REG); else
+				outb((0xE5), PASSWD_REG - 0x80);
 			break;
 	}
 
@@ -156,7 +168,9 @@
 		tmp = inb(0xe0f);	/* Read from data reg */
 	}
 	else
-		tmp = inb(port);
+		if (!c924pnp)
+			tmp = inb(port); else
+			tmp = inb(port-0x80);
 	restore_flags(flags);
 
 	return tmp;
@@ -185,7 +199,9 @@
 			break;
 
 		case C924:
-			outb((0xE5), PASSWD_REG);
+			if (!c924pnp)
+				outb((0xE5), PASSWD_REG); else
+				outb((0xE5), PASSWD_REG - 0x80);
 			break;
 	}
 
@@ -195,7 +211,9 @@
 		outb(((unsigned char) (value & 0xff)), 0xe0f);
 	}
 	else
-		outb(((unsigned char) (value & 0xff)), port);
+		if (!c924pnp)
+			outb(((unsigned char) (value & 0xff)), port); else
+			outb(((unsigned char) (value & 0xff)), port-0x80);
 	restore_flags(flags);
 }
 
@@ -261,7 +279,7 @@
 	if ((mad_read(MC0_PORT+13) & 0x80) == 0)
 		return 1;
 	
-	/* Force off PnP mode, This is not recommended because
+	/* Force off PnP mode. This is not recommended because
 	 * the PnP bios will not recognize the chip on the next
 	 * warm boot and may assignd different resources to other
 	 * PnP/PCI cards.
@@ -278,8 +296,8 @@
 	/*
 	 * Check that reading a register doesn't return bus float (0xff)
 	 * when the card is accessed using password. This may fail in case
-	 * the card is in low power mode. Normally at least the power saving mode
-	 * bit should be 0.
+	 * the card is in low power mode. Normally at least the power saving
+	 * mode bit should be 0.
 	 */
 
 	if ((tmp = mad_read(MC1_PORT)) == 0xff)
@@ -288,7 +306,9 @@
 		return 0;
 	}
 	for (i = 0xf8d; i <= 0xf98; i++)
-		DDB(printk("Port %0x (init value) = %0x\n", i, mad_read(i)));
+		if (!c924pnp)
+			DDB(printk("Port %0x (init value) = %0x\n", i, mad_read(i))) else
+			DDB(printk("Port %0x (init value) = %0x\n", i-0x80, mad_read(i)));
 
 	if (board_type == C930)
 		return detect_c930();
@@ -435,9 +455,15 @@
 	board_type = C924;
 
 	DDB(printk("Detect using password = 0xE5\n"));
+	
+	if (!detect_mad16()) {
+		c924pnp++;
+		DDB(printk("Detect using password = 0xE5 (again), port offset -0x80\n"));
+	}
 
 	if (!detect_mad16())	/* No luck. Try different model */
 	{
+		c924pnp=0;
 		board_type = C928;
 
 		DDB(printk("Detect using password = 0xE2\n"));
@@ -471,23 +497,15 @@
 					return 0;
 
 				DDB(printk("mad16.c: 82C930 detected\n"));
-			}
-			else
-			{
+			} else
 				DDB(printk("mad16.c: 82C929 detected\n"));
-			}
-		}
-		else
-		{
+		} else {
 			unsigned char model;
 
-			if (((model = mad_read(MC3_PORT)) & 0x03) == 0x03)
-			{
+			if (((model = mad_read(MC3_PORT)) & 0x03) == 0x03) {
 				DDB(printk("mad16.c: Mozart detected\n"));
 				board_type = MOZART;
-			}
-			else
-			{
+			} else {
 				DDB(printk("mad16.c: 82C928 detected???\n"));
 				board_type = C928;
 			}
@@ -527,7 +545,9 @@
 
 
 	for (i = 0xf8d; i <= 0xf93; i++)
-		DDB(printk("port %03x = %02x\n", i, mad_read(i)));
+		if (!c924pnp)
+			DDB(printk("port %03x = %02x\n", i, mad_read(i))) else
+			DDB(printk("port %03x = %02x\n", i-0x80, mad_read(i)));
 
 /*
  * Set the WSS address
@@ -596,9 +616,9 @@
 		mad_write(MC5_PORT, 0x30 | cs4231_mode);
 	}
 
-	for (i = 0xf8d; i <= 0xf93; i++)
-		DDB(printk("port %03x after init = %02x\n", i, mad_read(i)));
-
+	for (i = 0xf8d; i <= 0xf93; i++) if (!c924pnp)
+		DDB(printk("port %03x after init = %02x\n", i, mad_read(i))) else
+		DDB(printk("port %03x after init = %02x\n", i-0x80, mad_read(i)));
 	wss_init(hw_config);
 
 	return 1;
@@ -814,11 +834,14 @@
 
 void unload_mad16(struct address_info *hw_config)
 {
+	int mixer = audio_devs[hw_config->slots[0]]->mixer_dev;
 	ad1848_unload(hw_config->io_base + 4,
 			hw_config->irq,
 			hw_config->dma,
 			hw_config->dma2, 0);
 	release_region(hw_config->io_base, 4);
+	if(mixer>=0)
+		sound_unload_mixerdev(mixer);
 	sound_unload_audiodev(hw_config->slots[0]);
 
 }
@@ -867,6 +890,7 @@
 MODULE_PARM(cddma,"i");
 MODULE_PARM(opl4,"i");
 MODULE_PARM(joystick,"i");
+MODULE_PARM(debug,"i");
 
 EXPORT_NO_SYMBOLS;
 
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/msnd.c linux/drivers/sound/msnd.c
--- v2.1.108/linux/drivers/sound/msnd.c	Wed Jul  1 19:38:55 1998
+++ linux/drivers/sound/msnd.c	Fri Jul 10 14:03:36 1998
@@ -2,7 +2,7 @@
  *
  * msnd.c - Driver Base
  *
- * Turtle Beach MultiSound Soundcard Driver for Linux
+ * Turtle Beach MultiSound Sound Card Driver for Linux
  *
  * Copyright (C) 1998 Andrew Veliath
  *
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/msnd.h linux/drivers/sound/msnd.h
--- v2.1.108/linux/drivers/sound/msnd.h	Tue Jun 23 10:01:24 1998
+++ linux/drivers/sound/msnd.h	Fri Jul 10 14:03:36 1998
@@ -2,7 +2,7 @@
  *
  * msnd.h
  *
- * Turtle Beach MultiSound Soundcard Driver for Linux
+ * Turtle Beach MultiSound Sound Card Driver for Linux
  *
  * Some parts of this header file were derived from the Turtle Beach
  * MultiSound Driver Development Kit.
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/msnd_classic.c linux/drivers/sound/msnd_classic.c
--- v2.1.108/linux/drivers/sound/msnd_classic.c	Wed Jul  1 19:38:55 1998
+++ linux/drivers/sound/msnd_classic.c	Mon Jul 13 12:43:04 1998
@@ -2,7 +2,7 @@
  *
  * msnd_classic.c - Support for Turtle Beach Classic/Monterey/Tahiti
  *
- * Turtle Beach MultiSound Soundcard Driver for Linux
+ * Turtle Beach MultiSound Sound Card Driver for Linux
  *
  * Copyright (C) 1998 Andrew Veliath
  *
@@ -24,6 +24,7 @@
  *
  ********************************************************************/
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/malloc.h>
 #include <linux/types.h>
@@ -1161,10 +1162,18 @@
 static int irq __initdata =		-1;
 static int mem __initdata =		-1;
 static int fifosize __initdata =	DEFFIFOSIZE;
-static int
-calibrate_signal __initdata =		0;
+static int calibrate_signal __initdata;
 
 int init_module(void)
+#else
+static int io __initdata =		CONFIG_MSNDCLAS_IO;
+static int irq __initdata =		CONFIG_MSNDCLAS_IRQ;
+static int mem __initdata =		CONFIG_MSNDCLAS_MEM;
+static int fifosize __initdata =	DEFFIFOSIZE;
+static int calibrate_signal __initdata;
+
+__initfunc(msnd_classic_init(void))
+#endif
 {
 	int err;
 
@@ -1291,6 +1300,8 @@
 
 	return 0;
 }
+
+#ifdef MODULE
 
 void cleanup_module(void)
 {
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/msnd_classic.h linux/drivers/sound/msnd_classic.h
--- v2.1.108/linux/drivers/sound/msnd_classic.h	Wed Jul  1 19:38:55 1998
+++ linux/drivers/sound/msnd_classic.h	Fri Jul 10 14:03:36 1998
@@ -2,7 +2,7 @@
  *
  * msnd_classic.h
  *
- * Turtle Beach MultiSound Soundcard Driver for Linux
+ * Turtle Beach MultiSound Sound Card Driver for Linux
  *
  * Some parts of this header file were derived from the Turtle Beach
  * MultiSound Driver Development Kit.
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/msnd_pinnacle.c linux/drivers/sound/msnd_pinnacle.c
--- v2.1.108/linux/drivers/sound/msnd_pinnacle.c	Wed Jul  1 19:38:55 1998
+++ linux/drivers/sound/msnd_pinnacle.c	Mon Jul 13 12:43:04 1998
@@ -2,7 +2,7 @@
  *
  * msnd_pinnacle.c - Support for Turtle Beach Pinnacle and Fiji
  *
- * Turtle Beach MultiSound Soundcard Driver for Linux
+ * Turtle Beach MultiSound Sound Card Driver for Linux
  *
  * Copyright (C) 1998 Andrew Veliath
  *
@@ -24,6 +24,7 @@
  *
  ********************************************************************/
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/malloc.h>
 #include <linux/types.h>
@@ -1211,10 +1212,18 @@
 static int irq __initdata =		-1;
 static int mem __initdata =		-1;
 static int fifosize __initdata =	DEFFIFOSIZE;
-static int
-calibrate_signal __initdata =		0;
+static int calibrate_signal __initdata;
 
 int init_module(void)
+#else
+static int io __initdata =		CONFIG_MSNDPIN_IO;
+static int irq __initdata =		CONFIG_MSNDPIN_IRQ;
+static int mem __initdata =		CONFIG_MSNDPIN_MEM;
+static int fifosize __initdata =	DEFFIFOSIZE;
+static int calibrate_signal __initdata;
+
+__initfunc(int msnd_pinnacle_init(void))
+#endif
 {
 	int err;
 
@@ -1323,6 +1332,8 @@
 
 	return 0;
 }
+
+#ifdef MODULE
 
 void cleanup_module(void)
 {
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/msnd_pinnacle.h linux/drivers/sound/msnd_pinnacle.h
--- v2.1.108/linux/drivers/sound/msnd_pinnacle.h	Wed Jul  1 19:38:55 1998
+++ linux/drivers/sound/msnd_pinnacle.h	Fri Jul 10 14:03:36 1998
@@ -2,7 +2,7 @@
  *
  * msnd_pinnacle.h
  *
- * Turtle Beach MultiSound Soundcard Driver for Linux
+ * Turtle Beach MultiSound Sound Card Driver for Linux
  *
  * Some parts of this header file were derived from the Turtle Beach
  * MultiSound Driver Development Kit.
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/pas2_card.c linux/drivers/sound/pas2_card.c
--- v2.1.108/linux/drivers/sound/pas2_card.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/pas2_card.c	Fri Jul 10 14:03:36 1998
@@ -42,6 +42,16 @@
 #else
 static int 	joystick = 1;
 #endif
+#ifdef SYMPHONY_PAS
+static int 	symphony = 1;
+#else
+static int 	symphony = 0;
+#endif
+#ifdef BROKEN_BUS_CLOCK
+static int	broken_bus_clock = 1;
+#else
+static int	broken_bus_clock = 0;
+#endif
 
 
 char            pas_model = 0;
@@ -199,19 +209,21 @@
 	 * This fixes the timing problems of the PAS due to the Symphony chipset
 	 * as per Media Vision.  Only define this if your PAS doesn't work correctly.
 	 */
-#ifdef SYMPHONY_PAS
-	outb((0x05), 0xa8);
-	outb((0x60), 0xa9);
-#endif
 
-#ifdef BROKEN_BUS_CLOCK
-	pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388);
-#else
-	/*
-	 * pas_write(0x01, 0x8388);
-	 */
-	pas_write(0x01 | 0x10 | 0x20, 0x8388);
-#endif
+	if(symphony)
+	{
+		outb((0x05), 0xa8);
+		outb((0x60), 0xa9);
+	}
+
+	if(broken_bus_clock)
+		pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388);
+	else
+		/*
+		 * pas_write(0x01, 0x8388);
+		 */
+		pas_write(0x01 | 0x10 | 0x20, 0x8388);
+
 	pas_write(0x18, 0x838A);	/* ??? */
 	pas_write(0x20 | 0x01, 0x0B8A);		/* Mute off, filter = 17.897 kHz */
 	pas_write(8, 0xBF8A);
@@ -348,14 +360,12 @@
 	}
 }
 
-int
-probe_pas(struct address_info *hw_config)
+int probe_pas(struct address_info *hw_config)
 {
 	return detect_pas_hw(hw_config);
 }
 
-void
-unload_pas(struct address_info *hw_config)
+void unload_pas(struct address_info *hw_config)
 {
 	sound_free_dma(hw_config->dma);
 	free_irq(hw_config->irq, NULL);
@@ -384,13 +394,15 @@
 MODULE_PARM(sb_dma16,"i");
 
 MODULE_PARM(joystick,"i");
+MODULE_PARM(symphony,"i");
+MODULE_PARM(broken_bus_clock,"i");
 
 struct address_info config;
 struct address_info sbhw_config;
 
 int init_module(void)
 {
-	printk(KERN_INFO "MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
+	printk(KERN_INFO "Pro Audio Spectrum driver Copyright (C) by Hannu Savolainen 1993-1996\n");
 
 	if (io == -1 || dma == -1 || irq == -1)
 	{
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/pas2_pcm.c linux/drivers/sound/pas2_pcm.c
--- v2.1.108/linux/drivers/sound/pas2_pcm.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/pas2_pcm.c	Fri Jul 10 14:03:36 1998
@@ -1,16 +1,19 @@
 /*
  * pas2_pcm.c Audio routines for PAS16
- */
-/*
+ *
+ *
  * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
- */
-/*
+ *
+ *
  * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ * Alan Cox	   : Swatted a double allocation of device bug. Made a few
+ *		     more things module options.
  */
+
 #include <linux/config.h>
 
 #include "sound_config.h"
@@ -42,11 +45,10 @@
 int             pas_audiodev = 0;
 static int      open_mode = 0;
 
-static int
-pcm_set_speed(int arg)
+static int pcm_set_speed(int arg)
 {
-	int             foo, tmp;
-	unsigned long   flags;
+	int foo, tmp;
+	unsigned long flags;
 
 	if (arg == 0)
 		return pcm_speed;
@@ -57,30 +59,31 @@
 		arg = 5000;
 
 	if (pcm_channels & 2)
-	  {
-		  foo = (596590 + (arg / 2)) / arg;
-		  arg = (596590 + (foo / 2)) / foo;
-	} else
-	  {
-		  foo = (1193180 + (arg / 2)) / arg;
-		  arg = (1193180 + (foo / 2)) / foo;
-	  }
+	{
+		foo = (596590 + (arg / 2)) / arg;
+		arg = (596590 + (foo / 2)) / foo;
+	}
+	else
+	{
+		foo = (1193180 + (arg / 2)) / arg;
+		arg = (1193180 + (foo / 2)) / foo;
+	}
 
 	pcm_speed = arg;
 
 	tmp = pas_read(0x0B8A);
 
 	/*
-	   * Set anti-aliasing filters according to sample rate. You really *NEED*
-	   * to enable this feature for all normal recording unless you want to
-	   * experiment with aliasing effects.
-	   * These filters apply to the selected "recording" source.
-	   * I (pfw) don't know the encoding of these 5 bits. The values shown
-	   * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
-	   *
-	   * I cleared bit 5 of these values, since that bit controls the master
-	   * mute flag. (Olav Wölfelschneider)
-	   *
+	 * Set anti-aliasing filters according to sample rate. You really *NEED*
+	 * to enable this feature for all normal recording unless you want to
+	 * experiment with aliasing effects.
+	 * These filters apply to the selected "recording" source.
+	 * I (pfw) don't know the encoding of these 5 bits. The values shown
+	 * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
+	 *
+	 * I cleared bit 5 of these values, since that bit controls the master
+	 * mute flag. (Olav Wölfelschneider)
+	 *
 	 */
 #if !defined NO_AUTO_FILTER_SET
 	tmp &= 0xe0;
@@ -113,25 +116,23 @@
 	return pcm_speed;
 }
 
-static int
-pcm_set_channels(int arg)
+static int pcm_set_channels(int arg)
 {
 
 	if ((arg != 1) && (arg != 2))
 		return pcm_channels;
 
 	if (arg != pcm_channels)
-	  {
-		  pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);
+	{
+		pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);
 
-		  pcm_channels = arg;
-		  pcm_set_speed(pcm_speed);	/* The speed must be reinitialized */
-	  }
+		pcm_channels = arg;
+		pcm_set_speed(pcm_speed);	/* The speed must be reinitialized */
+	}
 	return pcm_channels;
 }
 
-static int
-pcm_set_bits(int arg)
+static int pcm_set_bits(int arg)
 {
 	if (arg == 0)
 		return pcm_bits;
@@ -140,11 +141,11 @@
 		return pcm_bits;
 
 	if (arg != pcm_bits)
-	  {
-		  pas_write(pas_read(0x8389) ^ 0x04, 0x8389);
+	{
+		pas_write(pas_read(0x8389) ^ 0x04, 0x8389);
 
-		  pcm_bits = arg;
-	  }
+		pcm_bits = arg;
+	}
 	return pcm_bits;
 }
 
@@ -154,9 +155,10 @@
 
 	DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
 
-	switch (cmd) {
+	switch (cmd) 
+	{
 	case SOUND_PCM_WRITE_RATE:
-		if (__get_user(val, (int *)arg)) 
+		if (get_user(val, (int *)arg)) 
 			return -EFAULT;
 		ret = pcm_set_speed(val);
 		break;
@@ -166,13 +168,13 @@
 		break;
 		
 	case SNDCTL_DSP_STEREO:
-		if (__get_user(val, (int *)arg)) 
+		if (get_user(val, (int *)arg)) 
 			return -EFAULT;
 		ret = pcm_set_channels(val + 1) - 1;
 		break;
 
 	case SOUND_PCM_WRITE_CHANNELS:
-		if (__get_user(val, (int *)arg)) 
+		if (get_user(val, (int *)arg)) 
 			return -EFAULT;
 		ret = pcm_set_channels(val);
 		break;
@@ -182,7 +184,7 @@
 		break;
 
 	case SNDCTL_DSP_SETFMT:
-		if (__get_user(val, (int *)arg))
+		if (get_user(val, (int *)arg))
 			return -EFAULT;
 		ret = pcm_set_bits(val);
 		break;
@@ -194,19 +196,17 @@
 	default:
 		return -EINVAL;
 	}
-	return __put_user(ret, (int *)arg);
+	return put_user(ret, (int *)arg);
 }
 
-static void
-pas_audio_reset(int dev)
+static void pas_audio_reset(int dev)
 {
 	DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n"));
 
 	pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);	/* Disable PCM */
 }
 
-static int
-pas_audio_open(int dev, int mode)
+static int pas_audio_open(int dev, int mode)
 {
 	int             err;
 	unsigned long   flags;
@@ -216,10 +216,10 @@
 	save_flags(flags);
 	cli();
 	if (pcm_busy)
-	  {
-		  restore_flags(flags);
-		  return -EBUSY;
-	  }
+	{
+		restore_flags(flags);
+		return -EBUSY;
+	}
 	pcm_busy = 1;
 	restore_flags(flags);
 
@@ -233,8 +233,7 @@
 	return 0;
 }
 
-static void
-pas_audio_close(int dev)
+static void pas_audio_close(int dev)
 {
 	unsigned long   flags;
 
@@ -251,8 +250,7 @@
 	restore_flags(flags);
 }
 
-static void
-pas_audio_output_block(int dev, unsigned long buf, int count,
+static void pas_audio_output_block(int dev, unsigned long buf, int count,
 		       int intrflag)
 {
 	unsigned long   flags, cnt;
@@ -280,15 +278,15 @@
 		count >>= 1;
 
 	if (count != pcm_count)
-	  {
-		  pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
-		  pas_write(0x40 | 0x30 | 0x04, 0x138B);
-		  pas_write(count & 0xff, 0x1389);
-		  pas_write((count >> 8) & 0xff, 0x1389);
-		  pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
+	{
+		pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
+		pas_write(0x40 | 0x30 | 0x04, 0x138B);
+		pas_write(count & 0xff, 0x1389);
+		pas_write((count >> 8) & 0xff, 0x1389);
+		pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
 
-		  pcm_count = count;
-	  }
+		pcm_count = count;
+	}
 	pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
 #ifdef NO_TRIGGER
 	pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
@@ -299,8 +297,7 @@
 	restore_flags(flags);
 }
 
-static void
-pas_audio_start_input(int dev, unsigned long buf, int count,
+static void pas_audio_start_input(int dev, unsigned long buf, int count,
 		      int intrflag)
 {
 	unsigned long   flags;
@@ -326,15 +323,15 @@
 		count >>= 1;
 
 	if (count != pcm_count)
-	  {
-		  pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
-		  pas_write(0x40 | 0x30 | 0x04, 0x138B);
-		  pas_write(count & 0xff, 0x1389);
-		  pas_write((count >> 8) & 0xff, 0x1389);
-		  pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
+	{
+		pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
+		pas_write(0x40 | 0x30 | 0x04, 0x138B);
+		pas_write(count & 0xff, 0x1389);
+		pas_write((count >> 8) & 0xff, 0x1389);
+		pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
 
-		  pcm_count = count;
-	  }
+		pcm_count = count;
+	}
 	pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
 #ifdef NO_TRIGGER
 	pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
@@ -346,8 +343,7 @@
 }
 
 #ifndef NO_TRIGGER
-static void
-pas_audio_trigger(int dev, int state)
+static void pas_audio_trigger(int dev, int state)
 {
 	unsigned long   flags;
 
@@ -366,15 +362,13 @@
 }
 #endif
 
-static int
-pas_audio_prepare_for_input(int dev, int bsize, int bcount)
+static int pas_audio_prepare_for_input(int dev, int bsize, int bcount)
 {
 	pas_audio_reset(dev);
 	return 0;
 }
 
-static int
-pas_audio_prepare_for_output(int dev, int bsize, int bcount)
+static int pas_audio_prepare_for_output(int dev, int bsize, int bcount)
 {
 	pas_audio_reset(dev);
 	return 0;
@@ -397,8 +391,7 @@
 	pas_audio_trigger
 };
 
-void
-pas_pcm_init(struct address_info *hw_config)
+void pas_pcm_init(struct address_info *hw_config)
 {
 	DEB(printk("pas2_pcm.c: long pas_pcm_init()\n"));
 
@@ -408,55 +401,44 @@
 
 	pcm_set_speed(DSP_DEFAULT_SPEED);
 
-	if ((pas_audiodev = sound_alloc_audiodev()) != -1)
-	  {
-
-		  if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
-						    "Pro Audio Spectrum",
-						       &pas_audio_driver,
-					     sizeof(struct audio_driver),
-							     DMA_AUTOMODE,
-						   AFMT_U8 | AFMT_S16_LE,
-							     NULL,
-							  hw_config->dma,
-						    hw_config->dma)) < 0)
-		    {
-			    return;
-		    }
-	} else
+	if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
+					"Pro Audio Spectrum",
+					&pas_audio_driver,
+					sizeof(struct audio_driver),
+					DMA_AUTOMODE,
+					AFMT_U8 | AFMT_S16_LE,
+					NULL,
+					hw_config->dma,
+					hw_config->dma)) < 0)
 		printk(KERN_WARNING "PAS16: Too many PCM devices available\n");
 }
 
-void
-pas_pcm_interrupt(unsigned char status, int cause)
+void pas_pcm_interrupt(unsigned char status, int cause)
 {
 	if (cause == 1)
-	  {
-		  /*
-		   * Halt the PCM first. Otherwise we don't have time to start a new
-		   * block before the PCM chip proceeds to the next sample
-		   */
-
-		  if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
-		    {
-			    pas_write(pas_read(0xF8A) & ~0x40,
-				      0xF8A);
-		    }
-		  switch (pcm_mode)
-		    {
-
-		    case PCM_DAC:
-			    DMAbuf_outputintr(pas_audiodev, 1);
-			    break;
-
-		    case PCM_ADC:
-			    DMAbuf_inputintr(pas_audiodev);
-			    break;
-
-		    default:
-			    printk("PAS: Unexpected PCM interrupt\n");
-		    }
-	  }
+	{
+		/*
+		 * Halt the PCM first. Otherwise we don't have time to start a new
+		 * block before the PCM chip proceeds to the next sample
+		 */
+
+		if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
+			pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
+
+		switch (pcm_mode)
+		{
+			case PCM_DAC:
+				DMAbuf_outputintr(pas_audiodev, 1);
+				break;
+
+			case PCM_ADC:
+				DMAbuf_inputintr(pas_audiodev);
+				break;
+
+			default:
+				printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");
+		}
+	}
 }
 
 #endif
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/pss.c linux/drivers/sound/pss.c
--- v2.1.108/linux/drivers/sound/pss.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/pss.c	Fri Jul 10 14:03:36 1998
@@ -22,6 +22,8 @@
  *          Requested two regions for PSS (PSS mixer, PSS config)
  *          Modified pss_download_boot
  *          To probe_pss_mss added test for initialize AD1848
+ * 98-05-28: Vladimir Michl <vladimir.michl@upol.cz>
+ *          Fixed computation of mixer volumes
  */
 
 
@@ -89,7 +91,13 @@
 NULL;
 #endif
 
-unsigned char pss_mixer = 1;
+/* If compiled into kernel, it enable or disable pss mixer */
+#ifdef CONFIG_PSS_MIXER
+static unsigned char pss_mixer = 1;
+#else
+static unsigned char pss_mixer = 0;
+#endif
+
 
 typedef struct pss_mixerdata {
 	unsigned int volume_l;
@@ -377,12 +385,7 @@
 
 static void set_synth_volume(pss_confdata *devc, int volume)
 {
-	/* Should use:
-		int vol = (int)(0x8000/100.0 * (float)volume);
-		
-	  Fixme: integerise the above cleanly
-	*/
-	int vol = (0x8000/(100L*volume));
+	int vol = ((0x8000*volume)/100L);
 	pss_write(devc, 0x0080);
 	pss_write(devc, vol);
 	pss_write(devc, 0x0081);
@@ -391,32 +394,21 @@
 
 static void set_bass(pss_confdata *devc, int level)
 {
-	/* Should use
-	   int vol = (int)((0xfd - 0xf0)/100.0 * (float)level) + 0xf0;     
-	   
-	   Fixme: integerise cleanly 
-	*/
-	int vol = (int)((0xfd - 0xf0)/100L * level) + 0xf0;
+	int vol = (int)(((0xfd - 0xf0) * level)/100L) + 0xf0;
 	pss_write(devc, 0x0010);
 	pss_write(devc, vol | 0x0200);
 };
 
 static void set_treble(pss_confdata *devc, int level)
 {	
-	/* Should use 
-	   int vol = (int)((0xfd - 0xf0)/100.0 * (float)level) + 0xf0;
-	   
-	   Fixme: integerise properly
-	*/
-	
-	int vol = ((0xfd - 0xf0)/100L * level) + 0xf0;
+	int vol = (((0xfd - 0xf0) * level)/100L) + 0xf0;
 	pss_write(devc, 0x0010);
 	pss_write(devc, vol | 0x0300);
 };
 
 static void pss_mixer_reset(pss_confdata *devc)
 {
-	set_master_volume(devc, 23, 23);
+	set_master_volume(devc, 33, 33);
 	set_bass(devc, 50);
 	set_treble(devc, 50);
 	set_synth_volume(devc, 30);
@@ -425,7 +417,7 @@
 	
 	if(pss_mixer)
 	{
-		devc->mixer.volume_l = devc->mixer.volume_r = 23;
+		devc->mixer.volume_l = devc->mixer.volume_r = 33;
 		devc->mixer.bass = 50;
 		devc->mixer.treble = 50;
 		devc->mixer.synth = 30;
@@ -1061,7 +1053,7 @@
 MODULE_PARM(mpu_irq, "i");
 MODULE_PARM_DESC(mpu_irq, "Set MIDI IRQ (3, 5, 7, 9, 10, 11, 12)");
 MODULE_PARM(pss_mixer, "b");
-MODULE_PARM_DESC(pss_mixer, "Enable (1) or disable (0) PSS mixer (controlling of output volume, bass, treble synth volume). The mixer is not available on all PSS cards.");
+MODULE_PARM_DESC(pss_mixer, "Enable (1) or disable (0) PSS mixer (controlling of output volume, bass, treble, synth volume). The mixer is not available on all PSS cards.");
 MODULE_AUTHOR("Hannu Savolainen, Vladimir Michl");
 MODULE_DESCRIPTION("Module for PSS sound cards (based on AD1848, ADSP-2115 and ESC614). This module includes control of output amplifier and synth volume of the Beethoven ADSP-16 card (this may work with other PSS cards).\n");
 
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/sb_audio.c linux/drivers/sound/sb_audio.c
--- v2.1.108/linux/drivers/sound/sb_audio.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/sb_audio.c	Fri Jul 10 14:03:36 1998
@@ -14,7 +14,7 @@
  *	Alan Cox	:	Formatting and clean ups
  *
  * Status
- *	Mostly working. mmap bug still present (swaps channels)
+ *	Mostly working. Weird uart bug causing irq storms
  */
 
 #include <linux/config.h>
@@ -32,7 +32,7 @@
 
 	if (devc == NULL)
 	{
-		  printk(KERN_ERR "SB: Incomplete initialization\n");
+		  printk(KERN_ERR "Sound Blaster: incomplete initialization.\n");
 		  return -ENXIO;
 	}
 	if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ)
@@ -146,7 +146,7 @@
 		sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
 	}
 	else
-		printk(KERN_WARNING "soundblaster: Unable to start DAC\n");
+		printk(KERN_WARNING "Sound Blaster:  unable to start DAC.\n");
 	restore_flags(flags);
 	devc->intr_active = 1;
 }
@@ -177,7 +177,7 @@
 		sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
 	}
 	else
-		printk(KERN_ERR "soundblaster: Unable to start ADC\n");
+		printk(KERN_ERR "Sound Blaster:  unable to start ADC.\n");
 	restore_flags(flags);
 
 	devc->intr_active = 1;
@@ -322,10 +322,10 @@
 			cmd = 0x90;	/* 8 bit high speed PCM output (SB2.01/Pro) */
 
 		if (!sb_dsp_command(devc, cmd))
-			printk(KERN_ERR "soundblaster: Unable to start DAC\n");
+			printk(KERN_ERR "Sound Blaster:  unable to start DAC.\n");
 	}
 	else
-		printk(KERN_ERR "soundblaster: Unable to start DAC\n");
+		printk(KERN_ERR "Sound Blaster: unable to start DAC.\n");
 	restore_flags(flags);
 	devc->intr_active = 1;
 }
@@ -362,10 +362,10 @@
 			cmd = 0x98;	/* 8 bit high speed PCM input (SB2.01/Pro) */
 
 		if (!sb_dsp_command(devc, cmd))
-			printk(KERN_ERR "soundblaster: Unable to start ADC\n");
+			printk(KERN_ERR "Sound Blaster:  unable to start ADC.\n");
 	}
 	else
-		printk(KERN_ERR "soundblaster: Unable to start ADC\n");
+		printk(KERN_ERR "Sound Blaster:  unable to start ADC.\n");
 	restore_flags(flags);
 	devc->intr_active = 1;
 }
@@ -1138,7 +1138,7 @@
 				audio_flags, format_mask, devc,
 				devc->dma8, devc->dma8)) < 0)
 	{
-		  printk(KERN_ERR "sb: unable to install audio.\n");
+		  printk(KERN_ERR "Sound Blaster:  unable to install audio.\n");
 		  return;
 	}
 	audio_devs[devc->my_dev]->mixer_dev = devc->my_mixerdev;
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.1.108/linux/drivers/sound/sb_common.c	Tue Jun  9 11:57:30 1998
+++ linux/drivers/sound/sb_common.c	Fri Jul 10 14:03:36 1998
@@ -82,7 +82,7 @@
 			return 1;
 		}
 	}
-	printk(KERN_WARNING "soundblaster: DSP Command(%x) Timeout.\n", val);
+	printk(KERN_WARNING "Sound Blaster:  DSP command(%x) timeout.\n", val);
 	return 0;
 }
 
@@ -873,8 +873,8 @@
 	conf_printf(name, hw_config);
 
 	/*
-	 * Assuming that a soundcard is Sound Blaster (compatible) is the most common
-	 * configuration error and the mother of all problems. Usually soundcards
+	 * Assuming that a sound card is Sound Blaster (compatible) is the most common
+	 * configuration error and the mother of all problems. Usually sound cards
 	 * emulate SB Pro but in addition they have a 16 bit native mode which should be
 	 * used in Unix. See Readme.cards for more information about configuring OSS/Free
 	 * properly.
@@ -883,7 +883,7 @@
 	{
 		if (devc->major == 3 && devc->minor != 1)	/* "True" SB Pro should have v3.1 (rare ones may have 3.2). */
 		{
-			printk(KERN_INFO "This soundcard may not be fully Sound Blaster Pro compatible.\n");
+			printk(KERN_INFO "This sound card may not be fully Sound Blaster Pro compatible.\n");
 			printk(KERN_INFO "In many cases there is another way to configure OSS so that\n");
 			printk(KERN_INFO "it works properly with OSS (for example in 16 bit mode).\n");
 			printk(KERN_INFO "Please ignore this message if you _really_ have a SB Pro.\n");
@@ -908,14 +908,14 @@
 		if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
 		{
 			if (sound_alloc_dma(devc->dma16, "SoundBlaster16"))
-				printk(KERN_WARNING "soundblaster: Can't allocate 16 bit DMA channel %d\n", devc->dma16);
+				printk(KERN_WARNING "Sound Blaster:  can't allocate 16 bit DMA channel %d.\n", devc->dma16);
 		}
 		sb_audio_init(devc, name);
 		hw_config->slots[0]=devc->dev;
 	}
 	else
 	{
-		MDB(printk("soundblaster: No audio devices found.\n"));
+		MDB(printk("Sound Blaster:  no audio devices found.\n"));
 	}
 }
 
@@ -1262,7 +1262,7 @@
 {
 #if defined(CONFIG_MIDI) && defined(CONFIG_UART401)
 	attach_uart401(hw_config);
-	last_sb->midi_irq_cookie=midi_devs[hw_config->slots[4]];
+	last_sb->midi_irq_cookie=midi_devs[hw_config->slots[4]]->devc;
 #endif
 }
 
@@ -1289,7 +1289,7 @@
 		case MDL_SB16:
 			if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330)
 			{
-				printk(KERN_ERR "SB16: Invalid MIDI port %x\n", hw_config->irq);
+				printk(KERN_ERR "SB16: Invalid MIDI port %x\n", hw_config->io_base);
 				return 0;
 			}
 			hw_config->name = "Sound Blaster 16";
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/sb_midi.c linux/drivers/sound/sb_midi.c
--- v2.1.108/linux/drivers/sound/sb_midi.c	Tue Jun  9 11:57:30 1998
+++ linux/drivers/sound/sb_midi.c	Fri Jul 10 14:03:36 1998
@@ -179,7 +179,7 @@
 
 	if (dev == -1)
 	{
-		printk(KERN_ERR "sb_midi: Too many midi devices detected\n");
+		printk(KERN_ERR "sb_midi: too many MIDI devices detected\n");
 		return;
 	}
 	std_midi_synth.midi_dev = dev;
@@ -188,7 +188,7 @@
 	midi_devs[dev] = (struct midi_operations *)kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
 	if (midi_devs[dev] == NULL)
 	{
-		printk(KERN_WARNING "soundblaster: Failed to allocate MIDI memory.\n");
+		printk(KERN_WARNING "Sound Blaster:  failed to allocate MIDI memory.\n");
 		sound_unload_mididev(dev);
 		  return;
 	}
@@ -201,7 +201,7 @@
 	midi_devs[dev]->converter = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
 	if (midi_devs[dev]->converter == NULL)
 	{
-		  printk(KERN_WARNING "soundblaster: Failed to allocate MIDI memory.\n");
+		  printk(KERN_WARNING "Sound Blaster:  failed to allocate MIDI memory.\n");
 		  kfree(midi_devs[dev]);
 		  sound_unload_mididev(dev);
 		  return;
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v2.1.108/linux/drivers/sound/sequencer.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/sequencer.c	Fri Jul 10 14:03:36 1998
@@ -993,7 +993,7 @@
 
 	if (!sequencer_ok)
 	{
-/*		printk("Soundcard: Sequencer not initialized\n");*/
+/*		printk("Sound card: sequencer not initialized\n");*/
 		return -ENXIO;
 	}
 	if (dev)		/* Patch manager device (obsolete) */
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/softoss.c linux/drivers/sound/softoss.c
--- v2.1.108/linux/drivers/sound/softoss.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/softoss.c	Fri Jul 10 14:03:36 1998
@@ -1015,7 +1015,7 @@
 	if (devc->bits != 16 || devc->channels != 2)
 	{
 		audio_release((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo);
-/*		printk("SoftOSS: A 16 bit stereo soundcard is required\n");*/
+/*		printk("SoftOSS: A 16 bit stereo sound card is required\n");*/
 		return -EINVAL;
 	}
 	if (devc->max_playahead >= audio_devs[devc->audiodev]->dmap_out->nbufs)
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/sonicvibes.c linux/drivers/sound/sonicvibes.c
--- v2.1.108/linux/drivers/sound/sonicvibes.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/sonicvibes.c	Fri Jul 10 14:03:36 1998
@@ -0,0 +1,2454 @@
+/*****************************************************************************/
+
+/*
+ *      sonicvibes.c  --  S3 Sonic Vibes audio driver.
+ *
+ *      Copyright (C) 1998  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Special thanks to David C. Niemi
+ *
+ *
+ * Module command line parameters:
+ *   none so far
+ *
+ *
+ *  Supported devices:
+ *  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible
+ *  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible
+ *  /dev/midi   simple MIDI UART interface, no ioctl
+ *
+ *  The card has both an FM and a Wavetable synth, but I have to figure
+ *  out first how to drive them...
+ *
+ *  Revision history
+ *    06.05.98   0.1   Initial release
+ *    10.05.98   0.2   Fixed many bugs, esp. ADC rate calculation
+ *                     First stab at a simple midi interface (no bells&whistles)
+ *    13.05.98   0.3   Fix stupid cut&paste error: set_adc_rate was called instead of
+ *                     set_dac_rate in the FMODE_WRITE case in sv_open
+ *                     Fix hwptr out of bounds (now mpg123 works)
+ *    14.05.98   0.4   Don't allow excessive interrupt rates
+ *    08.06.98   0.5   First release using Alan Cox' soundcore instead of miscdevice
+ *
+ */
+
+/*****************************************************************************/
+      
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/modversions.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/malloc.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <asm/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+
+#include "dm.h"
+
+/* --------------------------------------------------------------------- */
+
+#ifndef PCI_VENDOR_ID_S3
+#define PCI_VENDOR_ID_S3             0x5333
+#endif
+#ifndef PCI_DEVICE_ID_S3_SONICVIBES
+#define PCI_DEVICE_ID_S3_SONICVIBES  0xca00
+#endif
+
+#define SV_MAGIC  ((PCI_VENDOR_ID_S3<<16)|PCI_DEVICE_ID_S3_SONICVIBES)
+
+#define SV_EXTENT_SB      0x10
+#define SV_EXTENT_ENH     0x10
+#define SV_EXTENT_SYNTH   0x4
+#define SV_EXTENT_MIDI    0x4
+#define SV_EXTENT_GAME    0x8
+#define SV_EXTENT_DMA     0x10
+
+
+#define SV_MIDI_DATA      0
+#define SV_MIDI_COMMAND   1
+#define SV_MIDI_STATUS    1
+
+#define SV_DMA_ADDR0      0
+#define SV_DMA_ADDR1      1
+#define SV_DMA_ADDR2      2
+#define SV_DMA_ADDR3      3
+#define SV_DMA_COUNT0     4
+#define SV_DMA_COUNT1     5
+#define SV_DMA_COUNT2     6
+#define SV_DMA_MODE       0xb
+#define SV_DMA_RESET      0xd
+#define SV_DMA_MASK       0xf
+
+/*
+ * DONT reset the DMA controllers unless you understand
+ * the reset semantics. Assuming reset semantics as in
+ * the 8237 does not work.
+ */
+
+#define DMA_MODE_AUTOINIT 0x10
+#define DMA_MODE_READ     0x44    /* I/O to memory, no autoinit, increment, single mode */
+#define DMA_MODE_WRITE    0x48    /* memory to I/O, no autoinit, increment, single mode */
+
+#define SV_CODEC_CONTROL  0
+#define SV_CODEC_INTMASK  1
+#define SV_CODEC_STATUS   2
+#define SV_CODEC_IADDR    4
+#define SV_CODEC_IDATA    5
+
+#define SV_CCTRL_RESET      0x80
+#define SV_CCTRL_INTADRIVE  0x20
+#define SV_CCTRL_WAVETABLE  0x08
+#define SV_CCTRL_REVERB     0x04
+#define SV_CCTRL_ENHANCED   0x01
+
+#define SV_CINTMASK_DMAA    0x01
+#define SV_CINTMASK_DMAC    0x04
+#define SV_CINTMASK_SPECIAL 0x08
+#define SV_CINTMASK_UPDOWN  0x40
+#define SV_CINTMASK_MIDI    0x80
+
+#define SV_CSTAT_DMAA       0x01
+#define SV_CSTAT_DMAC	    0x04
+#define SV_CSTAT_SPECIAL    0x08
+#define SV_CSTAT_UPDOWN	    0x40
+#define SV_CSTAT_MIDI	    0x80
+
+#define SV_CIADDR_TRD       0x80
+#define SV_CIADDR_MCE       0x40
+
+/* codec indirect registers */
+#define SV_CIMIX_ADCINL     0x00
+#define SV_CIMIX_ADCINR     0x01
+#define SV_CIMIX_AUX1INL    0x02
+#define SV_CIMIX_AUX1INR    0x03
+#define SV_CIMIX_CDINL      0x04
+#define SV_CIMIX_CDINR      0x05
+#define SV_CIMIX_LINEINL    0x06
+#define SV_CIMIX_LINEINR    0x07
+#define SV_CIMIX_MICIN      0x08
+#define SV_CIMIX_SYNTHINL   0x0A
+#define SV_CIMIX_SYNTHINR   0x0B
+#define SV_CIMIX_AUX2INL    0x0C
+#define SV_CIMIX_AUX2INR    0x0D
+#define SV_CIMIX_ANALOGINL  0x0E
+#define SV_CIMIX_ANALOGINR  0x0F
+#define SV_CIMIX_PCMINL     0x10
+#define SV_CIMIX_PCMINR     0x11
+
+#define SV_CIGAMECONTROL    0x09
+#define SV_CIDATAFMT        0x12
+#define SV_CIENABLE         0x13
+#define SV_CIUPDOWN         0x14
+#define SV_CIREVISION       0x15
+#define SV_CIADCOUTPUT      0x16
+#define SV_CIDMAABASECOUNT1 0x18
+#define SV_CIDMAABASECOUNT0 0x19
+#define SV_CIDMACBASECOUNT1 0x1c
+#define SV_CIDMACBASECOUNT0 0x1d
+#define SV_CIPCMSR0         0x1e
+#define SV_CIPCMSR1         0x1f
+#define SV_CISYNTHSR0       0x20
+#define SV_CISYNTHSR1       0x21
+#define SV_CIADCCLKSOURCE   0x22
+#define SV_CIADCALTSR       0x23
+#define SV_CIADCPLLM        0x24
+#define SV_CIADCPLLN        0x25
+#define SV_CISYNTHPLLM      0x26
+#define SV_CISYNTHPLLN      0x27
+#define SV_CIUARTCONTROL    0x2a
+#define SV_CIDRIVECONTROL   0x2b
+#define SV_CISRSSPACE       0x2c
+#define SV_CISRSCENTER      0x2d
+#define SV_CIWAVETABLESRC   0x2e
+#define SV_CIANALOGPWRDOWN  0x30
+#define SV_CIDIGITALPWRDOWN 0x31
+
+
+#define SV_CIMIX_ADCSRC_CD     0x20
+#define SV_CIMIX_ADCSRC_DAC    0x40
+#define SV_CIMIX_ADCSRC_AUX2   0x60
+#define SV_CIMIX_ADCSRC_LINE   0x80
+#define SV_CIMIX_ADCSRC_AUX1   0xa0
+#define SV_CIMIX_ADCSRC_MIC    0xc0
+#define SV_CIMIX_ADCSRC_MIXOUT 0xe0
+#define SV_CIMIX_ADCSRC_MASK   0xe0
+
+#define SV_CFMT_STEREO     0x01
+#define SV_CFMT_16BIT      0x02
+#define SV_CFMT_MASK       0x03
+#define SV_CFMT_ASHIFT     0   
+#define SV_CFMT_CSHIFT     4
+
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+#define SV_CENABLE_PPE     0x4
+#define SV_CENABLE_RE      0x2
+#define SV_CENABLE_PE      0x1
+
+
+/* MIDI buffer sizes */
+
+#define MIDIINBUF  256
+#define MIDIOUTBUF 256
+
+#define FMODE_MIDI_SHIFT 2
+#define FMODE_MIDI_READ  (FMODE_READ << FMODE_MIDI_SHIFT)
+#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
+
+#define FMODE_DMFM 0x10
+
+#define SND_DEV_DSP16   5 
+
+/* --------------------------------------------------------------------- */
+
+struct sv_state {
+	/* magic */
+	unsigned int magic;
+
+	/* we keep sv cards in a linked list */
+	struct sv_state *next;
+
+	/* soundcore stuff */
+	int dev_audio;
+	int dev_mixer;
+	int dev_midi;
+	int dev_dmfm;
+
+	/* hardware resources */
+	unsigned int iosb, ioenh, iosynth, iomidi, iogame, iodmaa, iodmac, irq;
+
+        /* mixer stuff */
+        struct {
+                unsigned int modcnt;
+        } mix;
+
+	/* wave stuff */
+	unsigned int rateadc, ratedac;
+	unsigned char fmt, enable;
+
+	spinlock_t lock;
+	struct semaphore open_sem;
+	mode_t open_mode;
+	struct wait_queue *open_wait;
+
+	struct dmabuf {
+		void *rawbuf;
+		unsigned buforder;
+		unsigned numfrag;
+		unsigned fragshift;
+		unsigned hwptr, swptr;
+		unsigned total_bytes;
+		int count;
+		unsigned error; /* over/underrun */
+		struct wait_queue *wait;
+		/* redundant, but makes calculations easier */
+		unsigned fragsize;
+		unsigned dmasize;
+		unsigned fragsamples;
+		/* OSS stuff */
+		unsigned mapped:1;
+		unsigned ready:1;
+		unsigned endcleared:1;
+		unsigned ossfragshift;
+		int ossmaxfrags;
+		unsigned subdivision;
+	} dma_dac, dma_adc;
+
+	/* midi stuff */
+	struct {
+		unsigned ird, iwr, icnt;
+		unsigned ord, owr, ocnt;
+		struct wait_queue *iwait;
+		struct wait_queue *owait;
+		struct timer_list timer;
+		unsigned char ibuf[MIDIINBUF];
+		unsigned char obuf[MIDIOUTBUF];
+	} midi;
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct sv_state *devs = NULL;
+static unsigned long wavetable_mem = 0;
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned ld2(unsigned int x)
+{
+	unsigned r = 0;
+	
+	if (x >= 0x10000) {
+		x >>= 16;
+		r += 16;
+	}
+	if (x >= 0x100) {
+		x >>= 8;
+		r += 8;
+	}
+	if (x >= 0x10) {
+		x >>= 4;
+		r += 4;
+	}
+	if (x >= 4) {
+		x >>= 2;
+		r += 2;
+	}
+	if (x >= 2)
+		r++;
+	return r;
+}
+
+/*
+ * hweightN: returns the hamming weight (i.e. the number
+ * of bits set) of a N-bit word
+ */
+
+#ifdef hweight32
+#undef hweight32
+#endif
+
+extern __inline__ unsigned int hweight32(unsigned int w)
+{
+        unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
+        res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+        res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
+        res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
+        return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Why use byte IO? Nobody knows, but S3 does it also in their Windows driver.
+ */
+
+#undef DMABYTEIO
+
+static void set_dmaa(struct sv_state *s, unsigned int addr, unsigned int count)
+{
+#ifdef DMABYTEIO
+	unsigned io = s->iodmaa, u;
+
+	count--;
+	for (u = 4; u > 0; u--, addr >>= 8, io++)
+		outb(addr & 0xff, io);
+	for (u = 3; u > 0; u--, count >>= 8, io++)
+		outb(count & 0xff, io);
+#else /* DMABYTEIO */
+	count--;
+	outl(addr, s->iodmaa + SV_DMA_ADDR0);
+	outl(count, s->iodmaa + SV_DMA_COUNT0);
+#endif /* DMABYTEIO */
+	outb(0x18, s->iodmaa + SV_DMA_MODE);
+}
+
+static void set_dmac(struct sv_state *s, unsigned int addr, unsigned int count)
+{
+#ifdef DMABYTEIO
+	unsigned io = s->iodmac, u;
+
+	count >>= 1;
+	count--;
+	for (u = 4; u > 0; u--, addr >>= 8, io++)
+		outb(addr & 0xff, io);
+	for (u = 3; u > 0; u--, count >>= 8, io++)
+		outb(count & 0xff, io);
+#else /* DMABYTEIO */
+	count >>= 1;
+	count--;
+	outl(addr, s->iodmac + SV_DMA_ADDR0);
+	outl(count, s->iodmac + SV_DMA_COUNT0);
+#endif /* DMABYTEIO */
+	outb(0x14, s->iodmac + SV_DMA_MODE);
+}
+
+extern __inline__ unsigned get_dmaa(struct sv_state *s)
+{
+#ifdef DMABYTEIO
+	unsigned io = s->iodmaa+6, v = 0, u;
+
+	for (u = 3; u > 0; u--, io--) {
+		v <<= 8;
+		v |= inb(io);
+	}
+	return v + 1;
+#else /* DMABYTEIO */
+	return (inl(s->iodmaa + SV_DMA_COUNT0) & 0xffffff) + 1;
+#endif /* DMABYTEIO */
+}
+
+extern __inline__ unsigned get_dmac(struct sv_state *s)
+{
+#ifdef DMABYTEIO
+	unsigned io = s->iodmac+6, v = 0, u;
+
+	for (u = 3; u > 0; u--, io--) {
+		v <<= 8;
+		v |= inb(io);
+	}
+	return (v + 1) << 1;
+#else /* DMABYTEIO */
+	return ((inl(s->iodmac + SV_DMA_COUNT0) & 0xffffff) + 1) << 1;
+#endif /* DMABYTEIO */
+}
+
+static void wrindir(struct sv_state *s, unsigned char idx, unsigned char data)
+{
+	outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	outb(data, s->ioenh + SV_CODEC_IDATA);
+	udelay(10);
+}
+
+static unsigned char rdindir(struct sv_state *s, unsigned char idx)
+{
+	unsigned char v;
+
+	outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	v = inb(s->ioenh + SV_CODEC_IDATA);
+	udelay(10);
+	return v;
+}
+
+static void set_fmt(struct sv_state *s, unsigned char mask, unsigned char data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	outb(SV_CIDATAFMT | SV_CIADDR_MCE, s->ioenh + SV_CODEC_IADDR);
+	if (mask) {
+		s->fmt = inb(s->ioenh + SV_CODEC_IDATA);
+		udelay(10);
+	}
+	s->fmt = (s->fmt & mask) | data;
+	outb(s->fmt, s->ioenh + SV_CODEC_IDATA);
+	udelay(10);
+	outb(0, s->ioenh + SV_CODEC_IADDR);
+	spin_unlock_irqrestore(&s->lock, flags);
+	udelay(10);
+}
+
+static void frobindir(struct sv_state *s, unsigned char idx, unsigned char mask, unsigned char data)
+{
+	outb(idx & 0x3f, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	outb((inb(s->ioenh + SV_CODEC_IDATA) & mask) ^ data, s->ioenh + SV_CODEC_IDATA);
+	udelay(10);
+}
+
+#define REFFREQUENCY  24576000
+#define ADCMULT 512
+#define FULLRATE 48000
+
+static unsigned setpll(struct sv_state *s, unsigned char reg, unsigned rate)
+{
+	unsigned long flags;
+	unsigned char r, m, n;
+	unsigned xm, xn, xr, xd, metric = ~0U;
+
+	if (rate < 625000/ADCMULT)
+		rate = 625000/ADCMULT;
+	if (rate > 150000000/ADCMULT)
+		rate = 150000000/ADCMULT;
+	/* slight violation of specs, needed for continuous sampling rates */
+	for (r = 0; rate < 75000000/ADCMULT; r += 0x20, rate <<= 1);
+	for (xn = 3; xn < 35; xn++)
+		for (xm = 3; xm < 130; xm++) {
+			xr = REFFREQUENCY/ADCMULT * xm / xn;
+			xd = abs((signed)(xr - rate));
+			if (xd < metric) {
+				metric = xd;
+				m = xm - 2;
+				n = xn - 2;
+			}
+		}
+	reg &= 0x3f;
+	spin_lock_irqsave(&s->lock, flags);
+	outb(reg, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	outb(m, s->ioenh + SV_CODEC_IDATA);
+	udelay(10);
+	outb(reg+1, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	outb(r | n, s->ioenh + SV_CODEC_IDATA);
+	spin_unlock_irqrestore(&s->lock, flags);
+	udelay(10);
+	return (REFFREQUENCY/ADCMULT * (m + 2) / (n + 2)) >> ((r >> 5) & 7);
+}
+
+#if 0
+
+static unsigned getpll(struct sv_state *s, unsigned char reg)
+{
+	unsigned long flags;
+	unsigned char m, n;
+
+	reg &= 0x3f;
+	spin_lock_irqsave(&s->lock, flags);
+	outb(reg, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	m = inb(s->ioenh + SV_CODEC_IDATA);
+	udelay(10);
+	outb(reg+1, s->ioenh + SV_CODEC_IADDR);
+	udelay(10);
+	n = inb(s->ioenh + SV_CODEC_IDATA);
+	spin_unlock_irqrestore(&s->lock, flags);
+	udelay(10);
+	return (REFFREQUENCY/ADCMULT * (m + 2) / ((n & 0x1f) + 2)) >> ((n >> 5) & 7);
+}
+
+#endif
+
+static void set_dac_rate(struct sv_state *s, unsigned rate)
+{
+	unsigned div;
+	unsigned long flags;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+	div = (rate * 65536 + FULLRATE/2) / FULLRATE;
+	if (div > 65535)
+		div = 65535;
+	spin_lock_irqsave(&s->lock, flags);
+	wrindir(s, SV_CIPCMSR1, div >> 8);
+	wrindir(s, SV_CIPCMSR0, div);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->ratedac = (div * FULLRATE + 32768) / 65536;
+}
+
+static void set_adc_rate(struct sv_state *s, unsigned rate)
+{
+	unsigned long flags;
+	unsigned rate1, rate2, div;
+
+	if (rate > 48000)
+		rate = 48000;
+	if (rate < 4000)
+		rate = 4000;
+	rate1 = setpll(s, SV_CIADCPLLM, rate);
+	div = (48000 + rate/2) / rate;
+	if (div > 8)
+		div = 8;
+	rate2 = (48000 + div/2) / div;
+	spin_lock_irqsave(&s->lock, flags);
+	wrindir(s, SV_CIADCALTSR, (div-1) << 4);
+	if (abs((signed)(rate-rate2)) <= abs((signed)(rate-rate1))) {
+		wrindir(s, SV_CIADCCLKSOURCE, 0x10);
+		s->rateadc = rate2;
+	} else {
+		wrindir(s, SV_CIADCCLKSOURCE, 0x00);
+		s->rateadc = rate1;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+extern inline void stop_adc(struct sv_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->enable &= ~SV_CENABLE_RE;
+	wrindir(s, SV_CIENABLE, s->enable);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+extern inline void stop_dac(struct sv_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	s->enable &= ~(SV_CENABLE_PPE | SV_CENABLE_PE);
+	wrindir(s, SV_CIENABLE, s->enable);
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_dac(struct sv_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) {
+		s->enable = (s->enable & ~SV_CENABLE_PPE) | SV_CENABLE_PE;
+		wrindir(s, SV_CIENABLE, s->enable);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+static void start_adc(struct sv_state *s)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	if ((s->dma_adc.mapped || s->dma_adc.count < s->dma_adc.dmasize - 2*s->dma_adc.fragsize) 
+	    && s->dma_adc.ready) {
+		s->enable |= SV_CENABLE_RE;
+		wrindir(s, SV_CIENABLE, s->enable);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+}	
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER 8
+#define DMABUF_MINORDER 1
+
+
+static void dealloc_dmabuf(struct dmabuf *db)
+{
+	unsigned long map, mapend;
+
+	if (db->rawbuf) {
+		/* undo marking the pages as reserved */
+		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+			clear_bit(PG_reserved, &mem_map[map].flags);	
+		free_pages((unsigned long)db->rawbuf, db->buforder);
+	}
+	db->rawbuf = NULL;
+	db->mapped = db->ready = 0;
+}
+
+
+/* DMAA is used for playback, DMAC is used for recording */
+
+static int prog_dmabuf(struct sv_state *s, unsigned rec)
+{
+	struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac;
+	unsigned rate = rec ? s->rateadc : s->ratedac;
+	int order;
+	unsigned bytepersec;
+	unsigned bufs;
+	unsigned long map, mapend;
+	unsigned char fmt;
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->lock, flags);
+	fmt = s->fmt;
+	if (rec) {
+		s->enable &= ~SV_CENABLE_RE;
+		fmt >>= SV_CFMT_CSHIFT;
+	} else {
+		s->enable &= ~SV_CENABLE_PE;
+		fmt >>= SV_CFMT_ASHIFT;
+	}
+	wrindir(s, SV_CIENABLE, s->enable);
+	spin_unlock_irqrestore(&s->lock, flags);
+	fmt &= SV_CFMT_MASK;
+	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+	if (!db->rawbuf) {
+		db->ready = db->mapped = 0;
+		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--)
+			db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order);
+		if (!db->rawbuf)
+			return -ENOMEM;
+		db->buforder = order;
+		if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff)
+			printk(KERN_DEBUG "sv: DMA buffer crosses 64k boundary: busaddr 0x%lx  size %ld\n", 
+			       virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder);
+		if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff)
+			printk(KERN_DEBUG "sv: DMA buffer beyond 16MB: busaddr 0x%lx  size %ld\n", 
+			       virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder);
+		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+		mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+		for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+			set_bit(PG_reserved, &mem_map[map].flags);
+	}
+	bytepersec = rate << sample_shift[fmt];
+	bufs = PAGE_SIZE << db->buforder;
+	if (db->ossfragshift) {
+		if ((1000 << db->ossfragshift) < bytepersec)
+			db->fragshift = ld2(bytepersec/1000);
+		else
+			db->fragshift = db->ossfragshift;
+	} else {
+		db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+		if (db->fragshift < 3)
+			db->fragshift = 3;
+	}
+	db->numfrag = bufs >> db->fragshift;
+	while (db->numfrag < 4 && db->fragshift > 3) {
+		db->fragshift--;
+		db->numfrag = bufs >> db->fragshift;
+	}
+	db->fragsize = 1 << db->fragshift;
+	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+		db->numfrag = db->ossmaxfrags;
+	db->fragsamples = db->fragsize >> sample_shift[fmt];
+	db->dmasize = db->numfrag << db->fragshift;
+	memset(db->rawbuf, (fmt & SV_CFMT_16BIT) ? 0 : 0x80, db->dmasize);
+	spin_lock_irqsave(&s->lock, flags);
+	if (rec) {
+		set_dmac(s, virt_to_bus(db->rawbuf), db->numfrag << db->fragshift);
+		/* program enhanced mode registers */
+		wrindir(s, SV_CIDMACBASECOUNT1, (db->fragsamples-1) >> 8);
+		wrindir(s, SV_CIDMACBASECOUNT0, db->fragsamples-1);
+	} else {
+		set_dmaa(s, virt_to_bus(db->rawbuf), db->numfrag << db->fragshift);
+		/* program enhanced mode registers */
+		wrindir(s, SV_CIDMAABASECOUNT1, (db->fragsamples-1) >> 8);
+		wrindir(s, SV_CIDMAABASECOUNT0, db->fragsamples-1);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	db->ready = 1;
+	return 0;
+}
+
+extern __inline__ void clear_advance(struct sv_state *s)
+{
+	unsigned char c = (s->fmt & (SV_CFMT_16BIT << SV_CFMT_ASHIFT)) ? 0 : 0x80;
+	unsigned char *buf = s->dma_dac.rawbuf;
+	unsigned bsize = s->dma_dac.dmasize;
+	unsigned bptr = s->dma_dac.swptr;
+	unsigned len = s->dma_dac.fragsize;
+
+	if (bptr + len > bsize) {
+		unsigned x = bsize - bptr;
+		memset(buf + bptr, c, x);
+		bptr = 0;
+		len -= x;
+	}
+	memset(buf + bptr, c, len);
+}
+
+/* call with spinlock held! */
+static void sv_update_ptr(struct sv_state *s)
+{
+	unsigned hwptr;
+	int diff;
+
+	/* update ADC pointer */
+	if (s->dma_adc.ready) {
+		hwptr = (s->dma_adc.dmasize - get_dmac(s)) % s->dma_adc.dmasize;
+		diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;
+		s->dma_adc.hwptr = hwptr;
+		s->dma_adc.total_bytes += diff;
+		s->dma_adc.count += diff;
+		if (s->dma_adc.mapped) {
+			if (s->dma_adc.count >= s->dma_adc.fragsize) 
+				wake_up(&s->dma_adc.wait);
+		} else {
+			if (s->dma_adc.count > s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1)) {
+				s->enable &= ~SV_CENABLE_RE;
+				wrindir(s, SV_CIENABLE, s->enable);
+				s->dma_adc.error++;
+			}
+			if (s->dma_adc.count > 0)
+				wake_up(&s->dma_adc.wait);
+		}
+	}
+	/* update DAC pointer */
+	if (s->dma_dac.ready) {
+		hwptr = (s->dma_dac.dmasize - get_dmaa(s)) % s->dma_dac.dmasize;
+		diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;
+		s->dma_dac.hwptr = hwptr;
+		s->dma_dac.total_bytes += diff;
+		if (s->dma_dac.mapped) {
+			s->dma_dac.count += diff;
+			if (s->dma_dac.count >= s->dma_dac.fragsize)
+				wake_up(&s->dma_dac.wait);
+		} else {
+			s->dma_dac.count -= diff;
+			if (s->dma_dac.count <= 0) {
+				s->enable &= ~SV_CENABLE_PE;
+				wrindir(s, SV_CIENABLE, s->enable);
+				s->dma_dac.error++;
+			} else if (s->dma_dac.count <= s->dma_dac.fragsize && !s->dma_dac.endcleared) {
+				clear_advance(s);
+				s->dma_dac.endcleared = 1;
+			}
+			if (s->dma_dac.count < s->dma_dac.dmasize)
+				wake_up(&s->dma_dac.wait);
+		}
+	}
+}
+
+/* hold spinlock for the following! */
+static void sv_handle_midi(struct sv_state *s)
+{
+	unsigned char ch;
+	int wake;
+
+	wake = 0;
+	while (!(inb(s->iomidi+1) & 0x80)) {
+		ch = inb(s->iomidi);
+		if (s->midi.icnt < MIDIINBUF) {
+			s->midi.ibuf[s->midi.iwr] = ch;
+			s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF;
+			s->midi.icnt++;
+		}
+		wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.iwait);
+	wake = 0;
+	while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) {
+		outb(s->midi.obuf[s->midi.ord], s->iomidi);
+		s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF;
+		s->midi.ocnt--;
+		if (s->midi.ocnt < MIDIOUTBUF-16)
+			wake = 1;
+	}
+	if (wake)
+		wake_up(&s->midi.owait);
+}
+
+static void sv_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+        struct sv_state *s = (struct sv_state *)dev_id;
+	unsigned int intsrc;
+	
+	/* fastpath out, to ease interrupt sharing */
+	intsrc = inb(s->ioenh + SV_CODEC_STATUS);
+	if (!(intsrc & (SV_CSTAT_DMAA | SV_CSTAT_DMAC | SV_CSTAT_MIDI)))
+		return;
+	spin_lock(&s->lock);
+	sv_update_ptr(s);
+	sv_handle_midi(s);
+	spin_unlock(&s->lock);
+}
+
+static void sv_midi_timer(unsigned long data)
+{
+	struct sv_state *s = (struct sv_state *)data;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&s->lock, flags);
+	sv_handle_midi(s);
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->midi.timer.expires = jiffies+1;
+	add_timer(&s->midi.timer);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT "sv: invalid magic value\n";
+
+#define VALIDATE_STATE(s)                         \
+({                                                \
+	if (!(s) || (s)->magic != SV_MAGIC) { \
+		printk(invalid_magic);            \
+		return -ENXIO;                    \
+	}                                         \
+})
+
+/* --------------------------------------------------------------------- */
+
+#define MT_4          1
+#define MT_5MUTE      2
+#define MT_4MUTEMONO  3
+#define MT_6MUTE      4
+
+static const struct {
+	unsigned left:5;
+	unsigned right:5;
+	unsigned type:3;
+	unsigned rec:3;
+} mixtable[SOUND_MIXER_NRDEVICES] = {
+	[SOUND_MIXER_RECLEV] = { SV_CIMIX_ADCINL,    SV_CIMIX_ADCINR,    MT_4,         0 },
+	[SOUND_MIXER_LINE1]  = { SV_CIMIX_AUX1INL,   SV_CIMIX_AUX1INR,   MT_5MUTE,     5 },
+	[SOUND_MIXER_CD]     = { SV_CIMIX_CDINL,     SV_CIMIX_CDINR,     MT_5MUTE,     1 },
+	[SOUND_MIXER_LINE]   = { SV_CIMIX_LINEINL,   SV_CIMIX_LINEINR,   MT_5MUTE,     4 },
+	[SOUND_MIXER_MIC]    = { SV_CIMIX_MICIN,     SV_CIMIX_ADCINL,    MT_4MUTEMONO, 6 },
+	[SOUND_MIXER_SYNTH]  = { SV_CIMIX_SYNTHINL,  SV_CIMIX_SYNTHINR,  MT_5MUTE,     2 },
+	[SOUND_MIXER_LINE2]  = { SV_CIMIX_AUX2INL,   SV_CIMIX_AUX2INR,   MT_5MUTE,     3 },
+	[SOUND_MIXER_VOLUME] = { SV_CIMIX_ANALOGINL, SV_CIMIX_ANALOGINR, MT_5MUTE,     7 },
+	[SOUND_MIXER_PCM]    = { SV_CIMIX_PCMINL,    SV_CIMIX_PCMINR,    MT_6MUTE,     0 }
+};
+
+static int return_mixval(struct sv_state *s, unsigned i, int *arg)
+{
+	unsigned long flags;
+	unsigned char l, r, rl, rr;
+
+	spin_lock_irqsave(&s->lock, flags);
+	l = rdindir(s, mixtable[i].left);
+	r = rdindir(s, mixtable[i].right);
+	spin_unlock_irqrestore(&s->lock, flags);
+	switch (mixtable[i].type) {
+	case MT_4:
+		r &= 0xf;
+		l &= 0xf;
+		rl = 10 + 6 * (l & 15);
+		rr = 10 + 6 * (r & 15);
+		break;
+
+	case MT_4MUTEMONO:
+		rl = 55 - 3 * (l & 15);
+		if (r & 0x10)
+			rl += 45;
+		rr = rl;
+		r = l;
+		break;
+
+	case MT_5MUTE:
+	default:
+		rl = 100 - 3 * (l & 31);
+		rr = 100 - 3 * (r & 31);
+		break;
+				
+	case MT_6MUTE:
+		rl = 100 - 3 * (l & 63) / 2;
+		rr = 100 - 3 * (r & 63) / 2;
+		break;
+	}
+	if (l & 0x80)
+		rl = 0;
+	if (r & 0x80)
+		rr = 0;
+	return put_user((rr << 8) | rl, arg);
+}
+
+static unsigned mixer_recmask(struct sv_state *s)
+{
+	unsigned long flags;
+	int i, j;
+
+	spin_lock_irqsave(&s->lock, flags);
+	j = rdindir(s, SV_CIMIX_ADCINL) >> 5;
+	spin_unlock_irqrestore(&s->lock, flags);
+	j &= 7;
+	for (i = 0; i < SOUND_MIXER_NRDEVICES && mixtable[i].rec != j; i++);
+	return 1 << i;
+}
+
+static int mixer_ioctl(struct sv_state *s, unsigned int cmd, unsigned long arg)
+{
+	unsigned long flags;
+	int i, val;
+	unsigned char l, r, rl, rr;
+
+	VALIDATE_STATE(s);
+        if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, "SonicVibes", sizeof(info.id));
+		strncpy(info.name, "S3 SonicVibes", sizeof(info.name));
+		info.modify_counter = s->mix.modcnt;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		strncpy(info.id, "SonicVibes", sizeof(info.id));
+		strncpy(info.name, "S3 SonicVibes", sizeof(info.name));
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *)arg);
+	if (cmd == SOUND_MIXER_PRIVATE1) {  /* SRS settings */
+		get_user_ret(val, (int *)arg, -EFAULT);
+		spin_lock_irqsave(&s->lock, flags);
+		if (val & 1) {
+			if (val & 2) {
+				l = 4 - ((val >> 2) & 7);
+				if (l & ~3)
+					l = 4;
+				r = 4 - ((val >> 5) & 7);
+				if (r & ~3)
+					r = 4;
+				wrindir(s, SV_CISRSSPACE, l);
+				wrindir(s, SV_CISRSCENTER, r);
+			} else
+				wrindir(s, SV_CISRSSPACE, 0x80);
+		}
+		l = rdindir(s, SV_CISRSSPACE);
+		r = rdindir(s, SV_CISRSCENTER);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (l & 0x80)
+			return put_user(0, (int *)arg);
+		return put_user(((4 - (l & 7)) << 2) | ((4 - (r & 7)) << 5) | 2, (int *)arg);
+	}
+	if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))
+                return -EINVAL;
+        if (_IOC_DIR(cmd) == _IOC_READ) {
+                switch (_IOC_NR(cmd)) {
+                case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+			return put_user(mixer_recmask(s), (int *)arg);
+			
+                case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].type)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+
+                case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].rec)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+				if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO)
+					val |= 1 << i;
+			return put_user(val, (int *)arg);
+			
+                case SOUND_MIXER_CAPS:
+			return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg);
+
+		default:
+			i = _IOC_NR(cmd);
+                        if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type)
+                                return -EINVAL;
+			return return_mixval(s, i, (int *)arg);
+		}
+	}
+        if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) 
+		return -EINVAL;
+	s->mix.modcnt++;
+	switch (_IOC_NR(cmd)) {
+	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+		get_user_ret(val, (int *)arg, -EFAULT);
+		i = hweight32(val);
+		if (i == 0)
+			return 0; /*val = mixer_recmask(s);*/
+		else if (i > 1) 
+			val &= ~mixer_recmask(s);
+		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+			if (!(val & (1 << i)))
+				continue;
+			if (mixtable[i].rec)
+				break;
+		}
+		if (!mixtable[i].rec)
+			return 0;
+		spin_lock_irqsave(&s->lock, flags);
+		frobindir(s, SV_CIMIX_ADCINL, 0x1f, mixtable[i].rec << 5);
+		frobindir(s, SV_CIMIX_ADCINR, 0x1f, mixtable[i].rec << 5);
+		spin_unlock_irqrestore(&s->lock, flags);
+		return 0;
+
+	default:
+		i = _IOC_NR(cmd);
+		if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type)
+			return -EINVAL;
+		get_user_ret(val, (int *)arg, -EFAULT);
+		l = val & 0xff;
+		r = (val >> 8) & 0xff;
+		if (mixtable[i].type == MT_4MUTEMONO)
+			l = (r + l) / 2;
+		if (l > 100)
+			l = 100;
+		if (r > 100)
+			r = 100;
+		spin_lock_irqsave(&s->lock, flags);
+		switch (mixtable[i].type) {
+		case MT_4:
+			if (l >= 10)
+				l -= 10;
+			if (r >= 10)
+				r -= 10;
+			frobindir(s, mixtable[i].left, 0xf0, l / 6);
+			frobindir(s, mixtable[i].right, 0xf0, l / 6);
+			break;
+
+		case MT_4MUTEMONO:
+			rr = 0;
+			if (l < 10)
+				rl = 0x80;
+			else {
+				if (l >= 55) {
+					rr = 0x10;
+					l -= 45;
+				}
+				rl = (55 - l) / 3;
+			}
+			wrindir(s, mixtable[i].left, rl);
+			frobindir(s, mixtable[i].right, ~0x10, rr);
+			break;
+			
+		case MT_5MUTE:
+			if (l < 7)
+				rl = 0x80;
+			else
+				rl = (100 - l) / 3;
+			if (r < 7)
+				rr = 0x80;
+			else
+				rr = (100 - r) / 3;
+			wrindir(s, mixtable[i].left, rl);
+			wrindir(s, mixtable[i].right, rr);
+			break;
+				
+		case MT_6MUTE:
+			if (l < 6)
+				rl = 0x80;
+			else
+				rl = (100 - l) * 2 / 3;
+			if (r < 6)
+				rr = 0x80;
+			else
+				rr = (100 - r) * 2 / 3;
+			wrindir(s, mixtable[i].left, rl);
+			wrindir(s, mixtable[i].right, rr);
+			break;
+		}
+		spin_unlock_irqrestore(&s->lock, flags);
+                return return_mixval(s, i, (int *)arg);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static loff_t sv_llseek(struct file *file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sv_open_mixdev(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct sv_state *s = devs;
+
+	while (s && s->dev_mixer != minor)
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int sv_release_mixdev(struct inode *inode, struct file *file)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	
+	VALIDATE_STATE(s);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int sv_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	return mixer_ioctl((struct sv_state *)file->private_data, cmd, arg);
+}
+
+static /*const*/ struct file_operations sv_mixer_fops = {
+	&sv_llseek,
+	NULL,  /* read */
+	NULL,  /* write */
+	NULL,  /* readdir */
+	NULL,  /* poll */
+	&sv_ioctl_mixdev,
+	NULL,  /* mmap */
+	&sv_open_mixdev,
+	&sv_release_mixdev,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct sv_state *s, int nonblock)
+{
+        struct wait_queue wait = { current, NULL };
+	unsigned long flags;
+	int count, tmo;
+
+	if (s->dma_dac.mapped)
+		return 0;
+        current->state = TASK_INTERRUPTIBLE;
+        add_wait_queue(&s->dma_dac.wait, &wait);
+        for (;;) {
+                spin_lock_irqsave(&s->lock, flags);
+		count = s->dma_dac.count;
+                spin_unlock_irqrestore(&s->lock, flags);
+		if (count <= 0)
+			break;
+		if (signal_pending(current))
+                        break;
+                if (nonblock) {
+                        remove_wait_queue(&s->dma_dac.wait, &wait);
+                        current->state = TASK_RUNNING;
+                        return -EBUSY;
+                }
+		tmo = (count * HZ) / s->ratedac;
+		tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK];
+		current->timeout = jiffies + (tmo ? tmo : 1);
+                schedule();
+		if (tmo && !current->timeout)
+			printk(KERN_DEBUG "sv: dma timed out??\n");
+		current->timeout = 0;
+        }
+        remove_wait_queue(&s->dma_dac.wait, &wait);
+        current->state = TASK_RUNNING;
+        if (signal_pending(current))
+                return -ERESTARTSYS;
+        return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t sv_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_adc.mapped)
+		return -ENXIO;
+	if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
+		return ret;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+#if 0
+   spin_lock_irqsave(&s->lock, flags);
+   sv_update_ptr(s);
+   spin_unlock_irqrestore(&s->lock, flags);
+#endif
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		swptr = s->dma_adc.swptr;
+		cnt = s->dma_adc.dmasize-swptr;
+		if (s->dma_adc.count < cnt)
+			cnt = s->dma_adc.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_adc(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->dma_adc.wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_adc.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_adc.swptr = swptr;
+		s->dma_adc.count -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_adc(s);
+	}
+	return ret;
+}
+
+static ssize_t sv_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned swptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (s->dma_dac.mapped)
+		return -ENXIO;
+	if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+		return ret;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+#if 0
+   spin_lock_irqsave(&s->lock, flags);
+   sv_update_ptr(s);
+   spin_unlock_irqrestore(&s->lock, flags);
+#endif
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		if (s->dma_dac.count < 0) {
+			s->dma_dac.count = 0;
+			s->dma_dac.swptr = s->dma_dac.hwptr;
+		}
+		swptr = s->dma_dac.swptr;
+		cnt = s->dma_dac.dmasize-swptr;
+		if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
+			cnt = s->dma_dac.dmasize - s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			start_dac(s);
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->dma_dac.wait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt))
+			return ret ? ret : -EFAULT;
+		swptr = (swptr + cnt) % s->dma_dac.dmasize;
+		spin_lock_irqsave(&s->lock, flags);
+		s->dma_dac.swptr = swptr;
+		s->dma_dac.count += cnt;
+		s->dma_dac.endcleared = 0;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		start_dac(s);
+	}
+	return ret;
+}
+
+static unsigned int sv_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_flags & FMODE_WRITE)
+		poll_wait(file, &s->dma_dac.wait, wait);
+	if (file->f_flags & FMODE_READ)
+		poll_wait(file, &s->dma_adc.wait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	sv_update_ptr(s);
+	if (file->f_flags & FMODE_READ) {
+		if (s->dma_adc.mapped) {
+			if (s->dma_adc.count >= s->dma_adc.fragsize)
+				mask |= POLLIN | POLLRDNORM;
+		} else {
+			if (s->dma_adc.count > 0)
+				mask |= POLLIN | POLLRDNORM;
+		}
+	}
+	if (file->f_flags & FMODE_WRITE) {
+		if (s->dma_dac.mapped) {
+			if (s->dma_dac.count >= s->dma_dac.fragsize) 
+				mask |= POLLOUT | POLLWRNORM;
+		} else {
+			if (s->dma_dac.dmasize > s->dma_dac.count)
+				mask |= POLLOUT | POLLWRNORM;
+		}
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int sv_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	struct dmabuf *db;
+	int ret;
+	unsigned long size;
+
+	VALIDATE_STATE(s);
+	if (vma->vm_flags & VM_WRITE) {
+		if ((ret = prog_dmabuf(s, 1)) != 0)
+			return ret;
+		db = &s->dma_dac;
+	} else if (vma->vm_flags & VM_READ) {
+		if ((ret = prog_dmabuf(s, 0)) != 0)
+			return ret;
+		db = &s->dma_adc;
+	} else 
+		return -EINVAL;
+	if (vma->vm_offset != 0)
+		return -EINVAL;
+	size = vma->vm_end - vma->vm_start;
+	if (size > (PAGE_SIZE << db->buforder))
+		return -EINVAL;
+	if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+		return -EAGAIN;
+	db->mapped = 1;
+	vma->vm_file = file;
+	file->f_count++;
+	return 0;
+}
+
+static int sv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	unsigned long flags;
+        audio_buf_info abinfo;
+        count_info cinfo;
+	int val, mapped, ret;
+	unsigned char fmtm, fmtd;
+
+	VALIDATE_STATE(s);
+        mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+		((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+	switch (cmd) {
+	case OSS_GETVERSION:
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	case SNDCTL_DSP_SYNC:
+		if (file->f_mode & FMODE_WRITE)
+			return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/);
+		return 0;
+		
+	case SNDCTL_DSP_SETDUPLEX:
+		return 0;
+
+	case SNDCTL_DSP_GETCAPS:
+		return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+		
+        case SNDCTL_DSP_RESET:
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			synchronize_irq();
+			s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0;
+		}
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			synchronize_irq();
+			s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SPEED:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val >= 0) {
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				set_adc_rate(s, val);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				set_dac_rate(s, val);
+			}
+		}
+		return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+		
+        case SNDCTL_DSP_STEREO:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		fmtd = 0;
+		fmtm = ~0;
+		if (file->f_mode & FMODE_READ) {
+			stop_adc(s);
+			s->dma_adc.ready = 0;
+			if (val)
+				fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT;
+			else
+				fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			stop_dac(s);
+			s->dma_dac.ready = 0;
+			if (val)
+				fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT;
+			else
+				fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT);
+		}
+		set_fmt(s, fmtm, fmtd);
+		return 0;
+
+        case SNDCTL_DSP_CHANNELS:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 0) {
+			fmtd = 0;
+			fmtm = ~0;
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				if (val >= 2)
+					fmtd |= SV_CFMT_STEREO << SV_CFMT_CSHIFT;
+				else
+					fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_CSHIFT);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val >= 2)
+					fmtd |= SV_CFMT_STEREO << SV_CFMT_ASHIFT;
+				else
+					fmtm &= ~(SV_CFMT_STEREO << SV_CFMT_ASHIFT);
+			}
+			set_fmt(s, fmtm, fmtd);
+		}
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_STEREO << SV_CFMT_CSHIFT) 
+					   : (SV_CFMT_STEREO << SV_CFMT_ASHIFT))) ? 2 : 1, (int *)arg);
+		
+	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+                return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != AFMT_QUERY) {
+			fmtd = 0;
+			fmtm = ~0;
+			if (file->f_mode & FMODE_READ) {
+				stop_adc(s);
+				s->dma_adc.ready = 0;
+				if (val == AFMT_S16_LE)
+					fmtd |= SV_CFMT_16BIT << SV_CFMT_CSHIFT;
+				else
+					fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_CSHIFT);
+			}
+			if (file->f_mode & FMODE_WRITE) {
+				stop_dac(s);
+				s->dma_dac.ready = 0;
+				if (val == AFMT_S16_LE)
+					fmtd |= SV_CFMT_16BIT << SV_CFMT_ASHIFT;
+				else
+					fmtm &= ~(SV_CFMT_16BIT << SV_CFMT_ASHIFT);
+			}
+			set_fmt(s, fmtm, fmtd);
+		}
+		return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (SV_CFMT_16BIT << SV_CFMT_CSHIFT) 
+					   : (SV_CFMT_16BIT << SV_CFMT_ASHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg);
+		
+	case SNDCTL_DSP_POST:
+                return 0;
+
+        case SNDCTL_DSP_GETTRIGGER:
+		val = 0;
+		if (file->f_mode & FMODE_READ && s->enable & SV_CENABLE_RE) 
+			val |= PCM_ENABLE_INPUT;
+		if (file->f_mode & FMODE_WRITE && s->enable & SV_CENABLE_PE) 
+			val |= PCM_ENABLE_OUTPUT;
+		return put_user(val, (int *)arg);
+		
+	case SNDCTL_DSP_SETTRIGGER:
+		get_user_ret(val, (int *)arg, -EFAULT);
+		if (file->f_mode & FMODE_READ) {
+			if (val & PCM_ENABLE_INPUT) {
+				if (!s->dma_adc.ready && (ret =  prog_dmabuf(s, 1)))
+					return ret;
+				start_adc(s);
+			} else
+				stop_adc(s);
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			if (val & PCM_ENABLE_OUTPUT) {
+				if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+					return ret;
+				start_dac(s);
+			} else
+				stop_dac(s);
+		}
+		return 0;
+
+	case SNDCTL_DSP_GETOSPACE:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		if (!(s->enable & SV_CENABLE_PE) && (val = prog_dmabuf(s, 0)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		sv_update_ptr(s);
+		abinfo.fragsize = s->dma_dac.fragsize;
+                abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count;
+                abinfo.fragstotal = s->dma_dac.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+	case SNDCTL_DSP_GETISPACE:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		if (!(s->enable & SV_CENABLE_RE) && (val = prog_dmabuf(s, 1)) != 0)
+			return val;
+		spin_lock_irqsave(&s->lock, flags);
+		sv_update_ptr(s);
+		abinfo.fragsize = s->dma_adc.fragsize;
+                abinfo.bytes = s->dma_adc.count;
+                abinfo.fragstotal = s->dma_adc.numfrag;
+                abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;      
+		spin_unlock_irqrestore(&s->lock, flags);
+		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+		
+        case SNDCTL_DSP_NONBLOCK:
+                file->f_flags |= O_NONBLOCK;
+                return 0;
+
+        case SNDCTL_DSP_GETODELAY:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		sv_update_ptr(s);
+                val = s->dma_dac.count;
+		spin_unlock_irqrestore(&s->lock, flags);
+		return put_user(val, (int *)arg);
+
+        case SNDCTL_DSP_GETIPTR:
+		if (!(file->f_mode & FMODE_READ))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		sv_update_ptr(s);
+                cinfo.bytes = s->dma_adc.total_bytes;
+                cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift;
+                cinfo.ptr = s->dma_adc.hwptr;
+		if (s->dma_adc.mapped)
+			s->dma_adc.count &= s->dma_adc.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETOPTR:
+		if (!(file->f_mode & FMODE_WRITE))
+			return -EINVAL;
+		spin_lock_irqsave(&s->lock, flags);
+		sv_update_ptr(s);
+                cinfo.bytes = s->dma_dac.total_bytes;
+                cinfo.blocks = s->dma_dac.total_bytes >> s->dma_dac.fragshift;
+                cinfo.ptr = s->dma_dac.hwptr;
+		if (s->dma_dac.mapped)
+			s->dma_dac.count &= s->dma_dac.fragsize-1;
+		spin_unlock_irqrestore(&s->lock, flags);
+                return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+        case SNDCTL_DSP_GETBLKSIZE:
+		if (file->f_mode & FMODE_WRITE) {
+			if ((val = prog_dmabuf(s, 0)))
+				return val;
+			return put_user(s->dma_dac.fragsize, (int *)arg);
+		}
+		if ((val = prog_dmabuf(s, 1)))
+			return val;
+		return put_user(s->dma_adc.fragsize, (int *)arg);
+
+        case SNDCTL_DSP_SETFRAGMENT:
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (file->f_mode & FMODE_READ) {
+			s->dma_adc.ossfragshift = val & 0xffff;
+			s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_adc.ossfragshift < 4)
+				s->dma_adc.ossfragshift = 4;
+			if (s->dma_adc.ossfragshift > 15)
+				s->dma_adc.ossfragshift = 15;
+			if (s->dma_adc.ossmaxfrags < 4)
+				s->dma_adc.ossmaxfrags = 4;
+		}
+		if (file->f_mode & FMODE_WRITE) {
+			s->dma_dac.ossfragshift = val & 0xffff;
+			s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
+			if (s->dma_dac.ossfragshift < 4)
+				s->dma_dac.ossfragshift = 4;
+			if (s->dma_dac.ossfragshift > 15)
+				s->dma_dac.ossfragshift = 15;
+			if (s->dma_dac.ossmaxfrags < 4)
+				s->dma_dac.ossmaxfrags = 4;
+		}
+		return 0;
+
+        case SNDCTL_DSP_SUBDIVIDE:
+		if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+		    (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
+			return -EINVAL;
+                get_user_ret(val, (int *)arg, -EFAULT);
+		if (val != 1 && val != 2 && val != 4)
+			return -EINVAL;
+		if (file->f_mode & FMODE_READ)
+			s->dma_adc.subdivision = val;
+		if (file->f_mode & FMODE_WRITE)
+			s->dma_dac.subdivision = val;
+		return 0;
+
+        case SOUND_PCM_WRITE_FILTER:
+        case SNDCTL_DSP_SETSYNCRO:
+        case SOUND_PCM_READ_RATE:
+        case SOUND_PCM_READ_CHANNELS:
+        case SOUND_PCM_READ_BITS:
+        case SOUND_PCM_READ_FILTER:
+                return -EINVAL;
+		
+	}
+	return mixer_ioctl(s, cmd, arg);
+}
+
+static int sv_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct sv_state *s = devs;
+	unsigned char fmtm = ~0, fmts = 0;
+
+	while (s && ((s->dev_audio ^ minor) & ~0xf))
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & file->f_mode) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	if (file->f_mode & FMODE_READ) {
+		fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_CSHIFT);
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			fmts |= SV_CFMT_16BIT << SV_CFMT_CSHIFT;
+		s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
+		set_adc_rate(s, 8000);
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		fmtm &= ~((SV_CFMT_STEREO | SV_CFMT_16BIT) << SV_CFMT_ASHIFT);
+		if ((minor & 0xf) == SND_DEV_DSP16)
+			fmts |= SV_CFMT_16BIT << SV_CFMT_ASHIFT;
+		s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0;
+		set_dac_rate(s, 8000);
+	}
+	set_fmt(s, fmtm, fmts);
+	s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int sv_release(struct inode *inode, struct file *file)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+
+	VALIDATE_STATE(s);
+	if (file->f_mode & FMODE_WRITE)
+		drain_dac(s, file->f_flags & O_NONBLOCK);
+	down(&s->open_sem);
+	if (file->f_flags & FMODE_WRITE) {
+		stop_dac(s);
+		dealloc_dmabuf(&s->dma_dac);
+	}
+	if (file->f_flags & FMODE_READ) {
+		stop_adc(s);
+		dealloc_dmabuf(&s->dma_adc);
+	}
+	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static /*const*/ struct file_operations sv_audio_fops = {
+	&sv_llseek,
+	&sv_read,
+	&sv_write,
+	NULL,  /* readdir */
+	&sv_poll,
+	&sv_ioctl,
+	&sv_mmap,
+	&sv_open,
+	&sv_release,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_WRITE, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.ird;
+		cnt = MIDIINBUF - ptr;
+		if (s->midi.icnt < cnt)
+			cnt = s->midi.icnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->midi.iwait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt))
+			return ret ? ret : -EFAULT;
+		ptr = (ptr + cnt) % MIDIINBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.ird = ptr;
+		s->midi.icnt -= cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+	}
+	return ret;
+}
+
+static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	ssize_t ret;
+	unsigned long flags;
+	unsigned ptr;
+	int cnt;
+
+	VALIDATE_STATE(s);
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return -EFAULT;
+	ret = 0;
+	while (count > 0) {
+		spin_lock_irqsave(&s->lock, flags);
+		ptr = s->midi.owr;
+		cnt = MIDIOUTBUF - ptr;
+		if (s->midi.ocnt + cnt > MIDIOUTBUF)
+			cnt = MIDIOUTBUF - s->midi.ocnt;
+		if (cnt <= 0)
+			sv_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+		if (cnt > count)
+			cnt = count;
+		if (cnt <= 0) {
+			if (file->f_flags & O_NONBLOCK)
+				return ret ? ret : -EBUSY;
+			interruptible_sleep_on(&s->midi.owait);
+			if (signal_pending(current))
+				return ret ? ret : -ERESTARTSYS;
+			continue;
+		}
+		if (copy_from_user(s->midi.obuf + ptr, buffer, cnt))
+			return ret ? ret : -EFAULT;
+		ptr = (ptr + cnt) % MIDIOUTBUF;
+		spin_lock_irqsave(&s->lock, flags);
+		s->midi.owr = ptr;
+		s->midi.ocnt += cnt;
+		spin_unlock_irqrestore(&s->lock, flags);
+		count -= cnt;
+		buffer += cnt;
+		ret += cnt;
+		spin_lock_irqsave(&s->lock, flags);
+		sv_handle_midi(s);
+		spin_unlock_irqrestore(&s->lock, flags);
+	}
+	return ret;
+}
+
+static unsigned int sv_midi_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	VALIDATE_STATE(s);
+	if (file->f_flags & FMODE_WRITE)
+		poll_wait(file, &s->midi.owait, wait);
+	if (file->f_flags & FMODE_READ)
+		poll_wait(file, &s->midi.iwait, wait);
+	spin_lock_irqsave(&s->lock, flags);
+	if (file->f_flags & FMODE_READ) {
+		if (s->midi.icnt > 0)
+			mask |= POLLIN | POLLRDNORM;
+	}
+	if (file->f_flags & FMODE_WRITE) {
+		if (s->midi.ocnt < MIDIOUTBUF)
+			mask |= POLLOUT | POLLWRNORM;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	return mask;
+}
+
+static int sv_midi_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct sv_state *s = devs;
+	unsigned long flags;
+
+	while (s && s->dev_midi != minor)
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+		//outb(inb(s->ioenh + SV_CODEC_CONTROL) | SV_CCTRL_WAVETABLE, s->ioenh + SV_CODEC_CONTROL);
+		outb(inb(s->ioenh + SV_CODEC_INTMASK) | SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK);
+		wrindir(s, SV_CIUARTCONTROL, 5); /* output MIDI data to external and internal synth */
+		wrindir(s, SV_CIWAVETABLESRC, 1); /* Wavetable in PC RAM */
+		outb(0xff, s->iomidi+1); /* reset command */
+		outb(0x3f, s->iomidi+1); /* uart command */
+		if (!(inb(s->iomidi+1) & 0x80))
+			inb(s->iomidi);
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+		init_timer(&s->midi.timer);
+		s->midi.timer.expires = jiffies+1;
+		s->midi.timer.data = (unsigned long)s;
+		s->midi.timer.function = sv_midi_timer;
+		add_timer(&s->midi.timer);
+	}
+	if (file->f_mode & FMODE_READ) {
+		s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+	}
+	if (file->f_mode & FMODE_WRITE) {
+		s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int sv_midi_release(struct inode *inode, struct file *file)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+        struct wait_queue wait = { current, NULL };
+	unsigned long flags;
+	unsigned count, tmo;
+
+	VALIDATE_STATE(s);
+
+	if (file->f_mode & FMODE_WRITE) {
+		current->state = TASK_INTERRUPTIBLE;
+		add_wait_queue(&s->midi.owait, &wait);
+		for (;;) {
+			spin_lock_irqsave(&s->lock, flags);
+			count = s->midi.ocnt;
+			spin_unlock_irqrestore(&s->lock, flags);
+			if (count <= 0)
+				break;
+			if (signal_pending(current))
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				remove_wait_queue(&s->midi.owait, &wait);
+				current->state = TASK_RUNNING;
+				return -EBUSY;
+			}
+			tmo = (count * HZ) / 3100;
+			current->timeout = tmo ? jiffies + tmo : 0;
+			schedule();
+			if (tmo && !current->timeout)
+				printk(KERN_DEBUG "sv: midi timed out??\n");
+			current->timeout = 0;
+		}
+		remove_wait_queue(&s->midi.owait, &wait);
+		current->state = TASK_RUNNING;
+	}
+	down(&s->open_sem);
+	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	spin_lock_irqsave(&s->lock, flags);
+	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+		outb(inb(s->ioenh + SV_CODEC_INTMASK) & ~SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK);
+		del_timer(&s->midi.timer);		
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static /*const*/ struct file_operations sv_midi_fops = {
+	&sv_llseek,
+	&sv_midi_read,
+	&sv_midi_write,
+	NULL,  /* readdir */
+	&sv_midi_poll,
+	NULL,  /* ioctl */
+	NULL,  /* mmap */
+	&sv_midi_open,
+	&sv_midi_release,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+static int sv_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	static const unsigned char op_offset[18] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15
+	};
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	struct dm_fm_voice v;
+	struct dm_fm_note n;
+	struct dm_fm_params p;
+	unsigned int io;
+	unsigned int regb;
+
+	switch (cmd) {		
+	case FM_IOCTL_RESET:
+		for (regb = 0xb0; regb < 0xb9; regb++) {
+			outb(regb, s->iosynth);
+			outb(0, s->iosynth+1);
+			outb(regb, s->iosynth+2);
+			outb(0, s->iosynth+3);
+		}
+		return 0;
+
+	case FM_IOCTL_PLAY_NOTE:
+		if (copy_from_user(&n, (void *)arg, sizeof(n)))
+			return -EFAULT;
+		if (n.voice >= 18)
+			return -EINVAL;
+		if (n.voice >= 9) {
+			regb = n.voice - 9;
+			io = s->iosynth+2;
+		} else {
+			regb = n.voice;
+			io = s->iosynth;
+		}
+		outb(0xa0 + regb, io);
+		outb(n.fnum & 0xff, io+1);
+		outb(0xb0 + regb, io);
+		outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1);
+		return 0;
+
+	case FM_IOCTL_SET_VOICE:
+		if (copy_from_user(&v, (void *)arg, sizeof(v)))
+			return -EFAULT;
+		if (v.voice >= 18)
+			return -EINVAL;
+		regb = op_offset[v.voice];
+		io = s->iosynth + ((v.op & 1) << 1);
+		outb(0x20 + regb, io);
+		outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) | 
+		     ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1);
+		outb(0x40 + regb, io);
+		outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1);
+		outb(0x60 + regb, io);
+		outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1);
+		outb(0x80 + regb, io);
+		outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1);
+		outb(0xe0 + regb, io);
+		outb(v.waveform & 0x7, io+1);
+		if (n.voice >= 9) {
+			regb = n.voice - 9;
+			io = s->iosynth+2;
+		} else {
+			regb = n.voice;
+			io = s->iosynth;
+		}
+		outb(0xc0 + regb, io);
+		outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) |
+		     (v.connection & 1), io+1);
+		return 0;
+		
+	case FM_IOCTL_SET_PARAMS:
+		if (copy_from_user(&p, (void *)arg, sizeof(p)))
+			return -EFAULT;
+		outb(0x08, s->iosynth);
+		outb((p.kbd_split & 1) << 6, s->iosynth+1);
+		outb(0xbd, s->iosynth);
+		outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) |
+		     ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1);
+		return 0;
+
+	case FM_IOCTL_SET_OPL:
+		outb(4, s->iosynth+2);
+		outb(arg, s->iosynth+3);
+		return 0;
+
+	case FM_IOCTL_SET_MODE:
+		outb(5, s->iosynth+2);
+		outb(arg & 1, s->iosynth+3);
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int sv_dmfm_open(struct inode *inode, struct file *file)
+{
+	int minor = MINOR(inode->i_rdev);
+	struct sv_state *s = devs;
+
+	while (s && s->dev_dmfm != minor)
+		s = s->next;
+	if (!s)
+		return -ENODEV;
+       	VALIDATE_STATE(s);
+	file->private_data = s;
+	/* wait for device to become free */
+	down(&s->open_sem);
+	while (s->open_mode & FMODE_DMFM) {
+		if (file->f_flags & O_NONBLOCK) {
+			up(&s->open_sem);
+			return -EBUSY;
+		}
+		up(&s->open_sem);
+		interruptible_sleep_on(&s->open_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		down(&s->open_sem);
+	}
+	/* init the stuff */
+	outb(1, s->iosynth);
+	outb(0x20, s->iosynth+1); /* enable waveforms */
+	outb(4, s->iosynth+2);
+	outb(0, s->iosynth+3);  /* no 4op enabled */
+	outb(5, s->iosynth+2);
+	outb(1, s->iosynth+3);  /* enable OPL3 */
+	s->open_mode |= FMODE_DMFM;
+	up(&s->open_sem);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int sv_dmfm_release(struct inode *inode, struct file *file)
+{
+	struct sv_state *s = (struct sv_state *)file->private_data;
+	unsigned int regb;
+
+	VALIDATE_STATE(s);
+	down(&s->open_sem);
+	s->open_mode &= ~FMODE_DMFM;
+	for (regb = 0xb0; regb < 0xb9; regb++) {
+		outb(regb, s->iosynth);
+		outb(0, s->iosynth+1);
+		outb(regb, s->iosynth+2);
+		outb(0, s->iosynth+3);
+	}
+	up(&s->open_sem);
+	wake_up(&s->open_wait);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static /*const*/ struct file_operations sv_dmfm_fops = {
+	&sv_llseek,
+	NULL,  /* read */
+	NULL,  /* write */
+	NULL,  /* readdir */
+	NULL,  /* poll */
+	&sv_dmfm_ioctl,
+	NULL,  /* mmap */
+	&sv_dmfm_open,
+	&sv_dmfm_release,
+	NULL,  /* fsync */
+	NULL,  /* fasync */
+	NULL,  /* check_media_change */
+	NULL,  /* revalidate */
+	NULL,  /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+/* maximum number of devices */
+#define NR_DEVICE 5
+
+static int reverb[NR_DEVICE] = { 0, };
+static int wavetable[NR_DEVICE] = { 0, };
+
+static unsigned dmaio = 0xac00;
+
+/* --------------------------------------------------------------------- */
+
+static const struct initvol {
+	int mixch;
+	int vol;
+} initvol[] __initdata = {
+	{ SOUND_MIXER_WRITE_RECLEV, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE1, 0x4040 },
+	{ SOUND_MIXER_WRITE_CD, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE, 0x4040 },
+	{ SOUND_MIXER_WRITE_MIC, 0x4040 },
+	{ SOUND_MIXER_WRITE_SYNTH, 0x4040 },
+	{ SOUND_MIXER_WRITE_LINE2, 0x4040 },
+	{ SOUND_MIXER_WRITE_VOLUME, 0x4040 },
+	{ SOUND_MIXER_WRITE_PCM, 0x4040 }
+};
+
+#ifdef MODULE
+__initfunc(int init_module(void))
+#else
+__initfunc(int init_sonicvibes(void))
+#endif
+{
+	struct sv_state *s;
+	struct pci_dev *pcidev = NULL;
+	mm_segment_t fs;
+	int i, val, index = 0;
+
+	if (!pci_present())   /* No PCI bus in this machine! */
+		return -ENODEV;
+	printk(KERN_INFO "sv: version v0.5 time " __TIME__ " " __DATE__ "\n");
+#if 0
+	if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
+		printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
+#endif
+	while (index < NR_DEVICE && 
+	       (pcidev = pci_find_device(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, pcidev))) {
+		if (pcidev->base_address[1] == 0 || 
+		    (pcidev->base_address[1] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
+			continue;
+		if (pcidev->base_address[2] == 0 || 
+		    (pcidev->base_address[2] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
+			continue;
+		if (pcidev->base_address[3] == 0 || 
+		    (pcidev->base_address[3] & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_IO)
+			continue;
+		if (pcidev->irq == 0)
+			continue;
+		if (!(s = kmalloc(sizeof(struct sv_state), GFP_KERNEL))) {
+			printk(KERN_WARNING "sv: out of memory\n");
+			continue;
+		}
+		memset(s, 0, sizeof(struct sv_state));
+		init_waitqueue(&s->dma_adc.wait);
+		init_waitqueue(&s->dma_dac.wait);
+		init_waitqueue(&s->open_wait);
+		init_waitqueue(&s->midi.iwait);
+		init_waitqueue(&s->midi.owait);
+		s->open_sem = MUTEX;
+		s->magic = SV_MAGIC;
+		s->iosb = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+		s->ioenh = pcidev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
+		s->iosynth = pcidev->base_address[2] & PCI_BASE_ADDRESS_IO_MASK;
+		s->iomidi = pcidev->base_address[3] & PCI_BASE_ADDRESS_IO_MASK;
+		s->iogame = pcidev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK;
+		pci_read_config_dword(pcidev, 0x40, &s->iodmaa);
+		pci_read_config_dword(pcidev, 0x48, &s->iodmac);
+		dmaio &= ~(SV_EXTENT_DMA-1);
+		s->iodmaa &= ~(SV_EXTENT_DMA-1);
+		s->iodmac &= ~(SV_EXTENT_DMA-1);
+		if (!(s->iodmaa)) {
+			s->iodmaa = dmaio;
+			dmaio += SV_EXTENT_DMA;
+			printk(KERN_INFO "sv: BIOS did not allocate DDMA channel A io, allocated at %#x\n",
+			       s->iodmaa);
+		}
+		if (!(s->iodmac)) {
+			s->iodmac = dmaio;
+			dmaio += SV_EXTENT_DMA;
+			printk(KERN_INFO "sv: BIOS did not allocate DDMA channel C io, allocated at %#x\n",
+			       s->iodmac);
+		}
+		pci_write_config_dword(pcidev, 0x40, s->iodmaa | 9);  /* enable and use extended mode */
+		pci_write_config_dword(pcidev, 0x48, s->iodmac | 9);  /* enable */
+		printk(KERN_DEBUG "sv: io ports: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+		       s->iosb, s->ioenh, s->iosynth, s->iomidi, s->iogame, s->iodmaa, s->iodmac);
+		if (s->ioenh == 0 || s->iodmaa == 0 || s->iodmac == 0)
+			continue;
+		s->irq = pcidev->irq;
+
+		/* hack */
+		pci_write_config_dword(pcidev, 0x60, wavetable_mem >> 12);  /* wavetable base address */
+
+
+		if (check_region(s->ioenh, SV_EXTENT_ENH)) {
+			printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->ioenh, s->ioenh+SV_EXTENT_ENH-1);
+			goto err_region5;
+		}
+		request_region(s->ioenh, SV_EXTENT_ENH, "S3 SonicVibes PCM");
+		if (check_region(s->iodmaa, SV_EXTENT_DMA)) {
+			printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmaa, s->iodmaa+SV_EXTENT_DMA-1);
+			goto err_region4;
+		}
+		request_region(s->iodmaa, SV_EXTENT_DMA, "S3 SonicVibes DMAA");
+		if (check_region(s->iodmac, SV_EXTENT_DMA)) {
+			printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iodmac, s->iodmac+SV_EXTENT_DMA-1);
+			goto err_region3;
+		}
+		request_region(s->iodmac, SV_EXTENT_DMA, "S3 SonicVibes DMAC");
+		if (check_region(s->iomidi, SV_EXTENT_MIDI)) {
+			printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+SV_EXTENT_MIDI-1);
+			goto err_region2;
+		}
+		request_region(s->iomidi, SV_EXTENT_MIDI, "S3 SonicVibes Midi");
+		if (check_region(s->iosynth, SV_EXTENT_SYNTH)) {
+			printk(KERN_ERR "sv: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+SV_EXTENT_SYNTH-1);
+			goto err_region1;
+		}
+		request_region(s->iosynth, SV_EXTENT_SYNTH, "S3 SonicVibes Synth");
+		/* initialize codec registers */
+		outb(0x80, s->ioenh + SV_CODEC_CONTROL); /* assert reset */
+		udelay(50);
+		outb(0x00, s->ioenh + SV_CODEC_CONTROL); /* deassert reset */
+		udelay(50);
+		outb(SV_CCTRL_INTADRIVE | SV_CCTRL_ENHANCED /*| SV_CCTRL_WAVETABLE | SV_CCTRL_REVERB*/,
+		     s->ioenh + SV_CODEC_CONTROL);
+		inb(s->ioenh + SV_CODEC_STATUS); /* clear ints */
+		wrindir(s, SV_CIDRIVECONTROL, 0);  /* drive current 16mA */
+		wrindir(s, SV_CIENABLE, s->enable = 0);  /* disable DMAA and DMAC */
+		outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK);
+		//outb(0xff, s->iodmaa + SV_DMA_RESET);
+		//outb(0xff, s->iodmac + SV_DMA_RESET);
+		inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */
+		wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */
+		wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */
+		wrindir(s, SV_CIDIGITALPWRDOWN, 0); /* power up the digital parts of the device */
+		setpll(s, SV_CIADCPLLM, 8000);
+		wrindir(s, SV_CISRSSPACE, 0x80); /* SRS off */
+		wrindir(s, SV_CIPCMSR0, (8000 * 65536 / FULLRATE) & 0xff);
+		wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff);
+		wrindir(s, SV_CIADCOUTPUT, 0);
+		/* request irq */
+		if (request_irq(s->irq, sv_interrupt, SA_INTERRUPT|SA_SHIRQ, "S3 SonicVibes", s)) {
+			printk(KERN_ERR "sv: irq %u in use\n", s->irq);
+			goto err_irq;
+		}
+		printk(KERN_INFO "sv: found adapter at io %#06x irq %u dmaa %#06x dmac %#06x revision %u\n",
+		       s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION));
+		/* register devices */
+		if ((s->dev_audio = register_sound_dsp(&sv_audio_fops)) < 0)
+			goto err_dev1;
+		if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops)) < 0)
+			goto err_dev2;
+		if ((s->dev_midi = register_sound_midi(&sv_midi_fops)) < 0)
+			goto err_dev3;
+		if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0)
+			goto err_dev4;
+		/* initialize the chips */
+		fs = get_fs();
+		set_fs(KERNEL_DS);
+		val = SOUND_MASK_LINE|SOUND_MASK_SYNTH;
+		mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+		for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
+			val = initvol[i].vol;
+			mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
+		}
+		set_fs(fs);
+		/* queue it for later freeing */
+		s->next = devs;
+		devs = s;
+		index++;
+		continue;
+
+	err_dev4:
+		unregister_sound_midi(s->dev_midi);
+	err_dev3:
+		unregister_sound_mixer(s->dev_mixer);
+	err_dev2:
+		unregister_sound_dsp(s->dev_audio);
+	err_dev1:
+		printk(KERN_ERR "sv: cannot register misc device\n");
+		free_irq(s->irq, s);
+	err_irq:
+		release_region(s->iosynth, SV_EXTENT_SYNTH);
+	err_region1:
+		release_region(s->iomidi, SV_EXTENT_MIDI);
+	err_region2:
+		release_region(s->iodmac, SV_EXTENT_DMA);
+	err_region3:
+		release_region(s->iodmaa, SV_EXTENT_DMA);
+	err_region4:
+		release_region(s->ioenh, SV_EXTENT_ENH);
+	err_region5:
+		kfree_s(s, sizeof(struct sv_state));
+	}
+	if (!devs) {
+		if (wavetable_mem)
+			free_pages(wavetable_mem, 20-PAGE_SHIFT);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+MODULE_PARM(reverb, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(reverb, "if 1 enables joystick interface (still need separate driver)");
+MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(wavetable, "if 1 the LINE input is converted to LINE out");
+
+MODULE_PARM(dmaio, "i");
+MODULE_PARM_DESC(dmaio, "if the motherboard BIOS did not allocate DDMA io, allocate them starting at this address");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("S3 SonicVibes Driver");
+
+void cleanup_module(void)
+{
+	struct sv_state *s;
+
+	while ((s = devs)) {
+		devs = devs->next;
+		outb(~0, s->ioenh + SV_CODEC_INTMASK);  /* disable ints */
+		synchronize_irq();
+		inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */
+		wrindir(s, SV_CIENABLE, 0);     /* disable DMAA and DMAC */
+		//outb(0, s->iodmaa + SV_DMA_RESET);
+		//outb(0, s->iodmac + SV_DMA_RESET);
+		free_irq(s->irq, s);
+		release_region(s->iodmac, SV_EXTENT_DMA);
+		release_region(s->iodmaa, SV_EXTENT_DMA);
+		release_region(s->ioenh, SV_EXTENT_ENH);
+		release_region(s->iomidi, SV_EXTENT_MIDI);
+		release_region(s->iosynth, SV_EXTENT_SYNTH);
+		unregister_sound_dsp(s->dev_audio);
+		unregister_sound_mixer(s->dev_mixer);
+		unregister_sound_midi(s->dev_midi);
+		unregister_sound_special(s->dev_dmfm);
+		kfree_s(s, sizeof(struct sv_state));
+	}
+	if (wavetable_mem)
+		free_pages(wavetable_mem, 20-PAGE_SHIFT);
+	printk(KERN_INFO "sv: unloading\n");
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/sound_core.c linux/drivers/sound/sound_core.c
--- v2.1.108/linux/drivers/sound/sound_core.c	Wed Jul  1 19:38:55 1998
+++ linux/drivers/sound/sound_core.c	Mon Jul 13 12:43:04 1998
@@ -34,6 +34,7 @@
  *	locking at some point in 2.3.x.
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/malloc.h>
 #include <linux/types.h>
@@ -318,5 +319,23 @@
 		printk(KERN_ERR "soundcore: sound device already in use.\n");
 		return -EBUSY;
 	}
+	/*
+	 *	Now init non OSS drivers
+	 */
+#ifdef CONFIG_SOUND_SONICVIBES
+	init_sonicvibes();
+#endif
+#ifdef CONFIG_SOUND_ES1370
+	init_es1370();
+#endif
+#ifdef CONFIG_SOUND_ES1371
+	init_es1371();
+#endif
+#ifdef CONFIG_SOUND_MSNDCLAS
+	msnd_classic_init();
+#endif
+#ifdef CONFIG_SOUND_MSNDPIN
+	msnd_pinnacle_init();
+#endif
 	return 0;
 }
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v2.1.108/linux/drivers/sound/soundcard.c	Tue Jun 23 10:01:24 1998
+++ linux/drivers/sound/soundcard.c	Fri Jul 10 14:03:36 1998
@@ -1,7 +1,7 @@
 /*
  * linux/drivers/sound/soundcard.c
  *
- * Soundcard driver for Linux
+ * Sound card driver for Linux
  */
 /*
  * Copyright (C) by Hannu Savolainen 1993-1997
@@ -435,7 +435,7 @@
 	}
 	dev = MINOR(inode->i_rdev);
 	if (!soundcard_configured && dev != SND_DEV_CTL && dev != SND_DEV_STATUS) {
-		/* printk("SoundCard Error: The soundcard system has not been configured\n");*/
+		/* printk("SoundCard Error: The sound system has not been configured\n");*/
 		return -ENXIO;
 	}
 	DEB(printk("sound_open(dev=%d)\n", dev));
diff -u --recursive --new-file v2.1.108/linux/drivers/sound/uart401.c linux/drivers/sound/uart401.c
--- v2.1.108/linux/drivers/sound/uart401.c	Tue Jun  9 11:57:30 1998
+++ linux/drivers/sound/uart401.c	Fri Jul 10 14:03:36 1998
@@ -81,7 +81,9 @@
 
 static void uart401_input_loop(uart401_devc * devc)
 {
-	while (input_avail(devc))
+	int work_limit=30000;
+	
+	while (input_avail(devc) && --work_limit)
 	{
 		unsigned char   c = uart401_read(devc);
 
@@ -90,17 +92,19 @@
 		else if (devc->opened & OPEN_READ && devc->midi_input_intr)
 			devc->midi_input_intr(devc->my_dev, c);
 	}
+	if(work_limit==0)
+		printk(KERN_WARNING "Too much work in interrupt on uart401 (0x%X). UART jabbering ??\n", devc->base);
 }
 
 void uart401intr(int irq, void *dev_id, struct pt_regs *dummy)
 {
 	uart401_devc *devc = dev_id;
 
-	if (irq < 1 || irq > 15)
-		return;
-
 	if (devc == NULL)
+	{
+		printk(KERN_ERR "uart401: bad devc\n");
 		return;
+	}
 
 	if (input_avail(devc))
 		uart401_input_loop(devc);
@@ -115,9 +119,10 @@
 	uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
 
 	if (devc->opened)
-	{
 		return -EBUSY;
-	}
+
+	/* Flush the UART */
+	
 	while (input_avail(devc))
 		uart401_read(devc);
 
@@ -311,9 +316,9 @@
 	if (midi_devs[devc->my_dev]->converter == NULL)
 	{
 		printk(KERN_WARNING "uart401: Failed to allocate memory\n");
-		sound_unload_mididev(devc->my_dev);
 		kfree(midi_devs[devc->my_dev]);
 		kfree(devc);
+		sound_unload_mididev(devc->my_dev);
 		devc=NULL;
 		return;
 	}
diff -u --recursive --new-file v2.1.108/linux/drivers/video/Config.in linux/drivers/video/Config.in
--- v2.1.108/linux/drivers/video/Config.in	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/Config.in	Fri Jul 10 15:18:31 1998
@@ -3,10 +3,7 @@
 #
 
 if [ "$CONFIG_FB" = "y" ]; then
-
-  mainmenu_option next_comment
-  comment 'Frame buffer devices'
-
+  define_bool CONFIG_DUMMY_CONSOLE y
   if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
     define_bool CONFIG_FB_ACORN y
   fi
@@ -51,97 +48,152 @@
   if [ "$CONFIG_HP300" = "y" ]; then
     define_bool CONFIG_FB_HP300 y
   fi
-  if [ "$CONFIG_TGA_CONSOLE" = "y" ]; then
-    define_bool CONFIG_FB_TGA y
+  # I used CONFIG_ARM here because the ARCH construct doesn't seem to work
+  # with xconfig.  --pb
+  if [ "$ARCH" = "i386" -o "$ARCH" = "alpha" -o "$ARCH" = "ppc" -o \
+       "$CONFIG_ARM" = "y" ]; then
+    tristate 'VGA chipset support (text only)' CONFIG_FB_VGA
   fi
-  if [ "$ARCH" = "i386" -o "$ARCH" = "alpha" -o "$ARCH" = "ppc" ]; then
-    bool 'VGA chipset support (text only)' CONFIG_FB_VGA
-  fi
-  if [ "$ARCH" = "i386" ]; then
-    bool 'VESA VGA graphics console' CONFIG_FB_VESA
-    define_bool CONFIG_VIDEO_SELECT y
+  if [ "$ARCH" = "alpha" ]; then
+    tristate 'TGA framebuffer support' CONFIG_FB_TGA
   fi
   if [ "$ARCH" = "i386" ]; then
+    if [ "$CONFIG_VGA_CONSOLE" != "y" ]; then
+      bool 'VESA VGA graphics console' CONFIG_FB_VESA
+      define_bool CONFIG_VIDEO_SELECT y
+    fi
     tristate 'MDA dual-headed support' CONFIG_FB_MDA
   fi
+  if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
+    bool 'PROM framebuffer' CONFIG_FB_PROM
+    bool 'SBUS and UPA framebuffers' CONFIG_FB_SBUS
+    if [ "$CONFIG_FB_SBUS" != "n" ]; then
+      if [ "$ARCH" = "sparc64" ]; then
+	bool 'Creator/Creator3D support' CONFIG_FB_CREATOR
+      fi
+      bool 'CGsix (GX,GXplus) support' CONFIG_FB_CGSIX
+    fi
+  fi
   tristate 'Virtual Frame Buffer support (ONLY FOR TESTING!)' CONFIG_FB_VIRTUAL
 
   bool 'Advanced low level driver options' CONFIG_FBCON_ADVANCED
   if [ "$CONFIG_FBCON_ADVANCED" = "y" ]; then
-    bool 'Monochrome support' CONFIG_FBCON_MFB
-    bool '2 bpp packed pixels support' CONFIG_FBCON_CFB2
-    bool '4 bpp packed pixels support' CONFIG_FBCON_CFB4
-    bool '8 bpp packed pixels support' CONFIG_FBCON_CFB8
-    bool '16 bpp packed pixels support' CONFIG_FBCON_CFB16
-    bool '24 bpp packed pixels support' CONFIG_FBCON_CFB24
-    bool '32 bpp packed pixels support' CONFIG_FBCON_CFB32
-    bool 'Amiga bitplanes support' CONFIG_FBCON_AFB
-    bool 'Amiga interleaved bitplanes support' CONFIG_FBCON_ILBM
-    bool 'Atari interleaved bitplanes (2 planes) support' CONFIG_FBCON_IPLAN2P2
-    bool 'Atari interleaved bitplanes (4 planes) support' CONFIG_FBCON_IPLAN2P4
-    bool 'Atari interleaved bitplanes (8 planes) support' CONFIG_FBCON_IPLAN2P8
-    bool 'Mac variable bpp packed pixels support' CONFIG_FBCON_MAC
-    bool 'VGA characters/attributes support' CONFIG_FBCON_VGA
+    tristate 'Monochrome support' CONFIG_FBCON_MFB
+    tristate '2 bpp packed pixels support' CONFIG_FBCON_CFB2
+    tristate '4 bpp packed pixels support' CONFIG_FBCON_CFB4
+    tristate '8 bpp packed pixels support' CONFIG_FBCON_CFB8
+    tristate '16 bpp packed pixels support' CONFIG_FBCON_CFB16
+    tristate '24 bpp packed pixels support' CONFIG_FBCON_CFB24
+    tristate '32 bpp packed pixels support' CONFIG_FBCON_CFB32
+    tristate 'Amiga bitplanes support' CONFIG_FBCON_AFB
+    tristate 'Amiga interleaved bitplanes support' CONFIG_FBCON_ILBM
+    tristate 'Atari interleaved bitplanes (2 planes) support' CONFIG_FBCON_IPLAN2P2
+    tristate 'Atari interleaved bitplanes (4 planes) support' CONFIG_FBCON_IPLAN2P4
+    tristate 'Atari interleaved bitplanes (8 planes) support' CONFIG_FBCON_IPLAN2P8
+#   tristate 'Atari interleaved bitplanes (16 planes) support' CONFIG_FBCON_IPLAN2P16
+    tristate 'Mac variable bpp packed pixels support' CONFIG_FBCON_MAC
+    tristate 'VGA characters/attributes support' CONFIG_FBCON_VGA
   else
-    if [ "$CONFIG_FB_AMIGA" = "y" -o "$CONFIG_FB_AMIGA" = "m" -o \
-	 "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \
-	 "$CONFIG_FB_CYBER" = "y" -o "$CONFIG_FB_CYBER" = "m" -o \
-	 "$CONFIG_FB_RETINAZ3" = "y" -o "$CONFIG_FB_RETINAZ3" = "m" -o \
-	 "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_VIRGE" = "m" -o \
-	 "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_MAC" = "m" -o \
-	 "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
+    # Guess what we need
+    if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_AMIGA" = "y" -o \
+	 "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_CYBER" = "y" -o \
+	 "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_RETINAZ3" = "y" -o \
+	 "$CONFIG_FB_VIRGE" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" ]; then
       define_bool CONFIG_FBCON_MFB y
+    else
+      if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_AMIGA" = "m" -o \
+	   "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_CYBER" = "m" -o \
+	   "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_RETINAZ3" = "m" -o \
+	   "$CONFIG_FB_VIRGE" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
+	define_bool CONFIG_FBCON_MFB m
+      fi
     fi
-    if [ "$CONFIG_FB_AMIGA" = "y" -o "$CONFIG_FB_AMIGA" = "m" ]; then
-      define_bool CONFIG_FBCON_ILBM y
-      define_bool CONFIG_FBCON_AFB y
-    fi
-    if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" ]; then
-      define_bool CONFIG_FBCON_IPLAN2P2 y
-      define_bool CONFIG_FBCON_IPLAN2P4 y
-      define_bool CONFIG_FBCON_IPLAN2P8 y
-    fi
-    if [ "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_MAC" = "m" -o \
-	 "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
-      define_bool CONFIG_FBCON_MAC  y
+    if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_MAC" = "y" -o \
+	 "$CONFIG_FB_VIRTUAL" = "y" ]; then
       define_bool CONFIG_FBCON_CFB2 y
       define_bool CONFIG_FBCON_CFB4 y
-    fi
-    if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \
-	 "$CONFIG_FB_OF" = "y" -o "$CONFIG_FB_OF" = "m" -o \
-	 "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_MAC" = "m" -o  \
-	 "$CONFIG_FB_TGA" = "y" -o "$CONFIG_FB_TGA" = "m" -o \
-	 "$CONFIG_FB_VESA" = "y" -o "$CONFIG_FB_VESA" = "m" -o \
-	 "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
+    else
+      if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_MAC" = "m" -o \
+	   "$CONFIG_FB_VIRTUAL" = "m" ]; then
+	define_bool CONFIG_FBCON_CFB2 m
+	define_bool CONFIG_FBCON_CFB4 m
+      fi
+    fi
+    if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_ATARI" = "y" -o \
+	 "$CONFIG_FB_MAC" = "y" -o  "$CONFIG_FB_OF" = "y" -o \
+	 "$CONFIG_FB_TGA" = "y" -o "$CONFIG_FB_VESA" = "y" -o \
+	 "$CONFIG_FB_VIRTUAL" = "y" ]; then
       define_bool CONFIG_FBCON_CFB8 y
-    fi
-    if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \
-	 "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_ATY" = "m" -o \
-	 "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_MAC" = "m" -o \
-	 "$CONFIG_FB_VESA" = "y" -o "$CONFIG_FB_VESA" = "m" -o \
-	 "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
+    else
+      if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \
+	   "$CONFIG_FB_MAC" = "m" -o  "$CONFIG_FB_OF" = "m" -o \
+	   "$CONFIG_FB_TGA" = "m" -o "$CONFIG_FB_VESA" = "m" -o \
+	   "$CONFIG_FB_VIRTUAL" = "m" ]; then
+	define_bool CONFIG_FBCON_CFB8 m
+      fi
+    fi
+    if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATY" = "y" -o \
+	 "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_VESA" = "y" -o \
+	 "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_TBOX" = "y" ]; then
       define_bool CONFIG_FBCON_CFB16 y
+    else
+      if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \
+	   "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_VESA" = "m" -o \
+	   "$CONFIG_FB_VIRTUAL" = "m" -o "$CONFIG_FB_TBOX" = "m" ]; then
+	define_bool CONFIG_FBCON_CFB16 m
+      fi
     fi
-    if [ "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
+    if [ "$CONFIG_FB_VIRTUAL" = "y" ]; then
       define_bool CONFIG_FBCON_CFB24 y
+    else
+      if [ "$CONFIG_FB_VIRTUAL" = "m" ]; then
+	define_bool CONFIG_FBCON_CFB24 m
+      fi
     fi
-    if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATARI" = "m" -o \
-	 "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_ATY" = "m" -o \
-	 "$CONFIG_FB_VESA" = "y" -o "$CONFIG_FB_VESA" = "m" -o \
-	 "$CONFIG_FB_VIRTUAL" = "y" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
+    if [ "$CONFIG_FB_ATARI" = "y" -o "$CONFIG_FB_ATY" = "y" -o \
+	 "$CONFIG_FB_VESA" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" ]; then
       define_bool CONFIG_FBCON_CFB32 y
+    else
+      if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \
+	   "$CONFIG_FB_VESA" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
+	define_bool CONFIG_FBCON_CFB32 m
+      fi
     fi
-    if [ "$CONFIG_FB_ACORN" != "n" ]; then
-      define_bool CONFIG_FBCON_MFB y
-      define_bool CONFIG_FBCON_CFB2 y
-      define_bool CONFIG_FBCON_CFB4 y
-      define_bool CONFIG_FBCON_CFB8 y
+    if [ "$CONFIG_FB_AMIGA" = "y" ]; then
+      define_bool CONFIG_FBCON_AFB y
+      define_bool CONFIG_FBCON_ILBM y
+    else
+      if [ "$CONFIG_FB_AMIGA" = "m" ]; then
+	define_bool CONFIG_FBCON_AFB m
+	define_bool CONFIG_FBCON_ILBM m
+      fi
     fi
-    if [ "$CONFIG_FB_VGA" = "y" -o "$CONFIG_FB_VGA" = "m" -o \
-	 "$CONFIG_FB_MDA" = "y" -o "$CONFIG_FB_MDA" = "m" ]; then
+    if [ "$CONFIG_FB_ATARI" = "y" ]; then
+      define_bool CONFIG_FBCON_IPLAN2P2 y
+      define_bool CONFIG_FBCON_IPLAN2P4 y
+      define_bool CONFIG_FBCON_IPLAN2P8 y
+#     define_bool CONFIG_FBCON_IPLAN2P16 y
+    else
+      if [ "$CONFIG_FB_ATARI" = "m" ]; then
+	define_bool CONFIG_FBCON_IPLAN2P2 m
+	define_bool CONFIG_FBCON_IPLAN2P4 m
+	define_bool CONFIG_FBCON_IPLAN2P8 m
+#	define_bool CONFIG_FBCON_IPLAN2P16 m
+      fi
+    fi
+    if [ "$CONFIG_FB_MAC" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" ]; then
+      define_bool CONFIG_FBCON_MAC  y
+    else
+      if [ "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
+	define_bool CONFIG_FBCON_MAC  m
+      fi
+    fi
+    if [ "$CONFIG_FB_MDA" = "y" -o "$CONFIG_FB_VGA" = "y" ]; then
       define_bool CONFIG_FBCON_VGA y
+    else
+      if [ "$CONFIG_FB_MDA" = "m" -o "$CONFIG_FB_VGA" = "m" ]; then
+	define_bool CONFIG_FBCON_VGA m
+      fi
     fi
   fi
-
-  endmenu
 fi
diff -u --recursive --new-file v2.1.108/linux/drivers/video/Makefile linux/drivers/video/Makefile
--- v2.1.108/linux/drivers/video/Makefile	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/Makefile	Fri Jul 10 15:18:31 1998
@@ -21,9 +21,17 @@
 
 # Frame Buffer Console
 
+# Nasty trick to make sure all wanted stuff is linked in
+O_TARGET = fbdev.o
+L_OBJS += fbdev.o
+
+ifeq ($(CONFIG_DUMMY_CONSOLE),y)
+  L_OBJS += dummycon.o
+endif
+
 ifeq ($(CONFIG_FB),y)
-  L_OBJS += fonts.o font_8x8.o font_8x16.o pearl_8x8.o font_6x11.o
-  LX_OBJS += fbcon.o fbcmap.o fbgen.o
+  L_OBJS += fonts.o font_8x8.o font_8x16.o pearl_8x8.o font_6x11.o font_sun8x16.o
+  OX_OBJS += fbcon.o fbcmap.o fbgen.o
 endif
 
 # Frame Buffer Devices
@@ -90,10 +98,18 @@
 
 ifeq ($(CONFIG_FB_TGA),y)
 L_OBJS += tgafb.o
+else
+  ifeq ($(CONFIG_FB_TGA),m)
+  M_OBJS += tgafb.o
+  endif
 endif
 
 ifeq ($(CONFIG_FB_VGA),y)
 L_OBJS += vgafb.o
+else
+  ifeq ($(CONFIG_FB_VGA),m)
+  M_OBJS += vgafb.o
+  endif
 endif
 
 ifeq ($(CONFIG_FB_VESA),y)
@@ -116,6 +132,50 @@
   endif
 endif
 
+ifeq ($(CONFIG_FB_PROM),y)
+L_OBJS += promfb.o
+else
+  ifeq ($(CONFIG_FB_PROM),m)
+  M_OBJS += promfb.o
+  endif
+endif
+
+ifeq ($(CONFIG_FB_SBUS),y)
+L_OBJS += sbusfb.o
+  ifeq ($(CONFIG_FB_CREATOR),y)
+    L_OBJS += creatorfb.o
+  else
+    ifeq ($(CONFIG_FB_CREATOR),m)
+    M_OBJS += creatorfb.o
+    endif
+  endif
+  ifeq ($(CONFIG_FB_CGSIX),y)
+    L_OBJS += cgsixfb.o
+  else
+    ifeq ($(CONFIG_FB_CGSIX),m)
+    M_OBJS += cgsixfb.o
+    endif
+  endif
+else
+  ifeq ($(CONFIG_FB_SBUS),m)
+  M_OBJS += sbusfb.o
+    ifeq ($(CONFIG_FB_CREATOR),y)
+      M_OBJS += creatorfb.o
+    else
+      ifeq ($(CONFIG_FB_CREATOR),m)
+      M_OBJS += creatorfb.o
+      endif
+    endif
+    ifeq ($(CONFIG_FB_CGSIX),y)
+      M_OBJS += cgsixfb.o
+    else
+      ifeq ($(CONFIG_FB_CGSIX),m)
+      M_OBJS += cgsixfb.o
+      endif
+    endif
+  endif
+endif
+
 ifeq ($(CONFIG_FB_VIRTUAL),y)
 L_OBJS += vfb.o
 else
@@ -126,80 +186,142 @@
 
 # Generic Low Level Drivers
 
-ifdef CONFIG_FBCON_AFB
-LX_OBJS += fbcon-afb.o
+ifeq ($(CONFIG_FBCON_AFB),y)
+OX_OBJS += fbcon-afb.o
+else
+  ifeq ($(CONFIG_FBCON_AFB),m)
+  MX_OBJS += fbcon-afb.o
+  endif
+endif
+
+ifeq ($(CONFIG_FBCON_CFB2),y)
+OX_OBJS += fbcon-cfb2.o
+else
+  ifeq ($(CONFIG_FBCON_CFB2),m)
+  MX_OBJS += fbcon-cfb2.o
+  endif
 endif
 
-ifdef CONFIG_FBCON_CFB2
-LX_OBJS += fbcon-cfb2.o
+ifeq ($(CONFIG_FBCON_CFB4),y)
+OX_OBJS += fbcon-cfb4.o
+else
+  ifeq ($(CONFIG_FBCON_CFB4),m)
+  MX_OBJS += fbcon-cfb4.o
+  endif
 endif
 
-ifdef CONFIG_FBCON_CFB4
-LX_OBJS += fbcon-cfb4.o
+ifeq ($(CONFIG_FBCON_CFB8),y)
+OX_OBJS += fbcon-cfb8.o
+else
+  ifeq ($(CONFIG_FBCON_CFB8),m)
+  MX_OBJS += fbcon-cfb8.o
+  endif
 endif
 
-ifdef CONFIG_FBCON_CFB8
-LX_OBJS += fbcon-cfb8.o
+ifeq ($(CONFIG_FBCON_CFB16),y)
+OX_OBJS += fbcon-cfb16.o
+else
+  ifeq ($(CONFIG_FBCON_CFB16),m)
+  MX_OBJS += fbcon-cfb16.o
+  endif
 endif
 
-ifdef CONFIG_FBCON_CFB16
-LX_OBJS += fbcon-cfb16.o
+ifeq ($(CONFIG_FBCON_CFB24),y)
+OX_OBJS += fbcon-cfb24.o
+else
+  ifeq ($(CONFIG_FBCON_CFB24),m)
+  MX_OBJS += fbcon-cfb24.o
+  endif
 endif
 
-ifdef CONFIG_FBCON_CFB24
-LX_OBJS += fbcon-cfb24.o
+ifeq ($(CONFIG_FBCON_CFB32),y)
+OX_OBJS += fbcon-cfb32.o
+else
+  ifeq ($(CONFIG_FBCON_CFB32),m)
+  MX_OBJS += fbcon-cfb32.o
+  endif
 endif
 
-ifdef CONFIG_FBCON_CFB32
-LX_OBJS += fbcon-cfb32.o
+ifeq ($(CONFIG_FBCON_ILBM),y)
+OX_OBJS += fbcon-ilbm.o
+else
+  ifeq ($(CONFIG_FBCON_ILBM),m)
+  MX_OBJS += fbcon-ilbm.o
+  endif
 endif
 
-ifdef CONFIG_FBCON_ILBM
-LX_OBJS += fbcon-ilbm.o
+ifeq ($(CONFIG_FBCON_IPLAN2P2),y)
+OX_OBJS += fbcon-iplan2p2.o
+else
+  ifeq ($(CONFIG_FBCON_IPLAN2P2),m)
+  MX_OBJS += fbcon-iplan2p2.o
+  endif
 endif
 
-ifdef CONFIG_FBCON_IPLAN2P2
-LX_OBJS += fbcon-iplan2p2.o
+ifeq ($(CONFIG_FBCON_IPLAN2P4),y)
+OX_OBJS += fbcon-iplan2p4.o
+else
+  ifeq ($(CONFIG_FBCON_IPLAN2P4),m)
+  MX_OBJS += fbcon-iplan2p4.o
+  endif
 endif
 
-ifdef CONFIG_FBCON_IPLAN2P4
-LX_OBJS += fbcon-iplan2p4.o
+ifeq ($(CONFIG_FBCON_IPLAN2P8),y)
+OX_OBJS += fbcon-iplan2p8.o
+else
+  ifeq ($(CONFIG_FBCON_IPLAN2P8),m)
+  MX_OBJS += fbcon-iplan2p8.o
+  endif
 endif
 
-ifdef CONFIG_FBCON_IPLAN2P8
-LX_OBJS += fbcon-iplan2p8.o
+ifeq ($(CONFIG_FBCON_IPLAN2P16),y)
+OX_OBJS += fbcon-iplan2p16.o
+else
+  ifeq ($(CONFIG_FBCON_IPLAN2P16),m)
+  MX_OBJS += fbcon-iplan2p16.o
+  endif
 endif
 
-ifdef CONFIG_FBCON_MAC
-LX_OBJS += fbcon-mac.o
+ifeq ($(CONFIG_FBCON_MAC),y)
+OX_OBJS += fbcon-mac.o
+else
+  ifeq ($(CONFIG_FBCON_MAC),m)
+  MX_OBJS += fbcon-mac.o
+  endif
 endif
 
-ifdef CONFIG_FBCON_MFB
-LX_OBJS += fbcon-mfb.o
+ifeq ($(CONFIG_FBCON_MFB),y)
+OX_OBJS += fbcon-mfb.o
+else
+  ifeq ($(CONFIG_FBCON_MFB),m)
+  MX_OBJS += fbcon-mfb.o
+  endif
 endif
 
-ifdef CONFIG_FBCON_VGA
-LX_OBJS += fbcon-vga.o
+ifeq ($(CONFIG_FBCON_VGA),y)
+OX_OBJS += fbcon-vga.o
+else
+  ifeq ($(CONFIG_FBCON_VGA),m)
+  MX_OBJS += fbcon-vga.o
+  endif
 endif
 
 # GSP Console
 
 ifdef CONFIG_AMIGA_GSP
-L_OBJS := $(L_OBJS) gspcon.o gspcore.o
+L_OBJS += gspcon.o gspcore.o
 endif
 
 # VGA Text Console
 
 ifdef CONFIG_VGA_CONSOLE
-ifndef CONFIG_FB_VGA
-L_OBJS := $(L_OBJS) vgacon.o
-endif
+L_OBJS += vgacon.o
 endif
 
 # Console Wrapper
 
 ifdef CONFIG_ABSCON_COMPAT
-L_OBJS := $(L_OBJS) compatcon.o
+L_OBJS += compatcon.o
 endif
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.108/linux/drivers/video/S3triofb.c linux/drivers/video/S3triofb.c
--- v2.1.108/linux/drivers/video/S3triofb.c	Wed Jul  1 19:38:55 1998
+++ linux/drivers/video/S3triofb.c	Fri Jul 10 15:18:31 1998
@@ -77,8 +77,8 @@
      *  Interface used by the world
      */
 
-static int s3trio_open(struct fb_info *info);
-static int s3trio_release(struct fb_info *info);
+static int s3trio_open(struct fb_info *info, int user);
+static int s3trio_release(struct fb_info *info, int user);
 static int s3trio_get_fix(struct fb_fix_screeninfo *fix, int con,
 			  struct fb_info *info);
 static int s3trio_get_var(struct fb_var_screeninfo *var, int con,
@@ -107,7 +107,7 @@
      *  Interface to the low level console driver
      */
 
-unsigned long s3trio_fb_init(unsigned long mem_start);
+void s3triofb_init(void);
 static int s3triofbcon_switch(int con, struct fb_info *info);
 static int s3triofbcon_updatevar(int con, struct fb_info *info);
 static void s3triofbcon_blank(int blank, struct fb_info *info);
@@ -158,7 +158,7 @@
      *  Open/Release the frame buffer device
      */
 
-static int s3trio_open(struct fb_info *info)
+static int s3trio_open(struct fb_info *info, int user)
 {
     /*
      *  Nothing, only a usage count for the moment
@@ -168,7 +168,7 @@
     return(0);
 }
 
-static int s3trio_release(struct fb_info *info)
+static int s3trio_release(struct fb_info *info, int user)
 {
     MOD_DEC_USE_COUNT;
     return(0);
@@ -298,7 +298,7 @@
     return -EINVAL;
 }
 
-__initfunc(unsigned long s3triofb_init(unsigned long mem_start))
+__initfunc(void s3triofb_init(void))
 {
 #ifdef __powerpc__
     /* We don't want to be called like this. */
@@ -306,7 +306,6 @@
 #else /* !__powerpc__ */
     /* To be merged with cybervision */
 #endif /* !__powerpc__ */
-    return mem_start;
 }
 
 __initfunc(void s3trio_resetaccel(void)) {
@@ -415,7 +414,7 @@
 
 __initfunc(void s3triofb_init_of(struct device_node *dp))
 {
-    int i, err, *pp, len;
+    int i, *pp, len;
     unsigned long address;
     u_long *CursorBase;
 
@@ -622,8 +621,7 @@
     }
 #endif /* CONFIG_FB_COMPAT_XPMAC) */
 
-    err = register_framebuffer(&fb_info);
-    if (err < 0)
+    if (register_framebuffer(&fb_info) < 0)
 	return;
 
     printk("fb%d: S3 Trio frame buffer device on %s\n",
@@ -921,7 +919,7 @@
 }
 
 static void fbcon_trio8_putcs(struct vc_data *conp, struct display *p,
-			      const char *s, int count, int yy, int xx)
+			      const unsigned short *s, int count, int yy, int xx)
 {
     Trio_WaitBlit();
     fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/amifb.c linux/drivers/video/amifb.c
--- v2.1.108/linux/drivers/video/amifb.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/amifb.c	Fri Jul 10 15:18:31 1998
@@ -52,6 +52,7 @@
 #include <linux/interrupt.h>
 #include <linux/fb.h>
 #include <linux/init.h>
+#include <linux/console.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -1159,8 +1160,8 @@
 
 void amifb_setup(char *options, int *ints);
 
-static int amifb_open(struct fb_info *info);
-static int amifb_release(struct fb_info *info);
+static int amifb_open(struct fb_info *info, int user);
+static int amifb_release(struct fb_info *info, int user);
 static int amifb_get_fix(struct fb_fix_screeninfo *fix, int con,
 			 struct fb_info *info);
 static int amifb_get_var(struct fb_var_screeninfo *var, int con,
@@ -1188,7 +1189,7 @@
 	 * Interface to the low level console driver
 	 */
 
-unsigned long amifb_init(unsigned long mem_start);
+void amifb_init(void);
 static int amifbcon_switch(int con, struct fb_info *info);
 static int amifbcon_updatevar(int con, struct fb_info *info);
 static void amifbcon_blank(int blank, struct fb_info *info);
@@ -1376,7 +1377,7 @@
 	 * Open/Release the frame buffer device
 	 */
 
-static int amifb_open(struct fb_info *info)
+static int amifb_open(struct fb_info *info, int user)
 {
 	/*
 	 * Nothing, only a usage count for the moment
@@ -1386,7 +1387,7 @@
 	return(0);
 }
 
-static int amifb_release(struct fb_info *info)
+static int amifb_release(struct fb_info *info, int user)
 {
 	MOD_DEC_USE_COUNT;
 	return(0);
@@ -1712,13 +1713,13 @@
 	 * Initialisation
 	 */
 
-__initfunc(unsigned long amifb_init(unsigned long mem_start))
+__initfunc(void amifb_init(void))
 {
-	int err, tag, i;
+	int tag, i;
 	u_long chipptr;
 
 	if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_VIDEO))
-		return mem_start;
+		return;
 
 	/*
 	 * TODO: where should we put this? The DMI Resolver doesn't have a
@@ -1729,7 +1730,7 @@
 	if (amifb_resolver){
 		custom.dmacon = DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
 				DMAF_BLITTER | DMAF_SPRITE;
-		return mem_start;
+		return;
 	}
 #endif
 
@@ -1800,7 +1801,7 @@
 			strcat(amifb_name, "Unknown");
 			goto default_chipset;
 #else /* CONFIG_FB_AMIGA_OCS */
-			return mem_start;
+			return;
 #endif /* CONFIG_FB_AMIGA_OCS */
 			break;
 	}
@@ -1858,10 +1859,6 @@
 	fb_info.updatevar = &amifbcon_updatevar;
 	fb_info.blank = &amifbcon_blank;
 
-	err = register_framebuffer(&fb_info);
-	if (err < 0)
-		return mem_start;
-
 	chipptr = chipalloc(videomemorysize+
 	                    SPRITEMEMSIZE+
 	                    DUMMYSPRITEMEMSIZE+
@@ -1904,14 +1901,15 @@
 
 	amifb_set_var(&amifb_default, -1, &fb_info);
 
+	if (register_framebuffer(&fb_info) < 0)
+		return;
+
 	printk("fb%d: %s frame buffer device, using %ldK of video memory\n",
 	       GET_FB_IDX(fb_info.node), fb_info.modename,
 	       videomemorysize>>10);
 
 	/* TODO: This driver cannot be unloaded yet */
 	MOD_INC_USE_COUNT;
-
-	return mem_start;
 }
 
 static int amifbcon_switch(int con, struct fb_info *info)
@@ -3494,7 +3492,8 @@
 #ifdef MODULE
 int init_module(void)
 {
-	return(amifb_init(NULL));
+	amifb_init();
+	return 0;
 }
 
 void cleanup_module(void)
diff -u --recursive --new-file v2.1.108/linux/drivers/video/atafb.c linux/drivers/video/atafb.c
--- v2.1.108/linux/drivers/video/atafb.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/atafb.c	Fri Jul 10 15:18:31 1998
@@ -2409,7 +2409,7 @@
 	 * Open/Release the frame buffer device
 	 */
 
-static int atafb_open(struct fb_info *info)
+static int atafb_open(struct fb_info *info, int user)
 {
 	/*
 	 * Nothing, only a usage count for the moment
@@ -2419,7 +2419,7 @@
 	return(0);
 }
 
-static int atafb_release(struct fb_info *info)
+static int atafb_release(struct fb_info *info, int user)
 {
 	MOD_DEC_USE_COUNT;
 	return(0);
@@ -2727,15 +2727,14 @@
 		do_install_cmap(currcon, info);
 }
 
-__initfunc(unsigned long atafb_init(unsigned long mem_start))
+__initfunc(void atafb_init(void))
 {
-	int err;
 	int pad;
 	int detected_mode;
 	unsigned long mem_req;
 
 	if (!MACH_IS_ATARI)
-	        return mem_start;
+	        return;
 
 	do {
 #ifdef ATAFB_EXT
@@ -2790,8 +2789,8 @@
 		mem_req = default_mem_req + ovsc_offset +
 			ovsc_addlen;
 		mem_req = ((mem_req + PAGE_SIZE - 1) & PAGE_MASK) + PAGE_SIZE;
-		screen_base = (unsigned long)atari_stram_alloc(mem_req, &mem_start,
-													   "atafb");
+		screen_base = (unsigned long)atari_stram_alloc(mem_req, NULL,
+							       "atafb");
 		memset((char *) screen_base, 0, mem_req);
 		pad = ((screen_base + PAGE_SIZE-1) & PAGE_MASK) - screen_base;
 		screen_base+=pad;
@@ -2811,13 +2810,11 @@
 		/* Map the video memory (physical address given) to somewhere
 		 * in the kernel address space.
 		 */
-		mem_start = PAGE_ALIGN(mem_start);
 		external_addr = kernel_map(external_addr, external_len,
-					   KERNELMAP_NO_COPYBACK, &mem_start);
+					   KERNELMAP_NO_COPYBACK, NULL);
 		if (external_vgaiobase)
 			external_vgaiobase = kernel_map(external_vgaiobase,
-				0x10000, KERNELMAP_NOCACHE_SER, &mem_start);
-		mem_start += PAGE_SIZE;
+				0x10000, KERNELMAP_NOCACHE_SER, NULL);
 		screen_base      =
 		real_screen_base = external_addr;
 		screen_len       = external_len & PAGE_MASK;
@@ -2836,26 +2833,24 @@
 	do_fb_set_var(&atafb_predefined[default_par-1], 1);
 	strcat(fb_info.modename, fb_var_names[default_par-1][0]);
 
-	err=register_framebuffer(&fb_info);
-	if (err < 0)
-		return(err);
-
 	atafb_get_var(&disp.var, -1, &fb_info);
 	atafb_set_disp(-1, &fb_info);
+	do_install_cmap(0, &fb_info);
+
+	if (register_framebuffer(&fb_info) < 0)
+		return;
+
 	printk("Determined %dx%d, depth %d\n",
 	       disp.var.xres, disp.var.yres, disp.var.bits_per_pixel);
 	if ((disp.var.xres != disp.var.xres_virtual) ||
 	    (disp.var.yres != disp.var.yres_virtual))
 	   printk("   virtual %dx%d\n",
 			  disp.var.xres_virtual, disp.var.yres_virtual);
-	do_install_cmap(0, &fb_info);
 	printk("fb%d: %s frame buffer device, using %dK of video memory\n",
 	       GET_FB_IDX(fb_info.node), fb_info.modename, screen_len>>10);
 
 	/* TODO: This driver cannot be unloaded yet */
 	MOD_INC_USE_COUNT;
-
-	return mem_start;
 }
 
 /* a strtok which returns empty strings, too */
@@ -3136,7 +3131,8 @@
 #ifdef MODULE
 int init_module(void)
 {
-	return(atafb_init(NULL));
+	atafb_init();
+	return 0;
 }
 
 void cleanup_module(void)
diff -u --recursive --new-file v2.1.108/linux/drivers/video/atyfb.c linux/drivers/video/atyfb.c
--- v2.1.108/linux/drivers/video/atyfb.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/atyfb.c	Fri Jul 10 15:18:31 1998
@@ -365,14 +365,14 @@
      *  Interface used by the world
      */
 
-unsigned long atyfb_init(unsigned long mem_start);
+void atyfb_init(void);
 #ifdef CONFIG_FB_OF
 void atyfb_of_init(struct device_node *dp);
 #endif
 void atyfb_setup(char *options, int *ints);
 
-static int atyfb_open(struct fb_info *info);
-static int atyfb_release(struct fb_info *info);
+static int atyfb_open(struct fb_info *info, int user);
+static int atyfb_release(struct fb_info *info, int user);
 static int atyfb_get_fix(struct fb_fix_screeninfo *fix, int con,
 			 struct fb_info *info);
 static int atyfb_get_var(struct fb_var_screeninfo *var, int con,
@@ -1024,7 +1024,7 @@
      *  Open/Release the frame buffer device
      */
 
-static int atyfb_open(struct fb_info *info)
+static int atyfb_open(struct fb_info *info, int user)
 
 {
     /*
@@ -1035,7 +1035,7 @@
     return(0);
 }
 
-static int atyfb_release(struct fb_info *info)
+static int atyfb_release(struct fb_info *info, int user)
 {
     MOD_DEC_USE_COUNT;
     return(0);
@@ -1573,7 +1573,7 @@
 {
     u32 chip_id;
     u32 i;
-    int j, k, err, sense;
+    int j, k, sense;
     struct fb_var_screeninfo var;
     struct aty_regvals *init;
     const char *chipname = NULL;
@@ -1756,19 +1756,19 @@
 	info->palette[j].blue = default_blu[k];
     }
 
-    err = register_framebuffer(&info->fb_info);
-    if (err < 0)
-	return 0;
     atyfb_set_par(&info->default_par, info);
     encode_var(&var, &info->default_par, info);
     atyfb_set_var(&var, -1, &info->fb_info);
 
+    if (register_framebuffer(&info->fb_info) < 0)
+	return 0;
+
     printk("fb%d: %s frame buffer device on %s\n",
 	   GET_FB_IDX(info->fb_info.node), atyfb_name, name);
     return 1;
 }
 
-__initfunc(unsigned long atyfb_init(unsigned long mem_start))
+__initfunc(void atyfb_init(void))
 {
 #if defined(CONFIG_FB_OF)
     /* We don't want to be called like this. */
@@ -1787,9 +1787,7 @@
 	    continue;
 	}
 
-	info = (struct fb_info_aty *)mem_start;
-	mem_start += sizeof(struct fb_info_aty);
-	mem_start = PAGE_ALIGN(mem_start);
+	info = kmalloc(sizeof(struct fb_info_aty), GFP_ATOMIC);
 
 	/*
 	 *  Map the video memory (physical address given) to somewhere in the
@@ -1797,20 +1795,19 @@
 	 */
 	info->frame_buffer = kernel_map(phys_vmembase[m64_num],
 					phys_size[m64_num],
-					KERNELMAP_NOCACHE_SER, &mem_start);
+					KERNELMAP_NOCACHE_SER, NULL);
 	info->frame_buffer_phys = info->frame_buffer;
 	info->ati_regbase = kernel_map(phys_guiregbase[m64_num], 0x10000,
-				       KERNELMAP_NOCACHE_SER, &mem_start)
-			    +0xFC00ul;
+				       KERNELMAP_NOCACHE_SER, NULL)+0xFC00ul;
 	info->ati_regbase_phys = info->ati_regbase;
 
 	if (!aty_init(info, "ISA bus")) {
+	    kfree(info);
 	    /* This is insufficient! kernel_map has added two large chunks!! */
-	    return mem_start;
+	    return;
 	}
     }
 #endif
-    return mem_start;
 }
 
 #ifdef CONFIG_FB_OF
@@ -2232,7 +2229,7 @@
 }
 
 static void fbcon_aty8_putcs(struct vc_data *conp, struct display *p,
-			     const char *s, int count, int yy, int xx)
+			     const unsigned short *s, int count, int yy, int xx)
 {
     wait_for_idle((struct fb_info_aty *)p->fb_info);
     fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
@@ -2253,7 +2250,7 @@
 }
 
 static void fbcon_aty16_putcs(struct vc_data *conp, struct display *p,
-			      const char *s, int count, int yy, int xx)
+			      const unsigned short *s, int count, int yy, int xx)
 {
     wait_for_idle((struct fb_info_aty *)p->fb_info);
     fbcon_cfb16_putcs(conp, p, s, count, yy, xx);
@@ -2274,7 +2271,7 @@
 }
 
 static void fbcon_aty32_putcs(struct vc_data *conp, struct display *p,
-			      const char *s, int count, int yy, int xx)
+			      const unsigned short *s, int count, int yy, int xx)
 {
     wait_for_idle((struct fb_info_aty *)p->fb_info);
     fbcon_cfb32_putcs(conp, p, s, count, yy, xx);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/cgsixfb.c linux/drivers/video/cgsixfb.c
--- v2.1.108/linux/drivers/video/cgsixfb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/cgsixfb.c	Fri Jul 10 15:18:31 1998
@@ -0,0 +1,548 @@
+/* $Id: cgsixfb.c,v 1.1 1998/07/06 15:51:09 jj Exp $
+ * cgsixfb.c: CGsix (GX,GXplus) frame buffer driver
+ *
+ * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
+ * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/selection.h>
+
+#include "sbusfb.h"
+#include <asm/io.h>
+
+/* Offset of interesting structures in the OBIO space */
+/*
+ * Brooktree is the video dac and is funny to program on the cg6.
+ * (it's even funnier on the cg3)
+ * The FBC could be the frame buffer control
+ * The FHC could is the frame buffer hardware control.
+ */
+#define CG6_ROM_OFFSET       0x0
+#define CG6_BROOKTREE_OFFSET 0x200000
+#define CG6_DHC_OFFSET       0x240000
+#define CG6_ALT_OFFSET       0x280000
+#define CG6_FHC_OFFSET       0x300000
+#define CG6_THC_OFFSET       0x301000
+#define CG6_FBC_OFFSET       0x700000
+#define CG6_TEC_OFFSET       0x701000
+#define CG6_RAM_OFFSET       0x800000
+
+/* FHC definitions */
+#define CG6_FHC_FBID_SHIFT           24
+#define CG6_FHC_FBID_MASK            255
+#define CG6_FHC_REV_SHIFT            20
+#define CG6_FHC_REV_MASK             15
+#define CG6_FHC_FROP_DISABLE         (1 << 19)
+#define CG6_FHC_ROW_DISABLE          (1 << 18)
+#define CG6_FHC_SRC_DISABLE          (1 << 17)
+#define CG6_FHC_DST_DISABLE          (1 << 16)
+#define CG6_FHC_RESET                (1 << 15)
+#define CG6_FHC_LITTLE_ENDIAN        (1 << 13)
+#define CG6_FHC_RES_MASK             (3 << 11)
+#define CG6_FHC_1024                 (0 << 11)
+#define CG6_FHC_1152                 (1 << 11)
+#define CG6_FHC_1280                 (2 << 11)
+#define CG6_FHC_1600                 (3 << 11)
+#define CG6_FHC_CPU_MASK             (3 << 9)
+#define CG6_FHC_CPU_SPARC            (0 << 9)
+#define CG6_FHC_CPU_68020            (1 << 9)
+#define CG6_FHC_CPU_386              (2 << 9)
+#define CG6_FHC_TEST		     (1 << 8)
+#define CG6_FHC_TEST_X_SHIFT	     4
+#define CG6_FHC_TEST_X_MASK	     15
+#define CG6_FHC_TEST_Y_SHIFT	     0
+#define CG6_FHC_TEST_Y_MASK	     15
+
+/* FBC mode definitions */
+#define CG6_FBC_BLIT_IGNORE		0x00000000
+#define CG6_FBC_BLIT_NOSRC		0x00100000
+#define CG6_FBC_BLIT_SRC		0x00200000
+#define CG6_FBC_BLIT_ILLEGAL		0x00300000
+#define CG6_FBC_BLIT_MASK		0x00300000
+
+#define CG6_FBC_VBLANK			0x00080000
+
+#define CG6_FBC_MODE_IGNORE		0x00000000
+#define CG6_FBC_MODE_COLOR8		0x00020000
+#define CG6_FBC_MODE_COLOR1		0x00040000
+#define CG6_FBC_MODE_HRMONO		0x00060000
+#define CG6_FBC_MODE_MASK		0x00060000
+
+#define CG6_FBC_DRAW_IGNORE		0x00000000
+#define CG6_FBC_DRAW_RENDER		0x00008000
+#define CG6_FBC_DRAW_PICK		0x00010000
+#define CG6_FBC_DRAW_ILLEGAL		0x00018000
+#define CG6_FBC_DRAW_MASK		0x00018000
+
+#define CG6_FBC_BWRITE0_IGNORE		0x00000000
+#define CG6_FBC_BWRITE0_ENABLE		0x00002000
+#define CG6_FBC_BWRITE0_DISABLE		0x00004000
+#define CG6_FBC_BWRITE0_ILLEGAL		0x00006000
+#define CG6_FBC_BWRITE0_MASK		0x00006000
+
+#define CG6_FBC_BWRITE1_IGNORE		0x00000000
+#define CG6_FBC_BWRITE1_ENABLE		0x00000800
+#define CG6_FBC_BWRITE1_DISABLE		0x00001000
+#define CG6_FBC_BWRITE1_ILLEGAL		0x00001800
+#define CG6_FBC_BWRITE1_MASK		0x00001800
+
+#define CG6_FBC_BREAD_IGNORE		0x00000000
+#define CG6_FBC_BREAD_0			0x00000200
+#define CG6_FBC_BREAD_1			0x00000400
+#define CG6_FBC_BREAD_ILLEGAL		0x00000600
+#define CG6_FBC_BREAD_MASK		0x00000600
+
+#define CG6_FBC_BDISP_IGNORE		0x00000000
+#define CG6_FBC_BDISP_0			0x00000080
+#define CG6_FBC_BDISP_1			0x00000100
+#define CG6_FBC_BDISP_ILLEGAL		0x00000180
+#define CG6_FBC_BDISP_MASK		0x00000180
+
+#define CG6_FBC_INDEX_MOD		0x00000040
+#define CG6_FBC_INDEX_MASK		0x00000030
+
+/* THC definitions */
+#define CG6_THC_MISC_REV_SHIFT       16
+#define CG6_THC_MISC_REV_MASK        15
+#define CG6_THC_MISC_RESET           (1 << 12)
+#define CG6_THC_MISC_VIDEO           (1 << 10)
+#define CG6_THC_MISC_SYNC            (1 << 9)
+#define CG6_THC_MISC_VSYNC           (1 << 8)
+#define CG6_THC_MISC_SYNC_ENAB       (1 << 7)
+#define CG6_THC_MISC_CURS_RES        (1 << 6)
+#define CG6_THC_MISC_INT_ENAB        (1 << 5)
+#define CG6_THC_MISC_INT             (1 << 4)
+#define CG6_THC_MISC_INIT            0x9f
+
+/* The contents are unknown */
+struct cg6_tec {
+	volatile int tec_matrix;
+	volatile int tec_clip;
+	volatile int tec_vdc;
+};
+
+struct cg6_thc {
+        uint thc_pad0[512];
+	volatile uint thc_hs;		/* hsync timing */
+	volatile uint thc_hsdvs;
+	volatile uint thc_hd;
+	volatile uint thc_vs;		/* vsync timing */
+	volatile uint thc_vd;
+	volatile uint thc_refresh;
+	volatile uint thc_misc;
+	uint thc_pad1[56];
+	volatile uint thc_cursxy;	/* cursor x,y position (16 bits each) */
+	volatile uint thc_cursmask[32];	/* cursor mask bits */
+	volatile uint thc_cursbits[32];	/* what to show where mask enabled */
+};
+
+struct cg6_fbc {
+	u32		xxx0[1];
+	volatile u32	mode;
+	volatile u32	clip;
+	u32		xxx1[1];	    
+	volatile u32	s;
+	volatile u32	draw;
+	volatile u32	blit;
+	volatile u32	font;
+	u32		xxx2[24];
+	volatile u32	x0, y0, z0, color0;
+	volatile u32	x1, y1, z1, color1;
+	volatile u32	x2, y2, z2, color2;
+	volatile u32	x3, y3, z3, color3;
+	volatile u32	offx, offy;
+	u32		xxx3[2];
+	volatile u32	incx, incy;
+	u32		xxx4[2];
+	volatile u32	clipminx, clipminy;
+	u32		xxx5[2];
+	volatile u32	clipmaxx, clipmaxy;
+	u32		xxx6[2];
+	volatile u32	fg;
+	volatile u32	bg;
+	volatile u32	alu;
+	volatile u32	pm;
+	volatile u32	pixelm;
+	u32		xxx7[2];
+	volatile u32	patalign;
+	volatile u32	pattern[8];
+	u32		xxx8[432];
+	volatile u32	apointx, apointy, apointz;
+	u32		xxx9[1];
+	volatile u32	rpointx, rpointy, rpointz;
+	u32		xxx10[5];
+	volatile u32	pointr, pointg, pointb, pointa;
+	volatile u32	alinex, aliney, alinez;
+	u32		xxx11[1];
+	volatile u32	rlinex, rliney, rlinez;
+	u32		xxx12[5];
+	volatile u32	liner, lineg, lineb, linea;
+	volatile u32	atrix, atriy, atriz;
+	u32		xxx13[1];
+	volatile u32	rtrix, rtriy, rtriz;
+	u32		xxx14[5];
+	volatile u32	trir, trig, trib, tria;
+	volatile u32	aquadx, aquady, aquadz;
+	u32		xxx15[1];
+	volatile u32	rquadx, rquady, rquadz;
+	u32		xxx16[5];
+	volatile u32	quadr, quadg, quadb, quada;
+	volatile u32	arectx, arecty, arectz;
+	u32		xxx17[1];
+	volatile u32	rrectx, rrecty, rrectz;
+	u32		xxx18[5];
+	volatile u32	rectr, rectg, rectb, recta;
+};
+
+static struct sbus_mmap_map cg6_mmap_map[] = {
+	{ CG6_FBC,		CG6_FBC_OFFSET,		PAGE_SIZE },
+	{ CG6_TEC,		CG6_TEC_OFFSET,		PAGE_SIZE },
+	{ CG6_BTREGS,		CG6_BROOKTREE_OFFSET,	PAGE_SIZE },
+	{ CG6_FHC,		CG6_FHC_OFFSET,		PAGE_SIZE },
+	{ CG6_THC,		CG6_THC_OFFSET,		PAGE_SIZE },
+	{ CG6_ROM,		CG6_ROM_OFFSET,		0x10000   },
+	{ CG6_RAM,		CG6_RAM_OFFSET,		0x100000  }, /* FIXME: This should really be fbsize */
+	{ CG6_DHC,		CG6_DHC_OFFSET,		0x40000   },
+	{ 0,			0,			0	  }
+};
+
+static void cg6_setup(struct display *p)
+{
+	p->next_line = sbusfbinfo(p->fb_info)->var.xres_virtual;
+	p->next_plane = 0;
+}
+
+static void cg6_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			   int height, int width)
+{
+#if 0
+	int bytes = p->next_line, linesize = bytes * p->fontheight, rows;
+	u8 *src, *dst;
+
+	if (sx == 0 && dx == 0 && width * 32 == bytes)
+		mymemmove(p->screen_base + dy * linesize,
+				  p->screen_base + sy * linesize,
+				  height * linesize);
+	else if (dy < sy || (dy == sy && dx < sx)) {
+		src = p->screen_base + sy * linesize + sx * 32;
+		dst = p->screen_base + dy * linesize + dx * 32;
+		for (rows = height * p->fontheight ; rows-- ;) {
+			mymemmove(dst, src, width * 32);
+			src += bytes;
+			dst += bytes;
+		}
+	} else {
+		src = p->screen_base + (sy+height) * linesize + sx * 32 - bytes;
+		dst = p->screen_base + (dy+height) * linesize + dx * 32 - bytes;
+		for (rows = height * p->fontheight ; rows-- ;) {
+			mymemmove(dst, src, width * 32);
+			src -= bytes;
+			dst -= bytes;
+		}
+	}
+#endif	
+}
+
+static void cg6_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		      int height, int width)
+{
+#if 0
+	struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)p->fb_info;
+	register struct cg6_fbc *fbc = fb->s.cg6.fbc;
+	int x, y, w, h;
+	
+	fbc->ppc = 0x1803;
+	fbc->fg = cg6_cmap[attr_bg_col_ec(conp)];
+	fbc->fbc = 0x2000707f;
+	fbc->rop = 0x83;
+	fbc->pmask = 0xffffffff;
+	fbc->unk2 = 8;
+
+	/* FIXME: Optimize this by allowing 8/16 fontheigh only and introduce p->fontheightlog */
+	if (p->fontheight == 16) {
+		y = sy << 4; h = height << 4;
+	} else {
+		y = sy * p->fontheight; h = height * p->fontheight;
+	}
+	x = sx << 3; w = width << 3;
+	fbc->by = y + fb->y_margin;
+	fbc->bx = x + fb->x_margin;
+	fbc->bh = h;
+	fbc->bw = w;
+#endif
+}
+
+static void cg6_fill(struct fb_info_sbusfb *fb, int s,
+		     int count, unsigned short *boxes)
+{
+#if 0
+	register struct cg6_fbc *fbc = fb->s.cg6.fbc;
+
+	fbc->ppc = 0x1803;
+	fbc->fg = cg6_cmap[attr_bg_col(s)];
+	fbc->fbc = 0x2000707f;
+	fbc->rop = 0x83;
+	fbc->pmask = 0xffffffff;
+	fbc->unk2 = 8;
+	while (count-- > 0) {
+		fbc->by = boxes[1];
+		fbc->bx = boxes[0];
+		fbc->bh = boxes[3] - boxes[1];
+		fbc->bw = boxes[2] - boxes[0];
+		boxes += 4;
+	}
+#endif
+}
+
+static void cg6_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
+{
+#if 0
+	struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)p->fb_info;
+	register struct cg6_fbc *fbc = fb->s.cg6.fbc;
+	int i, xy;
+	u8 *fd;
+
+	if (p->fontheight == 16) {
+		xy = (yy << (16 + 4));
+		fd = p->fontdata + ((c & 0xff) << 4);
+	} else {
+		xy = ((yy * p->fontheight) << 16);
+		fd = p->fontdata + (c & 0xff) * p->fontheight;
+	}
+	xy += (xx << 3) + fb->s.cg6.xy_margin;
+	fbc->ppc = 0x203;
+	fbc->fg = cg6_cmap[attr_fg_col(c)];
+	fbc->fbc = 0x2000707f;
+	fbc->rop = 0x83;
+	fbc->pmask = 0xffffffff;
+	fbc->bg = cg6_cmap[attr_bg_col(c)];
+	fbc->fontw = 8;
+	fbc->fontinc = 0x10000;
+	fbc->fontxy = xy;
+	for (i = 0; i < p->fontheight; i++)
+		fbc->font = *fd++ << 24;
+#endif		
+}
+
+static void cg6_putcs(struct vc_data *conp, struct display *p, const unsigned short *s,
+		      int count, int yy, int xx)
+{
+#if 0
+	struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)p->fb_info;
+	register struct cg6_fbc *fbc = fb->s.cg6.fbc;
+	int i, xy;
+	u8 *fd1, *fd2, *fd3, *fd4;
+
+	fbc->ppc = 0x203;
+	fbc->fg = cg6_cmap[attr_fg_col(*s)];
+	fbc->fbc = 0x2000707f;
+	fbc->rop = 0x83;
+	fbc->pmask = 0xffffffff;
+	fbc->bg = cg6_cmap[attr_bg_col(*s)];
+	if (p->fontheight == 16) {
+		xy = (yy << (16 + 4)) + (xx << 3) + fb->s.cg6.xy_margin;
+		while (count >= 4) {
+			count -= 4;
+			fbc->fontw = 32;
+			fbc->fontinc = 0x10000;
+			fbc->fontxy = xy;
+			fd1 = p->fontdata + ((*s++ & 0xff) << 4);
+			fd2 = p->fontdata + ((*s++ & 0xff) << 4);
+			fd3 = p->fontdata + ((*s++ & 0xff) << 4);
+			fd4 = p->fontdata + ((*s++ & 0xff) << 4);
+			for (i = 0; i < 16; i++)
+				fbc->font = ((u32)*fd4++) | ((((u32)*fd3++) | ((((u32)*fd2++) | (((u32)*fd1++) << 8)) << 8)) << 8);
+			xy += 32;
+		}
+		while (count) {
+			count--;
+			fbc->fontw = 8;
+			fbc->fontinc = 0x10000;
+			fbc->fontxy = xy;
+			fd1 = p->fontdata + ((*s++ & 0xff) << 4);
+			for (i = 0; i < 16; i++)
+				fbc->font = *fd1++ << 24;
+			xy += 8;
+		}
+	} else {
+		xy = ((yy * p->fontheight) << 16) + (xx << 3) + fb->s.cg6.xy_margin;
+		while (count >= 4) {
+			count -= 4;
+			fbc->fontw = 32;
+			fbc->fontinc = 0x10000;
+			fbc->fontxy = xy;
+			fd1 = p->fontdata + ((*s++ & 0xff) * p->fontheight);
+			fd2 = p->fontdata + ((*s++ & 0xff) * p->fontheight);
+			fd3 = p->fontdata + ((*s++ & 0xff) * p->fontheight);
+			fd4 = p->fontdata + ((*s++ & 0xff) * p->fontheight);
+			for (i = 0; i < p->fontheight; i++)
+				fbc->font = ((u32)*fd4++) | ((((u32)*fd3++) | ((((u32)*fd2++) | (((u32)*fd1++) << 8)) << 8)) << 8);
+			xy += 32;
+		}
+		while (count) {
+			count--;
+			fbc->fontw = 8;
+			fbc->fontinc = 0x10000;
+			fbc->fontxy = xy;
+			fd1 = p->fontdata + ((*s++ & 0xff) * p->fontheight);
+			for (i = 0; i < 16; i++)
+				fbc->font = *fd1++ << 24;
+			xy += 8;
+		}
+	}
+#endif
+}
+
+static void cg6_revc(struct display *p, int xx, int yy)
+{
+	u8 *dest;
+	int bytes=p->next_line, rows;
+        
+	dest = p->screen_base + yy * p->fontheight * bytes + xx * 8;
+	for (rows = p->fontheight ; rows-- ; dest += bytes) {
+		((u32 *)dest)[0] ^= 0x0f0f0f0f;
+		((u32 *)dest)[1] ^= 0x0f0f0f0f;
+	}
+}
+
+static void cg6_loadcmap (struct fb_info_sbusfb *fb, int index, int count)
+{
+	struct bt_regs *bt = fb->s.cg6.bt;
+	int i;
+                
+	bt->addr = index << 24;
+	for (i = index; count--; i++){
+		bt->color_map = fb->color_map CM(i,0) << 24;
+		bt->color_map = fb->color_map CM(i,1) << 24;
+		bt->color_map = fb->color_map CM(i,2) << 24;
+	}
+}
+
+static struct display_switch cg6_dispsw __initdata = {
+	cg6_setup, cg6_bmove, cg6_clear, cg6_putc, cg6_putcs, cg6_revc, NULL
+};
+
+static void cg6_setcursormap (struct fb_info_sbusfb *fb, u8 *red, u8 *green, u8 *blue)
+{
+        struct bt_regs *bt = fb->s.cg6.bt;
+        
+	bt->addr = 1 << 24;
+	bt->cursor = red[0] << 24;
+	bt->cursor = green[0] << 24;
+	bt->cursor = blue[0] << 24;
+	bt->addr = 3 << 24;
+	bt->cursor = red[1] << 24;
+	bt->cursor = green[1] << 24;
+	bt->cursor = blue[1] << 24;
+}
+
+/* Set cursor shape */
+static void cg6_setcurshape (struct fb_info_sbusfb *fb)
+{
+	struct cg6_thc *thc = fb->s.cg6.thc;
+	int i;
+
+	for (i = 0; i < 32; i++){
+		thc->thc_cursmask [i] = fb->cursor.bits[0][i];
+		thc->thc_cursbits [i] = fb->cursor.bits[1][i];
+	}
+}
+
+/* Load cursor information */
+static void cg6_setcursor (struct fb_info_sbusfb *fb)
+{
+	unsigned int v;
+	struct cg_cursor *c = &fb->cursor;
+
+	if (c->enable)
+		v = ((c->cpos.fbx - c->chot.fbx) << 16)
+		    |((c->cpos.fby - c->chot.fby) & 0xffff);
+	else
+		/* Magic constant to turn off the cursor */
+		v = ((65536-32) << 16) | (65536-32);
+	fb->s.cg6.thc->thc_cursxy = v;
+}
+
+static char idstring[60] __initdata = { 0 };
+
+__initfunc(char *cgsixfb_init(struct fb_info_sbusfb *fb))
+{
+	struct fb_fix_screeninfo *fix = &fb->fix;
+	struct fb_var_screeninfo *var = &fb->var;
+	struct display *disp = &fb->disp;
+	struct fbtype *type = &fb->type;
+	unsigned long phys = fb->sbdp->reg_addrs[0].phys_addr;
+	u32 conf;
+	char *p;
+
+	strcpy(fb->info.modename, "CGsix");
+		
+	strcpy(fix->id, "CGsix");
+	fix->smem_start = (char *)phys + CG6_RAM_OFFSET;
+	fix->line_length = fb->var.xres_virtual;
+	fix->mmio_start = (char *)phys + CG6_FBC_OFFSET;
+	fix->mmio_len = PAGE_SIZE;
+	
+	var->accel_flags = FB_ACCELF_TEXT;
+	
+	disp->scrollmode = SCROLL_YREDRAW;
+	if (!disp->screen_base)
+		disp->screen_base = (char *)sparc_alloc_io(phys + CG6_RAM_OFFSET, 0, 
+			type->fb_size, "cgsix_ram", fb->iospace, 0);
+	disp->screen_base += fix->line_length * fb->y_margin + fb->x_margin;
+	fb->s.cg6.fbc = (struct cg6_fbc *)(char *)sparc_alloc_io(phys + CG6_FBC_OFFSET, 0, 
+			4096, "cgsix_fbc", fb->iospace, 0);
+	fb->s.cg6.thc = (struct cg6_thc *)sparc_alloc_io(phys + CG6_THC_OFFSET, 0, 
+				sizeof(struct cg6_thc), "cgsix_thc", fb->iospace, 0);
+	fb->s.cg6.bt = (struct bt_regs *)sparc_alloc_io(phys + CG6_BROOKTREE_OFFSET, 0, 
+				sizeof(struct bt_regs), "cgsix_dac", fb->iospace, 0);
+	fb->s.cg6.fhc = (u32 *)sparc_alloc_io(phys + CG6_FHC_OFFSET, 0, 
+				sizeof(u32), "cgsix_fhc", fb->iospace, 0);
+	fb->dispsw = cg6_dispsw;
+
+	fb->loadcmap = cg6_loadcmap;
+	fb->setcursor = cg6_setcursor;
+	fb->setcursormap = cg6_setcursormap;
+	fb->setcurshape = cg6_setcurshape;
+	fb->fill = cg6_fill;
+	
+	fb->physbase = phys;
+	fb->mmap_map = cg6_mmap_map;
+	
+	/* Initialize Brooktree DAC */
+	fb->s.cg6.bt->addr = 0x04 << 24;         /* color planes */
+	fb->s.cg6.bt->control = 0xff << 24;
+	fb->s.cg6.bt->addr = 0x05 << 24;
+	fb->s.cg6.bt->control = 0x00 << 24;
+	fb->s.cg6.bt->addr = 0x06 << 24;         /* overlay plane */
+	fb->s.cg6.bt->control = 0x73 << 24;
+	fb->s.cg6.bt->addr = 0x07 << 24;
+	fb->s.cg6.bt->control = 0x00 << 24;
+	
+	conf = *fb->s.cg6.fhc;
+	switch(conf & CG6_FHC_CPU_MASK) {
+	case CG6_FHC_CPU_SPARC: p = "sparc"; break;
+	case CG6_FHC_CPU_68020: p = "68020"; break;
+	default: p = "i386"; break;
+	}
+	                                                                        
+	sprintf(idstring, "cgsix at %02x.%08lx TEC Rev %x CPU %s Rev %x", fb->iospace, phys, 
+		    (fb->s.cg6.thc->thc_misc >> CG6_THC_MISC_REV_SHIFT) & CG6_THC_MISC_REV_MASK,
+		    p, conf >> CG6_FHC_REV_SHIFT & CG6_FHC_REV_MASK);
+		    
+	return idstring;
+}
diff -u --recursive --new-file v2.1.108/linux/drivers/video/compatcon.c linux/drivers/video/compatcon.c
--- v2.1.108/linux/drivers/video/compatcon.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/compatcon.c	Fri Jul 10 15:18:31 1998
@@ -43,11 +43,11 @@
 static void compatcon_clear(struct vc_data *conp, int sy, int sx, int height,
 			    int width);
 static void compatcon_putc(struct vc_data *conp, int c, int ypos, int xpos);
-static void compatcon_putcs(struct vc_data *conp, const char *s, int count,
+static void compatcon_putcs(struct vc_data *conp, const unsigned short *s, int count,
 			    int ypos, int xpos);
 static void compatcon_cursor(struct vc_data *conp, int mode);
-static void compatcon_scroll(struct vc_data *conp, int t, int b, int dir,
-			     int count);
+static int compatcon_scroll(struct vc_data *conp, int t, int b, int dir,
+			    int count);
 static void compatcon_bmove(struct vc_data *conp, int sy, int sx, int dy,
 			    int dx, int height, int width);
 static int compatcon_switch(struct vc_data *conp);
@@ -62,10 +62,6 @@
      *  Internal routines
      */
 
-#if 0
-static int compatcon_show_logo(void);
-#endif
-
 unsigned long video_num_columns;
 unsigned long video_num_lines;
 unsigned long video_size_row;
@@ -100,10 +96,6 @@
     video_screen_size = video_num_lines*video_size_row;
 
     con_type_init(&display_desc);
-#if 0
-    if (!console_show_logo)
-	console_show_logo = compatcon_show_logo;
-#endif
     return display_desc;
 }
 
@@ -152,11 +144,11 @@
 	return;
 
     p = (u16 *)(video_mem_base+ypos*video_size_row+xpos*2);
-    scr_writew(conp->vc_attr << 8 | c, p);
+    scr_writew(c, p);
 }
 
 
-static void compatcon_putcs(struct vc_data *conp, const char *s, int count,
+static void compatcon_putcs(struct vc_data *conp, const unsigned short *s, int count,
 			    int ypos, int xpos)
 {
     u16 *p;
@@ -166,9 +158,8 @@
 	return;
 
     p = (u16 *)(video_mem_base+ypos*video_size_row+xpos*2);
-    sattr = conp->vc_attr << 8;
     while (count--)
-	scr_writew(sattr | ((int) (*s++) & 0xff), p++);
+	scr_writew(*s++, p++);
 }
 
 
@@ -187,11 +178,11 @@
 }
 
 
-static void compatcon_scroll(struct vc_data *conp, int t, int b, int dir,
-			     int count)
+static int compatcon_scroll(struct vc_data *conp, int t, int b, int dir,
+			    int count)
 {
     if (console_blanked)
-	return;
+	return 0;
 
     compatcon_cursor(conp, CM_ERASE);
 
@@ -226,6 +217,7 @@
 	    compatcon_clear(conp, 0, t, conp->vc_rows, count);
 	    break;
     }
+    return 0;
 }
 
 
@@ -279,7 +271,7 @@
 
 static int compatcon_switch(struct vc_data *conp)
 {
-    return 0;
+    return 1;
 }
 
 
@@ -327,21 +319,6 @@
     return -ENOSYS;
 }
 
-#if 0
-__initfunc(static int compatcon_show_logo( void ))
-{
-    int height = 0;
-    char *p;
-
-    printk(linux_serial_image);
-    for (p = linux_serial_image; *p; p++)
-	if (*p == '\n')
-	    height++;
-    return height;
-}
-#endif
-
-
     /*
      *  The console `switch' structure for the console wrapper
      */
@@ -350,5 +327,6 @@
     compatcon_startup, compatcon_init, compatcon_deinit, compatcon_clear,
     compatcon_putc, compatcon_putcs, compatcon_cursor, compatcon_scroll,
     compatcon_bmove, compatcon_switch, compatcon_blank, compatcon_get_font,
-    compatcon_set_font, compatcon_set_palette, compatcon_scrolldelta
+    compatcon_set_font, compatcon_set_palette, compatcon_scrolldelta,
+    NULL, NULL
 };
diff -u --recursive --new-file v2.1.108/linux/drivers/video/creatorfb.c linux/drivers/video/creatorfb.c
--- v2.1.108/linux/drivers/video/creatorfb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/creatorfb.c	Fri Jul 10 15:18:31 1998
@@ -0,0 +1,503 @@
+/* $Id: creatorfb.c,v 1.4 1998/07/06 15:51:10 jj Exp $
+ * creatorfb.c: Creator/Creator3D frame buffer driver
+ *
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@ultra.linux.cz)
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/selection.h>
+
+#include "sbusfb.h"
+
+#define	FFB_SFB8R_VOFF		0x00000000
+#define	FFB_SFB8G_VOFF		0x00400000
+#define	FFB_SFB8B_VOFF		0x00800000
+#define	FFB_SFB8X_VOFF		0x00c00000
+#define	FFB_SFB32_VOFF		0x01000000
+#define	FFB_SFB64_VOFF		0x02000000
+#define	FFB_FBC_REGS_VOFF	0x04000000
+#define	FFB_BM_FBC_REGS_VOFF	0x04002000
+#define	FFB_DFB8R_VOFF		0x04004000
+#define	FFB_DFB8G_VOFF		0x04404000
+#define	FFB_DFB8B_VOFF		0x04804000
+#define	FFB_DFB8X_VOFF		0x04c04000
+#define	FFB_DFB24_VOFF		0x05004000
+#define	FFB_DFB32_VOFF		0x06004000
+#define	FFB_DFB422A_VOFF	0x07004000	/* DFB 422 mode write to A */
+#define	FFB_DFB422AD_VOFF	0x07804000	/* DFB 422 mode with line doubling */
+#define	FFB_DFB24B_VOFF		0x08004000	/* DFB 24bit mode write to B */
+#define	FFB_DFB422B_VOFF	0x09004000	/* DFB 422 mode write to B */
+#define	FFB_DFB422BD_VOFF	0x09804000	/* DFB 422 mode with line doubling */
+#define	FFB_SFB16Z_VOFF		0x0a004000	/* 16bit mode Z planes */
+#define	FFB_SFB8Z_VOFF		0x0a404000	/* 8bit mode Z planes */
+#define	FFB_SFB422_VOFF		0x0ac04000	/* SFB 422 mode write to A/B */
+#define	FFB_SFB422D_VOFF	0x0b404000	/* SFB 422 mode with line doubling */
+#define	FFB_FBC_KREGS_VOFF	0x0bc04000
+#define	FFB_DAC_VOFF		0x0bc06000
+#define	FFB_PROM_VOFF		0x0bc08000
+#define	FFB_EXP_VOFF		0x0bc18000
+
+#define	FFB_SFB8R_POFF		0x04000000
+#define	FFB_SFB8G_POFF		0x04400000
+#define	FFB_SFB8B_POFF		0x04800000
+#define	FFB_SFB8X_POFF		0x04c00000
+#define	FFB_SFB32_POFF		0x05000000
+#define	FFB_SFB64_POFF		0x06000000
+#define	FFB_FBC_REGS_POFF	0x00600000
+#define	FFB_BM_FBC_REGS_POFF	0x00600000
+#define	FFB_DFB8R_POFF		0x01000000
+#define	FFB_DFB8G_POFF		0x01400000
+#define	FFB_DFB8B_POFF		0x01800000
+#define	FFB_DFB8X_POFF		0x01c00000
+#define	FFB_DFB24_POFF		0x02000000
+#define	FFB_DFB32_POFF		0x03000000
+#define	FFB_FBC_KREGS_POFF	0x00610000
+#define	FFB_DAC_POFF		0x00400000
+#define	FFB_PROM_POFF		0x00000000
+#define	FFB_EXP_POFF		0x00200000
+
+#define FFB_Y_BYTE_ADDR_SHIFT          11
+#define FFB_Y_ADDR_SHIFT               13
+
+#define FFB_PPC_ACE_DISABLE             1
+#define FFB_PPC_ACE_AUX_ADD             3
+#define FFB_PPC_ACE_SHIFT              18
+#define FFB_PPC_DCE_DISABLE             2
+#define FFB_PPC_DCE_SHIFT              16
+#define FFB_PPC_ABE_DISABLE             2
+#define FFB_PPC_ABE_SHIFT              14
+#define FFB_PPC_VCE_DISABLE             1
+#define FFB_PPC_VCE_2D                  2
+#define FFB_PPC_VCE_SHIFT              12
+#define FFB_PPC_APE_DISABLE             2
+#define FFB_PPC_APE_SHIFT              10
+#define FFB_PPC_CS_VARIABLE             2
+#define FFB_PPC_CS_SHIFT                0
+
+#define FFB_FBC_WB_A                    1
+#define FFB_FBC_WB_SHIFT               29
+#define FFB_FBC_PGE_MASK                3
+#define FFB_FBC_BE_SHIFT                4
+#define FFB_FBC_GE_SHIFT                2
+#define FFB_FBC_RE_SHIFT                0
+
+#define FFB_ROP_NEW                  0x83
+#define FFB_ROP_RGB_SHIFT               0
+
+#define FFB_UCSR_FIFO_MASK     0x00000fff
+#define FFB_UCSR_RP_BUSY       0x02000000
+
+struct ffb_fbc {
+	u8		xxx1[0x60];
+	volatile u32	by;
+	volatile u32	bx;
+	u32		xxx2;
+	u32		xxx3;
+	volatile u32	bh;
+	volatile u32	bw;
+	u8		xxx4[0x188];
+	volatile u32	ppc;
+	u32		xxx5;
+	volatile u32	fg;
+	volatile u32	bg;
+	u8		xxx6[0x44];
+	volatile u32	fbc;
+	volatile u32	rop;
+	u8		xxx7[0x34];
+	volatile u32	pmask;
+	u8		xxx8[12];
+	volatile u32	clip0min;
+	volatile u32	clip0max;
+	volatile u32	clip1min;
+	volatile u32	clip1max;
+	volatile u32	clip2min;
+	volatile u32	clip2max;
+	volatile u32	clip3min;
+	volatile u32	clip3max;
+	u8		xxx9[0x3c];
+	volatile u32	unk1;
+	volatile u32	unk2;
+	u8		xxx10[0x10];
+	volatile u32	fontxy;
+	volatile u32	fontw;
+	volatile u32	fontinc;
+	volatile u32	font;
+	u8		xxx11[0x4dc];
+	volatile u32	unk3;
+	u8		xxx12[0xfc];
+	volatile u32	ucsr;
+};
+
+struct ffb_dac {
+	volatile u32	type;
+	volatile u32	value;
+	volatile u32	type2;
+	volatile u32	value2;
+};
+
+static struct sbus_mmap_map ffb_mmap_map[] = {
+	{ FFB_SFB8R_VOFF,	FFB_SFB8R_POFF,		0x0400000 },
+	{ FFB_SFB8G_VOFF,	FFB_SFB8G_POFF,		0x0400000 },
+	{ FFB_SFB8B_VOFF,	FFB_SFB8B_POFF,		0x0400000 },
+	{ FFB_SFB8X_VOFF,	FFB_SFB8X_POFF,		0x0400000 },
+	{ FFB_SFB32_VOFF,	FFB_SFB32_POFF,		0x1000000 },
+	{ FFB_SFB64_VOFF,	FFB_SFB64_POFF,		0x2000000 },
+	{ FFB_FBC_REGS_VOFF,	FFB_FBC_REGS_POFF,	0x0002000 },
+	{ FFB_BM_FBC_REGS_VOFF,	FFB_BM_FBC_REGS_POFF,	0x0002000 },
+	{ FFB_DFB8R_VOFF,	FFB_DFB8R_POFF,		0x0400000 },
+	{ FFB_DFB8G_VOFF,	FFB_DFB8G_POFF,		0x0400000 },
+	{ FFB_DFB8B_VOFF,	FFB_DFB8B_POFF,		0x0400000 },
+	{ FFB_DFB8X_VOFF,	FFB_DFB8X_POFF,		0x0400000 },
+	{ FFB_DFB24_VOFF,	FFB_DFB24_POFF,		0x1000000 },
+	{ FFB_DFB32_VOFF,	FFB_DFB32_POFF,		0x1000000 },
+	{ FFB_FBC_KREGS_VOFF,	FFB_FBC_KREGS_POFF,	0x0002000 },
+	{ FFB_DAC_VOFF,		FFB_DAC_POFF,		0x0002000 },
+	{ FFB_PROM_VOFF,	FFB_PROM_POFF,		0x0010000 },
+	{ FFB_EXP_VOFF,		FFB_EXP_POFF,		0x0002000 },
+	{ 0,			0,			0	  }
+};
+
+static u32 ffb_cmap[16];
+
+static void ffb_setup(struct display *p)
+{
+	p->next_line = 8192;
+	p->next_plane = 0;
+}
+
+static void ffb_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			   int height, int width)
+{
+	int bytes = p->next_line, linesize = bytes * p->fontheight, rows;
+	u8 *src, *dst;
+
+	if (sx == 0 && dx == 0 && width * 32 == bytes)
+		mymemmove(p->screen_base + dy * linesize,
+				  p->screen_base + sy * linesize,
+				  height * linesize);
+	else if (dy < sy || (dy == sy && dx < sx)) {
+		src = p->screen_base + sy * linesize + sx * 32;
+		dst = p->screen_base + dy * linesize + dx * 32;
+		for (rows = height * p->fontheight ; rows-- ;) {
+			mymemmove(dst, src, width * 32);
+			src += bytes;
+			dst += bytes;
+		}
+	} else {
+		src = p->screen_base + (sy+height) * linesize + sx * 32 - bytes;
+		dst = p->screen_base + (dy+height) * linesize + dx * 32 - bytes;
+		for (rows = height * p->fontheight ; rows-- ;) {
+			mymemmove(dst, src, width * 32);
+			src -= bytes;
+			dst -= bytes;
+		}
+	}
+}
+
+static void ffb_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		      int height, int width)
+{
+	struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)p->fb_info;
+	register struct ffb_fbc *fbc = fb->s.ffb.fbc;
+	int x, y, w, h;
+	
+	fbc->ppc = 0x1803;
+	fbc->fg = ffb_cmap[attr_bg_col_ec(conp)];
+	fbc->fbc = 0x2000707f;
+	fbc->rop = 0x83;
+	fbc->pmask = 0xffffffff;
+	fbc->unk2 = 8;
+
+	/* FIXME: Optimize this by allowing 8/16 fontheigh only and introduce p->fontheightlog */
+	if (p->fontheight == 16) {
+		y = sy << 4; h = height << 4;
+	} else {
+		y = sy * p->fontheight; h = height * p->fontheight;
+	}
+	x = sx << 3; w = width << 3;
+	fbc->by = y + fb->y_margin;
+	fbc->bx = x + fb->x_margin;
+	fbc->bh = h;
+	fbc->bw = w;
+}
+
+static void ffb_fill(struct fb_info_sbusfb *fb, int s,
+		     int count, unsigned short *boxes)
+{
+	register struct ffb_fbc *fbc = fb->s.ffb.fbc;
+
+	fbc->ppc = 0x1803;
+	fbc->fg = ffb_cmap[attr_bg_col(s)];
+	fbc->fbc = 0x2000707f;
+	fbc->rop = 0x83;
+	fbc->pmask = 0xffffffff;
+	fbc->unk2 = 8;
+	while (count-- > 0) {
+		fbc->by = boxes[1];
+		fbc->bx = boxes[0];
+		fbc->bh = boxes[3] - boxes[1];
+		fbc->bw = boxes[2] - boxes[0];
+		boxes += 4;
+	}
+}
+
+static void ffb_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
+{
+	struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)p->fb_info;
+	register struct ffb_fbc *fbc = fb->s.ffb.fbc;
+	int i, xy;
+	u8 *fd;
+
+	if (p->fontheight == 16) {
+		xy = (yy << (16 + 4));
+		fd = p->fontdata + ((c & 0xff) << 4);
+	} else {
+		xy = ((yy * p->fontheight) << 16);
+		fd = p->fontdata + (c & 0xff) * p->fontheight;
+	}
+	xy += (xx << 3) + fb->s.ffb.xy_margin;
+	fbc->ppc = 0x203;
+	fbc->fg = ffb_cmap[attr_fg_col(c)];
+	fbc->fbc = 0x2000707f;
+	fbc->rop = 0x83;
+	fbc->pmask = 0xffffffff;
+	fbc->bg = ffb_cmap[attr_bg_col(c)];
+	fbc->fontw = 8;
+	fbc->fontinc = 0x10000;
+	fbc->fontxy = xy;
+	for (i = 0; i < p->fontheight; i++)
+		fbc->font = *fd++ << 24;
+}
+
+static void ffb_putcs(struct vc_data *conp, struct display *p, const unsigned short *s,
+		      int count, int yy, int xx)
+{
+	struct fb_info_sbusfb *fb = (struct fb_info_sbusfb *)p->fb_info;
+	register struct ffb_fbc *fbc = fb->s.ffb.fbc;
+	int i, xy;
+	u8 *fd1, *fd2, *fd3, *fd4;
+
+	fbc->ppc = 0x203;
+	fbc->fg = ffb_cmap[attr_fg_col(*s)];
+	fbc->fbc = 0x2000707f;
+	fbc->rop = 0x83;
+	fbc->pmask = 0xffffffff;
+	fbc->bg = ffb_cmap[attr_bg_col(*s)];
+	if (p->fontheight == 16) {
+		xy = (yy << (16 + 4)) + (xx << 3) + fb->s.ffb.xy_margin;
+		while (count >= 4) {
+			count -= 4;
+			fbc->fontw = 32;
+			fbc->fontinc = 0x10000;
+			fbc->fontxy = xy;
+			fd1 = p->fontdata + ((*s++ & 0xff) << 4);
+			fd2 = p->fontdata + ((*s++ & 0xff) << 4);
+			fd3 = p->fontdata + ((*s++ & 0xff) << 4);
+			fd4 = p->fontdata + ((*s++ & 0xff) << 4);
+			for (i = 0; i < 16; i++)
+				fbc->font = ((u32)*fd4++) | ((((u32)*fd3++) | ((((u32)*fd2++) | (((u32)*fd1++) << 8)) << 8)) << 8);
+			xy += 32;
+		}
+		while (count) {
+			count--;
+			fbc->fontw = 8;
+			fbc->fontinc = 0x10000;
+			fbc->fontxy = xy;
+			fd1 = p->fontdata + ((*s++ & 0xff) << 4);
+			for (i = 0; i < 16; i++)
+				fbc->font = *fd1++ << 24;
+			xy += 8;
+		}
+	} else {
+		xy = ((yy * p->fontheight) << 16) + (xx << 3) + fb->s.ffb.xy_margin;
+		while (count >= 4) {
+			count -= 4;
+			fbc->fontw = 32;
+			fbc->fontinc = 0x10000;
+			fbc->fontxy = xy;
+			fd1 = p->fontdata + ((*s++ & 0xff) * p->fontheight);
+			fd2 = p->fontdata + ((*s++ & 0xff) * p->fontheight);
+			fd3 = p->fontdata + ((*s++ & 0xff) * p->fontheight);
+			fd4 = p->fontdata + ((*s++ & 0xff) * p->fontheight);
+			for (i = 0; i < p->fontheight; i++)
+				fbc->font = ((u32)*fd4++) | ((((u32)*fd3++) | ((((u32)*fd2++) | (((u32)*fd1++) << 8)) << 8)) << 8);
+			xy += 32;
+		}
+		while (count) {
+			count--;
+			fbc->fontw = 8;
+			fbc->fontinc = 0x10000;
+			fbc->fontxy = xy;
+			fd1 = p->fontdata + ((*s++ & 0xff) * p->fontheight);
+			for (i = 0; i < 16; i++)
+				fbc->font = *fd1++ << 24;
+			xy += 8;
+		}
+	}
+}
+
+static void ffb_revc(struct display *p, int xx, int yy)
+{
+	u8 *dest;
+	int bytes = p->next_line, rows;
+
+	dest = p->screen_base + yy * p->fontheight * bytes + xx * 32;
+	for (rows = p->fontheight ; rows-- ; dest += bytes) {
+		((u32 *)dest)[0] ^= 0xffffffff;
+		((u32 *)dest)[1] ^= 0xffffffff;
+		((u32 *)dest)[2] ^= 0xffffffff;
+		((u32 *)dest)[3] ^= 0xffffffff;
+		((u32 *)dest)[4] ^= 0xffffffff;
+		((u32 *)dest)[5] ^= 0xffffffff;
+		((u32 *)dest)[6] ^= 0xffffffff;
+		((u32 *)dest)[7] ^= 0xffffffff;
+	}
+}
+
+static void ffb_loadcmap (struct fb_info_sbusfb *fb, int index, int count)
+{
+	struct ffb_dac *dac = fb->s.ffb.dac;
+	int i, j = count;
+	
+	dac->type = 0x2000 | index;
+	for (i = index; j--; i++)
+		/* Feed the colors in :)) */
+		dac->value = ((fb->color_map CM(i,0))) |
+			     ((fb->color_map CM(i,1)) << 8) |
+			     ((fb->color_map CM(i,2)) << 16);
+	for (i = index, j = count; i < 16 && j--; i++)
+		ffb_cmap[i] = ((fb->color_map CM(i,0))) |
+			      ((fb->color_map CM(i,1)) << 8) |
+			      ((fb->color_map CM(i,2)) << 16);
+}
+
+static struct display_switch ffb_dispsw __initdata = {
+	ffb_setup, ffb_bmove, ffb_clear, ffb_putc, ffb_putcs, ffb_revc, NULL
+};
+
+static inline void ffb_curs_enable (struct fb_info_sbusfb *fb, int enable)
+{
+	struct ffb_dac *dac = fb->s.ffb.dac;
+	
+	dac->type2 = 0x100;
+	if (fb->s.ffb.dac_rev <= 2)
+		dac->value2 = enable ? 3 : 0;
+	else
+		dac->value2 = enable ? 0 : 3;
+}
+
+static void ffb_setcursormap (struct fb_info_sbusfb *fb, u8 *red, u8 *green, u8 *blue)
+{
+	struct ffb_dac *dac = fb->s.ffb.dac;
+	
+	ffb_curs_enable (fb, 0);
+	dac->type2 = 0x102;
+	dac->value2 = (red[0] | (green[0]<<8) | (blue[0]<<16));
+	dac->value2 = (red[1] | (green[1]<<8) | (blue[1]<<16));
+}
+
+/* Set cursor shape */
+static void ffb_setcurshape (struct fb_info_sbusfb *fb)
+{
+	struct ffb_dac *dac = fb->s.ffb.dac;
+	int i, j;
+
+	ffb_curs_enable (fb, 0);
+	for (j = 0; j < 2; j++) {
+		dac->type2 = j ? 0 : 0x80;
+		for (i = 0; i < 0x40; i++) {
+			if (fb->cursor.size.fbx <= 32) {
+				dac->value2 = fb->cursor.bits [j][i];
+				dac->value2 = 0;
+			} else {
+				dac->value2 = fb->cursor.bits [j][2*i];
+				dac->value2 = fb->cursor.bits [j][2*i+1];
+			}
+		}
+	}	
+}
+
+/* Load cursor information */
+static void ffb_setcursor (struct fb_info_sbusfb *fb)
+{
+	struct ffb_dac *dac = fb->s.ffb.dac;
+	struct cg_cursor *c = &fb->cursor;
+
+	dac->type2 = 0x104;
+	/* Should this be just 0x7ff?? 
+	   Should I do some margin handling and setcurshape in that case? */
+	dac->value2 = (((c->cpos.fby - c->chot.fby) & 0xffff) << 16)
+	              |((c->cpos.fbx - c->chot.fbx) & 0xffff);
+	ffb_curs_enable (fb, fb->cursor.enable);
+}
+
+static char idstring[60] __initdata = { 0 };
+
+__initfunc(char *creatorfb_init(struct fb_info_sbusfb *fb))
+{
+	struct fb_fix_screeninfo *fix = &fb->fix;
+	struct fb_var_screeninfo *var = &fb->var;
+	struct display *disp = &fb->disp;
+	struct fbtype *type = &fb->type;
+	struct linux_prom64_registers regs[2*PROMREG_MAX];
+	int i;
+
+	if (prom_getproperty(fb->prom_node, "reg", (void *) regs, sizeof(regs)) <= 0)
+		return NULL;
+		
+	strcpy(fb->info.modename, "Creator");
+		
+	strcpy(fix->id, "Creator");
+	fix->smem_start = (char *)(regs[0].phys_addr) + FFB_DFB24_POFF;
+	fix->visual = FB_VISUAL_DIRECTCOLOR;
+	fix->line_length = 8192;
+	fix->mmio_start = (char *)(regs[0].phys_addr) + FFB_FBC_REGS_POFF;
+	fix->mmio_len = PAGE_SIZE;
+	
+	var->bits_per_pixel = 32;
+	var->green.offset = 8;
+	var->blue.offset = 16;
+	var->accel_flags = FB_ACCELF_TEXT;
+	
+	disp->scrollmode = SCROLL_YREDRAW;
+	disp->screen_base = (char *)__va(regs[0].phys_addr) + FFB_DFB24_POFF + 8192 * fb->y_margin + 4 * fb->x_margin;
+	fb->s.ffb.xy_margin = (fb->y_margin << 16) + fb->x_margin;
+	fb->s.ffb.fbc = (struct ffb_fbc *)((char *)__va(regs[0].phys_addr) + FFB_FBC_REGS_POFF);
+	fb->s.ffb.dac = (struct ffb_dac *)((char *)__va(regs[0].phys_addr) + FFB_DAC_POFF);
+	fb->dispsw = ffb_dispsw;
+
+	fb->loadcmap = ffb_loadcmap;
+	fb->setcursor = ffb_setcursor;
+	fb->setcursormap = ffb_setcursormap;
+	fb->setcurshape = ffb_setcurshape;
+	fb->fill = ffb_fill;
+	
+	fb->physbase = regs[0].phys_addr;
+	fb->mmap_map = ffb_mmap_map;
+	
+	fb->cursor.hwsize.fbx = 64;
+	fb->cursor.hwsize.fby = 64;
+	
+	type->fb_depth = 24;
+	
+	fb->s.ffb.dac->type = 0x8000;
+	fb->s.ffb.dac_rev = (fb->s.ffb.dac->value >> 0x1c);
+	                
+	i = prom_getintdefault (fb->prom_node, "board_type", 8);
+	                                                        
+	sprintf(idstring, "Creator at %016lx type %d DAC %d", regs[0].phys_addr, i, fb->s.ffb.dac_rev);
+	
+	return idstring;
+}
diff -u --recursive --new-file v2.1.108/linux/drivers/video/cyberfb.c linux/drivers/video/cyberfb.c
--- v2.1.108/linux/drivers/video/cyberfb.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/cyberfb.c	Fri Jul 10 15:18:31 1998
@@ -209,8 +209,8 @@
 
 void cyberfb_setup(char *options, int *ints);
 
-static int cyberfb_open(struct fb_info *info);
-static int cyberfb_release(struct fb_info *info);
+static int cyberfb_open(struct fb_info *info, int user);
+static int cyberfb_release(struct fb_info *info, int user);
 static int cyberfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct
 fb_info *info);
 static int cyberfb_get_var(struct fb_var_screeninfo *var, int con, struct
@@ -231,7 +231,7 @@
  *    Interface to the low level console driver
  */
 
-unsigned long cyberfb_init(unsigned long mem_start);
+void cyberfb_init(void);
 static int Cyberfb_switch(int con, struct fb_info *info);
 static int Cyberfb_updatevar(int con, struct fb_info *info);
 static void Cyberfb_blank(int blank, struct fb_info *info);
@@ -771,7 +771,7 @@
  *  Open/Release the frame buffer device
  */
 
-static int cyberfb_open(struct fb_info *info)
+static int cyberfb_open(struct fb_info *info, int user)
 {
 	/*
 	 * Nothing, only a usage count for the moment
@@ -781,7 +781,7 @@
 	return(0);
 }
 
-static int cyberfb_release(struct fb_info *info)
+static int cyberfb_release(struct fb_info *info, int user)
 {
 	MOD_DEC_USE_COUNT;
 	return(0);
@@ -1016,15 +1016,14 @@
  *    Initialization
  */
 
-__initfunc(unsigned long cyberfb_init(unsigned long mem_start))
+__initfunc(void cyberfb_init(void))
 {
-	int err;
 	struct cyberfb_par par;
 	unsigned long board_addr;
 	const struct ConfigDev *cd;
 
 	if (!(CyberKey = zorro_find(ZORRO_PROD_PHASE5_CYBERVISION64, 0, 0)))
-		return mem_start;
+		return;
 
 	cd = zorro_get_board (CyberKey);
 	zorro_config_board (CyberKey, 0);
@@ -1032,7 +1031,7 @@
 
 	/* This includes the video memory as well as the S3 register set */
 	CyberMem = kernel_map (board_addr + 0x01400000, 0x01000000,
-			       KERNELMAP_NOCACHE_SER, &mem_start);
+			       KERNELMAP_NOCACHE_SER, NULL);
 	CyberRegs = (char*) (CyberMem + 0x00c00000);
 
 	fbhw = &Cyber_switch;
@@ -1046,10 +1045,6 @@
 	fb_info.updatevar = &Cyberfb_updatevar;
 	fb_info.blank = &Cyberfb_blank;
 
-	err = register_framebuffer(&fb_info);
-	if (err < 0)
-		return mem_start;
-
 	fbhw->init();
 	fbhw->decode_var(&cyberfb_default, &par);
 	fbhw->encode_var(&cyberfb_default, &par);
@@ -1059,13 +1054,14 @@
 	cyberfb_set_disp(-1, &fb_info);
 	do_install_cmap(0, &fb_info);
 
+	if (register_framebuffer(&fb_info) < 0)
+		return;
+
 	printk("fb%d: %s frame buffer device, using %ldK of video memory\n",
 	       GET_FB_IDX(fb_info.node), fb_info.modename, CyberSize>>10);
 
 	/* TODO: This driver cannot be unloaded yet */
 	MOD_INC_USE_COUNT;
-
-	return mem_start;
 }
 
 
@@ -1164,7 +1160,7 @@
 }
 
 static void fbcon_cyber8_putcs(struct vc_data *conp, struct display *p,
-			       const char *s, int count, int yy, int xx)
+			       const unsigned short *s, int count, int yy, int xx)
 {
     Cyber_WaitBlit();
     fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
@@ -1186,7 +1182,8 @@
 #ifdef MODULE
 int init_module(void)
 {
-	return cyberfb_init(NULL);
+	cyberfb_init();
+	return 0;
 }
 
 void cleanup_module(void)
diff -u --recursive --new-file v2.1.108/linux/drivers/video/dnfb.c linux/drivers/video/dnfb.c
--- v2.1.108/linux/drivers/video/dnfb.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/dnfb.c	Fri Jul 10 15:18:31 1998
@@ -114,8 +114,8 @@
 
 /* frame buffer operations */
 
-static int dnfb_open(struct fb_info *info);
-static int dnfb_release(struct fb_info *info);
+static int dnfb_open(struct fb_info *info, int user);
+static int dnfb_release(struct fb_info *info, int user);
 static int dnfb_get_fix(struct fb_fix_screeninfo *fix, int con,
 			struct fb_info *info);
 static int dnfb_get_var(struct fb_var_screeninfo *var, int con,
@@ -149,7 +149,7 @@
 
 static char dnfb_name[]="Apollo";
 
-static int dnfb_open(struct fb_info *info)
+static int dnfb_open(struct fb_info *info, int user)
 {
         /*
          * Nothing, only a usage count for the moment
@@ -159,7 +159,7 @@
         return(0);
 }
 
-static int dnfb_release(struct fb_info *info)
+static int dnfb_release(struct fb_info *info, int user)
 {
         MOD_DEC_USE_COUNT;
         return(0);
@@ -311,10 +311,8 @@
 #endif
 }
   
-unsigned long dnfb_init(unsigned long mem_start)
+void dnfb_init(void)
 {
-	int err;
-       
 	fb_info.changevar=NULL;
 	strcpy(&fb_info.modename[0],dnfb_name);
 	fb_info.fontname[0]=0;
@@ -325,13 +323,6 @@
 	fb_info.node = -1;
 	fb_info.fbops = &dnfb_ops;
 	
-	err=register_framebuffer(&fb_info);
-	if(err < 0) {
-		panic("unable to register apollo frame buffer\n");
-	}
- 
-	/* now we have registered we can safely setup the hardware */
-
         outb(RESET_CREG, AP_CONTROL_3A);
         outw(0x0, AP_WRITE_ENABLE);
         outb(NORMAL_MODE,AP_CONTROL_0); 
@@ -339,16 +330,14 @@
         outb(S_DATA_PLN, AP_CONTROL_2);
         outw(SWAP(0x3),AP_ROP_1);
 
-        printk("fb%d: apollo frame buffer alive and kicking !\n",
-	       GET_FB_IDX(fb_info.node));
-
-	
         dnfb_get_var(&disp[0].var, 0, &fb_info);
-
 	dnfb_set_disp(-1, &fb_info);
 
-	return mem_start;
-
+	if (register_framebuffer(&fb_info) < 0)
+		panic("unable to register apollo frame buffer\n");
+ 
+        printk("fb%d: apollo frame buffer alive and kicking !\n",
+	       GET_FB_IDX(fb_info.node));
 }	
 
 	
diff -u --recursive --new-file v2.1.108/linux/drivers/video/dummycon.c linux/drivers/video/dummycon.c
--- v2.1.108/linux/drivers/video/dummycon.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/dummycon.c	Fri Jul 10 15:18:31 1998
@@ -0,0 +1,73 @@
+/*
+ *  linux/drivers/video/dummycon.c -- A dummy console driver
+ *
+ *  To be used if there's no other console driver (e.g. for plain VGA text)
+ *  available, usually until fbcon takes console over.
+ */
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/console_struct.h>
+#include <linux/vt_kern.h>
+#include <linux/init.h>
+
+/*
+ *  Dummy console driver
+ *
+ */
+
+#ifdef __sparc__
+/* Some reasonable defaults, so that we don't loose any text */
+#define DUMMY_COLUMNS	128
+#define DUMMY_ROWS	54
+#else
+#define DUMMY_COLUMNS	80
+#define DUMMY_ROWS	25
+#endif
+
+__initfunc(static const char *dummycon_startup(void))
+{
+    return "dummy device";
+}
+
+static void dummycon_init(struct vc_data *conp, int init)
+{
+    conp->vc_can_do_color = 1;
+    if (init) {
+	conp->vc_cols = DUMMY_COLUMNS;
+	conp->vc_rows = DUMMY_ROWS;
+    } else
+	vc_resize_con(DUMMY_ROWS, DUMMY_COLUMNS, conp->vc_num);
+}
+
+static int dummycon_dummy(void)
+{
+    return 0;
+}
+
+/*
+ *  The console `switch' structure for the dummy console
+ *
+ *  Most of the operations are dummies.
+ */
+
+struct consw dummy_con = {
+    dummycon_startup, dummycon_init,
+    (void *)dummycon_dummy,	/* con_deinit */
+    (void *)dummycon_dummy,	/* con_clear */
+    (void *)dummycon_dummy,	/* con_putc */
+    (void *)dummycon_dummy,	/* con_putcs */
+    (void *)dummycon_dummy,	/* con_cursor */
+    (void *)dummycon_dummy,	/* con_scroll */
+    (void *)dummycon_dummy,	/* con_bmove */
+    (void *)dummycon_dummy,	/* con_switch */
+    (void *)dummycon_dummy,	/* con_blank */
+    (void *)dummycon_dummy,	/* con_get_font */
+    (void *)dummycon_dummy,	/* con_set_font */
+    (void *)dummycon_dummy,	/* con_set_palette */
+    (void *)dummycon_dummy,	/* con_scrolldelta */
+    NULL,			/* con_set_origin */
+    NULL,			/* con_save_screen */
+};
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcmap.c linux/drivers/video/fbcmap.c
--- v2.1.108/linux/drivers/video/fbcmap.c	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcmap.c	Fri Jul 10 15:18:31 1998
@@ -59,7 +59,7 @@
     0x0000, 0x0000, 0x0000, 0x0000, 0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa
 };
 static u16 green8[] = {
-    0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0xaaaa, 0xaaaa
+    0x0000, 0x0000, 0xaaaa, 0x5555, 0x0000, 0x0000, 0xaaaa, 0xaaaa
 };
 static u16 blue8[] = {
     0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa, 0x0000, 0xaaaa
@@ -70,7 +70,7 @@
     0x5555, 0x5555, 0x5555, 0x5555, 0xffff, 0xffff, 0xffff, 0xffff
 };
 static u16 green16[] = {
-    0x0000, 0x0000, 0xaaaa, 0xaaaa, 0x0000, 0x0000, 0xaaaa, 0xaaaa,
+    0x0000, 0x0000, 0xaaaa, 0x5555, 0x0000, 0x0000, 0xaaaa, 0xaaaa,
     0x5555, 0x5555, 0xffff, 0xffff, 0x5555, 0x5555, 0xffff, 0xffff
 };
 static u16 blue16[] = {
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-afb.c linux/drivers/video/fbcon-afb.c
--- v2.1.108/linux/drivers/video/fbcon-afb.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fbcon-afb.c	Fri Jul 10 15:18:31 1998
@@ -250,12 +250,10 @@
     u_short i, j;
     int fg, bg;
 
-    c &= 0xff;
-
     dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
-    cdat0 = p->fontdata+c*p->fontheight;
-    fg = attr_fgcol(p,conp);
-    bg = attr_bgcol(p,conp);
+    cdat0 = p->fontdata+(c&0xff)*p->fontheight;
+    fg = attr_fgcol(p,c);
+    bg = attr_bgcol(p,c);
 
     i = p->var.bits_per_pixel;
     do {
@@ -282,8 +280,8 @@
      *  (cfr. fbcon_putcs_ilbm())
      */
 
-void fbcon_afb_putcs(struct vc_data *conp, struct display *p, const char *s,
-		     int count, int yy, int xx)
+void fbcon_afb_putcs(struct vc_data *conp, struct display *p, 
+		     const unsigned short *s, int count, int yy, int xx)
 {
     u8 *dest, *dest0, *dest1, *expand;
     u8 *cdat1, *cdat2, *cdat3, *cdat4, *cdat10, *cdat20, *cdat30, *cdat40;
@@ -292,8 +290,8 @@
     int fg0, bg0, fg, bg;
 
     dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
-    fg0 = attr_fgcol(p,conp);
-    bg0 = attr_bgcol(p,conp);
+    fg0 = attr_fgcol(p,*s);
+    bg0 = attr_bgcol(p,*s);
 
     while (count--)
 	if (xx&3 || count < 3) {	/* Slow version */
@@ -416,6 +414,17 @@
     fbcon_afb_setup, fbcon_afb_bmove, fbcon_afb_clear, fbcon_afb_putc,
     fbcon_afb_putcs, fbcon_afb_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-afb.h linux/drivers/video/fbcon-afb.h
--- v2.1.108/linux/drivers/video/fbcon-afb.h	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcon-afb.h	Fri Jul 10 15:18:31 1998
@@ -11,5 +11,5 @@
 extern void fbcon_afb_putc(struct vc_data *conp, struct display *p, int c,
 			   int yy, int xx);
 extern void fbcon_afb_putcs(struct vc_data *conp, struct display *p,
-			    const char *s, int count, int yy, int xx);
+			    const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_afb_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-cfb16.c linux/drivers/video/fbcon-cfb16.c
--- v2.1.108/linux/drivers/video/fbcon-cfb16.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fbcon-cfb16.c	Fri Jul 10 15:18:31 1998
@@ -112,13 +112,11 @@
     int bytes = p->next_line, rows;
     u32 eorx, fgx, bgx;
 
-    c &= 0xff;
-
     dest = p->screen_base + yy * p->fontheight * bytes + xx * 16;
-    cdat = p->fontdata + c * p->fontheight;
+    cdat = p->fontdata + (c & 0xff) * p->fontheight;
 
-    fgx = fbcon_cfb16_cmap[attr_fgcol(p, conp)];
-    bgx = fbcon_cfb16_cmap[attr_bgcol(p, conp)];
+    fgx = fbcon_cfb16_cmap[attr_fgcol(p, c)];
+    bgx = fbcon_cfb16_cmap[attr_bgcol(p, c)];
     fgx |= (fgx << 16);
     bgx |= (bgx << 16);
     eorx = fgx ^ bgx;
@@ -132,16 +130,16 @@
     }
 }
 
-void fbcon_cfb16_putcs(struct vc_data *conp, struct display *p, const char *s,
-		       int count, int yy, int xx)
+void fbcon_cfb16_putcs(struct vc_data *conp, struct display *p, 
+		       const unsigned short *s, int count, int yy, int xx)
 {
     u8 *cdat, c, *dest, *dest0;
     int rows, bytes = p->next_line;
     u32 eorx, fgx, bgx;
 
     dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 16;
-    fgx = fbcon_cfb16_cmap[attr_fgcol(p, conp)];
-    bgx = fbcon_cfb16_cmap[attr_bgcol(p, conp)];
+    fgx = fbcon_cfb16_cmap[attr_fgcol(p, *s)];
+    bgx = fbcon_cfb16_cmap[attr_bgcol(p, *s)];
     fgx |= (fgx << 16);
     bgx |= (bgx << 16);
     eorx = fgx ^ bgx;
@@ -183,6 +181,17 @@
     fbcon_cfb16_setup, fbcon_cfb16_bmove, fbcon_cfb16_clear, fbcon_cfb16_putc,
     fbcon_cfb16_putcs, fbcon_cfb16_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-cfb16.h linux/drivers/video/fbcon-cfb16.h
--- v2.1.108/linux/drivers/video/fbcon-cfb16.h	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcon-cfb16.h	Fri Jul 10 15:18:31 1998
@@ -12,5 +12,5 @@
 extern void fbcon_cfb16_putc(struct vc_data *conp, struct display *p, int c,
 			     int yy, int xx);
 extern void fbcon_cfb16_putcs(struct vc_data *conp, struct display *p,
-			      const char *s, int count, int yy, int xx);
+			      const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_cfb16_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-cfb2.c linux/drivers/video/fbcon-cfb2.c
--- v2.1.108/linux/drivers/video/fbcon-cfb2.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fbcon-cfb2.c	Fri Jul 10 15:18:31 1998
@@ -119,13 +119,11 @@
 	int bytes=p->next_line,rows;
 	u32 eorx,fgx,bgx;
 
-	c &= 0xff;
-
 	dest = p->screen_base + yy * p->fontheight * bytes + xx * 2;
-	cdat = p->fontdata + c * p->fontheight;
+	cdat = p->fontdata + (c & 0xff) * p->fontheight;
 
-	fgx=3;/*attr_fgcol(p,conp)&0x0F;*/
-	bgx=attr_bgcol(p,conp)&0x0F;
+	fgx=3;/*attr_fgcol(p,c);*/
+	bgx=attr_bgcol(p,c);
 	fgx |= (fgx << 2);	/* expand color to 8 bits */
 	fgx |= (fgx << 4);
 	bgx |= (bgx << 2);
@@ -140,7 +138,7 @@
 	}
 }
 
-void fbcon_cfb2_putcs(struct vc_data *conp, struct display *p, const char *s,
+void fbcon_cfb2_putcs(struct vc_data *conp, struct display *p, const unsigned short *s,
 		      int count, int yy, int xx)
 {
 	u8 *cdat, c, *dest, *dest0;
@@ -148,8 +146,8 @@
 	u32 eorx, fgx, bgx;
 
 	dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 2;
-	fgx=3/*attr_fgcol(p,conp)*/;
-	bgx=attr_bgcol(p,conp);
+	fgx=3/*attr_fgcol(p,*s)*/;
+	bgx=attr_bgcol(p,*s);
 	fgx |= (fgx << 2);
 	fgx |= (fgx << 4);
 	bgx |= (bgx << 2);
@@ -189,6 +187,17 @@
     fbcon_cfb2_setup, fbcon_cfb2_bmove, fbcon_cfb2_clear, fbcon_cfb2_putc,
     fbcon_cfb2_putcs, fbcon_cfb2_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-cfb2.h linux/drivers/video/fbcon-cfb2.h
--- v2.1.108/linux/drivers/video/fbcon-cfb2.h	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcon-cfb2.h	Fri Jul 10 15:18:31 1998
@@ -11,5 +11,5 @@
 extern void fbcon_cfb2_putc(struct vc_data *conp, struct display *p, int c,
 			    int yy, int xx);
 extern void fbcon_cfb2_putcs(struct vc_data *conp, struct display *p,
-			     const char *s, int count, int yy, int xx);
+			     const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_cfb2_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-cfb24.c linux/drivers/video/fbcon-cfb24.c
--- v2.1.108/linux/drivers/video/fbcon-cfb24.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fbcon-cfb24.c	Fri Jul 10 15:18:31 1998
@@ -19,9 +19,6 @@
 #include "fbcon-cfb24.h"
 
 
-#warning Remove this warning after the test cycle was finalized
-
-
     /*
      *  24 bpp packed pixels
      */
@@ -123,13 +120,11 @@
     int bytes = p->next_line, rows;
     u32 eorx, fgx, bgx;
 
-    c &= 0xff;
-
     dest = p->screen_base + yy * p->fontheight * bytes + xx * 24;
-    cdat = p->fontdata + c * p->fontheight;
+    cdat = p->fontdata + (c & 0xff) * p->fontheight;
 
-    fgx = fbcon_cfb24_cmap[attr_fgcol(p, conp)];
-    bgx = fbcon_cfb24_cmap[attr_bgcol(p, conp)];
+    fgx = fbcon_cfb24_cmap[attr_fgcol(p, c)];
+    bgx = fbcon_cfb24_cmap[attr_bgcol(p, c)];
     eorx = fgx ^ bgx;
 
     for (rows = p->fontheight ; rows-- ; dest += bytes) {
@@ -148,16 +143,16 @@
     }
 }
 
-void fbcon_cfb24_putcs(struct vc_data *conp, struct display *p, const char *s,
-		       int count, int yy, int xx)
+void fbcon_cfb24_putcs(struct vc_data *conp, struct display *p, 
+		       const unsigned short *s, int count, int yy, int xx)
 {
     u8 *cdat, c, *dest, *dest0;
     int rows, bytes = p->next_line;
     u32 eorx, fgx, bgx;
 
     dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 24;
-    fgx = fbcon_cfb24_cmap[attr_fgcol(p, conp)];
-    bgx = fbcon_cfb24_cmap[attr_bgcol(p, conp)];
+    fgx = fbcon_cfb24_cmap[attr_fgcol(p, *s)];
+    bgx = fbcon_cfb24_cmap[attr_bgcol(p, *s)];
     eorx = fgx ^ bgx;
     while (count--) {
 	c = *s++;
@@ -206,6 +201,17 @@
     fbcon_cfb24_setup, fbcon_cfb24_bmove, fbcon_cfb24_clear, fbcon_cfb24_putc,
     fbcon_cfb24_putcs, fbcon_cfb24_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-cfb24.h linux/drivers/video/fbcon-cfb24.h
--- v2.1.108/linux/drivers/video/fbcon-cfb24.h	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcon-cfb24.h	Fri Jul 10 15:18:31 1998
@@ -12,5 +12,5 @@
 extern void fbcon_cfb24_putc(struct vc_data *conp, struct display *p, int c,
 			     int yy, int xx);
 extern void fbcon_cfb24_putcs(struct vc_data *conp, struct display *p,
-			      const char *s, int count, int yy, int xx);
+			      const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_cfb24_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-cfb32.c linux/drivers/video/fbcon-cfb32.c
--- v2.1.108/linux/drivers/video/fbcon-cfb32.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fbcon-cfb32.c	Fri Jul 10 15:18:31 1998
@@ -109,13 +109,11 @@
     int bytes = p->next_line, rows;
     u32 eorx, fgx, bgx;
 
-    c &= 0xff;
-
     dest = p->screen_base + yy * p->fontheight * bytes + xx * 32;
-    cdat = p->fontdata + c * p->fontheight;
+    cdat = p->fontdata + (c & 0xff) * p->fontheight;
 
-    fgx = fbcon_cfb32_cmap[attr_fgcol(p, conp)];
-    bgx = fbcon_cfb32_cmap[attr_bgcol(p, conp)];
+    fgx = fbcon_cfb32_cmap[attr_fgcol(p, c)];
+    bgx = fbcon_cfb32_cmap[attr_bgcol(p, c)];
     eorx = fgx ^ bgx;
 
     for (rows = p->fontheight ; rows-- ; dest += bytes) {
@@ -131,16 +129,16 @@
     }
 }
 
-void fbcon_cfb32_putcs(struct vc_data *conp, struct display *p, const char *s,
-		       int count, int yy, int xx)
+void fbcon_cfb32_putcs(struct vc_data *conp, struct display *p, 
+		       const unsigned short *s, int count, int yy, int xx)
 {
     u8 *cdat, c, *dest, *dest0;
     int rows, bytes = p->next_line;
     u32 eorx, fgx, bgx;
 
     dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 32;
-    fgx = fbcon_cfb32_cmap[attr_fgcol(p, conp)];
-    bgx = fbcon_cfb32_cmap[attr_bgcol(p, conp)];
+    fgx = fbcon_cfb32_cmap[attr_fgcol(p, *s)];
+    bgx = fbcon_cfb32_cmap[attr_bgcol(p, *s)];
     eorx = fgx ^ bgx;
     while (count--) {
 	c = *s++;
@@ -188,6 +186,17 @@
     fbcon_cfb32_setup, fbcon_cfb32_bmove, fbcon_cfb32_clear, fbcon_cfb32_putc,
     fbcon_cfb32_putcs, fbcon_cfb32_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-cfb32.h linux/drivers/video/fbcon-cfb32.h
--- v2.1.108/linux/drivers/video/fbcon-cfb32.h	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcon-cfb32.h	Fri Jul 10 15:18:31 1998
@@ -12,5 +12,5 @@
 extern void fbcon_cfb32_putc(struct vc_data *conp, struct display *p, int c,
 			     int yy, int xx);
 extern void fbcon_cfb32_putcs(struct vc_data *conp, struct display *p,
-			      const char *s, int count, int yy, int xx);
+			      const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_cfb32_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-cfb4.c linux/drivers/video/fbcon-cfb4.c
--- v2.1.108/linux/drivers/video/fbcon-cfb4.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fbcon-cfb4.c	Fri Jul 10 15:18:31 1998
@@ -120,13 +120,11 @@
 	int bytes=p->next_line,rows;
 	u32 eorx,fgx,bgx;
 
-	c &= 0xff;
-
 	dest = p->screen_base + yy * p->fontheight * bytes + xx * 4;
-	cdat = p->fontdata + c * p->fontheight;
+	cdat = p->fontdata + (c & 0xff) * p->fontheight;
 
-	fgx=15;/*attr_fgcol(p,conp)&0x0F;*/
-	bgx=attr_bgcol(p,conp)&0x0F;
+	fgx=15;/*attr_fgcol(p,c);*/
+	bgx=attr_bgcol(p,c);
 	fgx |= (fgx << 4);
 	fgx |= (fgx << 8);
 	bgx |= (bgx << 4);
@@ -141,16 +139,16 @@
 	}
 }
 
-void fbcon_cfb4_putcs(struct vc_data *conp, struct display *p, const char *s,
-		      int count, int yy, int xx)
+void fbcon_cfb4_putcs(struct vc_data *conp, struct display *p, 
+		      const unsigned short *s, int count, int yy, int xx)
 {
 	u8 *cdat, c, *dest, *dest0;
 	int rows,bytes=p->next_line;
 	u32 eorx, fgx, bgx;
 
 	dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 4;
-	fgx=15/*attr_fgcol(p,conp)*/;
-	bgx=attr_bgcol(p,conp);
+	fgx=15/*attr_fgcol(p,*s)*/;
+	bgx=attr_bgcol(p,*s);
 	fgx |= (fgx << 4);
 	fgx |= (fgx << 8);
 	fgx |= (fgx << 16);
@@ -192,6 +190,17 @@
     fbcon_cfb4_setup, fbcon_cfb4_bmove, fbcon_cfb4_clear, fbcon_cfb4_putc,
     fbcon_cfb4_putcs, fbcon_cfb4_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-cfb4.h linux/drivers/video/fbcon-cfb4.h
--- v2.1.108/linux/drivers/video/fbcon-cfb4.h	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcon-cfb4.h	Fri Jul 10 15:18:31 1998
@@ -11,5 +11,5 @@
 extern void fbcon_cfb4_putc(struct vc_data *conp, struct display *p, int c,
 			    int yy, int xx);
 extern void fbcon_cfb4_putcs(struct vc_data *conp, struct display *p,
-			     const char *s, int count, int yy, int xx);
+			     const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_cfb4_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-cfb8.c linux/drivers/video/fbcon-cfb8.c
--- v2.1.108/linux/drivers/video/fbcon-cfb8.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fbcon-cfb8.c	Fri Jul 10 15:18:31 1998
@@ -113,13 +113,11 @@
     int bytes=p->next_line,rows;
     u32 eorx,fgx,bgx;
 
-    c &= 0xff;
-
     dest = p->screen_base + yy * p->fontheight * bytes + xx * 8;
-    cdat = p->fontdata + c * p->fontheight;
+    cdat = p->fontdata + (c & 0xff) * p->fontheight;
 
-    fgx=attr_fgcol(p,conp);
-    bgx=attr_bgcol(p,conp);
+    fgx=attr_fgcol(p,c);
+    bgx=attr_bgcol(p,c);
     fgx |= (fgx << 8);
     fgx |= (fgx << 16);
     bgx |= (bgx << 8);
@@ -132,16 +130,16 @@
     }
 }
 
-void fbcon_cfb8_putcs(struct vc_data *conp, struct display *p, const char *s,
-		      int count, int yy, int xx)
+void fbcon_cfb8_putcs(struct vc_data *conp, struct display *p, 
+		      const unsigned short *s, int count, int yy, int xx)
 {
     u8 *cdat, c, *dest, *dest0;
     int rows,bytes=p->next_line;
     u32 eorx, fgx, bgx;
 
     dest0 = p->screen_base + yy * p->fontheight * bytes + xx * 8;
-    fgx=attr_fgcol(p,conp);
-    bgx=attr_bgcol(p,conp);
+    fgx=attr_fgcol(p,*s);
+    bgx=attr_bgcol(p,*s);
     fgx |= (fgx << 8);
     fgx |= (fgx << 16);
     bgx |= (bgx << 8);
@@ -180,6 +178,17 @@
     fbcon_cfb8_setup, fbcon_cfb8_bmove, fbcon_cfb8_clear, fbcon_cfb8_putc,
     fbcon_cfb8_putcs, fbcon_cfb8_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-cfb8.h linux/drivers/video/fbcon-cfb8.h
--- v2.1.108/linux/drivers/video/fbcon-cfb8.h	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcon-cfb8.h	Fri Jul 10 15:18:31 1998
@@ -11,5 +11,5 @@
 extern void fbcon_cfb8_putc(struct vc_data *conp, struct display *p, int c,
 			    int yy, int xx);
 extern void fbcon_cfb8_putcs(struct vc_data *conp, struct display *p,
-			     const char *s, int count, int yy, int xx);
+			     const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_cfb8_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-ilbm.c linux/drivers/video/fbcon-ilbm.c
--- v2.1.108/linux/drivers/video/fbcon-ilbm.c	Wed Jul  1 19:38:55 1998
+++ linux/drivers/video/fbcon-ilbm.c	Fri Jul 10 15:18:31 1998
@@ -102,12 +102,10 @@
     u8 d;
     int fg0, bg0, fg, bg;
 
-    c &= 0xff;
-
     dest = p->screen_base+yy*p->fontheight*p->next_line+xx;
-    cdat = p->fontdata+c*p->fontheight;
-    fg0 = attr_fgcol(p,conp);
-    bg0 = attr_bgcol(p,conp);
+    cdat = p->fontdata+(c&0xff)*p->fontheight;
+    fg0 = attr_fgcol(p,c);
+    bg0 = attr_bgcol(p,c);
 
     for (rows = p->fontheight; rows--;) {
 	d = *cdat++;
@@ -146,8 +144,8 @@
      *  -- Geert
      */
 
-void fbcon_ilbm_putcs(struct vc_data *conp, struct display *p, const char *s,
-		      int count, int yy, int xx)
+void fbcon_ilbm_putcs(struct vc_data *conp, struct display *p, 
+		      const unsigned short *s, int count, int yy, int xx)
 {
     u8 *dest0, *dest, *cdat1, *cdat2, *cdat3, *cdat4;
     u_int rows, i;
@@ -156,8 +154,8 @@
     int fg0, bg0, fg, bg;
 
     dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
-    fg0 = attr_fgcol(p,conp);
-    bg0 = attr_bgcol(p,conp);
+    fg0 = attr_fgcol(p,*s);
+    bg0 = attr_bgcol(p,*s);
 
     while (count--)
 	if (xx&3 || count < 3) {	/* Slow version */
@@ -264,6 +262,17 @@
     fbcon_ilbm_setup, fbcon_ilbm_bmove, fbcon_ilbm_clear, fbcon_ilbm_putc,
     fbcon_ilbm_putcs, fbcon_ilbm_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-ilbm.h linux/drivers/video/fbcon-ilbm.h
--- v2.1.108/linux/drivers/video/fbcon-ilbm.h	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcon-ilbm.h	Fri Jul 10 15:18:31 1998
@@ -11,5 +11,5 @@
 extern void fbcon_ilbm_putc(struct vc_data *conp, struct display *p, int c,
 			    int yy, int xx);
 extern void fbcon_ilbm_putcs(struct vc_data *conp, struct display *p,
-			     const char *s, int count, int yy, int xx);
+			     const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_ilbm_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-iplan2p2.c linux/drivers/video/fbcon-iplan2p2.c
--- v2.1.108/linux/drivers/video/fbcon-iplan2p2.c	Wed Jul  1 19:38:55 1998
+++ linux/drivers/video/fbcon-iplan2p2.c	Fri Jul 10 15:18:31 1998
@@ -306,13 +306,11 @@
     int bytes = p->next_line;
     u16 eorx, fgx, bgx, fdx;
 
-    c &= 0xff;
-
     dest = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*4 + (xx & 1);
-    cdat = p->fontdata + (c * p->fontheight);
+    cdat = p->fontdata + (c & 0xff) * p->fontheight;
 
-    fgx = expand2w(COLOR_2P(attr_fgcol(p,conp)));
-    bgx = expand2w(COLOR_2P(attr_bgcol(p,conp)));
+    fgx = expand2w(COLOR_2P(attr_fgcol(p,c)));
+    bgx = expand2w(COLOR_2P(attr_bgcol(p,c)));
     eorx = fgx ^ bgx;
 
     for (rows = p->fontheight ; rows-- ; dest += bytes) {
@@ -322,7 +320,7 @@
 }
 
 void fbcon_iplan2p2_putcs(struct vc_data *conp, struct display *p,
-			  const char *s, int count, int yy, int xx)
+			  const unsigned short *s, int count, int yy, int xx)
 {
     u8 *dest, *dest0;
     u8 *cdat, c;
@@ -332,8 +330,8 @@
 
     bytes = p->next_line;
     dest0 = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*4 + (xx & 1);
-    fgx = expand2w(COLOR_2P(attr_fgcol(p,conp)));
-    bgx = expand2w(COLOR_2P(attr_bgcol(p,conp)));
+    fgx = expand2w(COLOR_2P(attr_fgcol(p,*s)));
+    bgx = expand2w(COLOR_2P(attr_bgcol(p,*s)));
     eorx = fgx ^ bgx;
 
     while (count--) {
@@ -378,6 +376,17 @@
     fbcon_iplan2p2_setup, fbcon_iplan2p2_bmove, fbcon_iplan2p2_clear,
     fbcon_iplan2p2_putc, fbcon_iplan2p2_putcs, fbcon_iplan2p2_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-iplan2p2.h linux/drivers/video/fbcon-iplan2p2.h
--- v2.1.108/linux/drivers/video/fbcon-iplan2p2.h	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcon-iplan2p2.h	Fri Jul 10 15:18:31 1998
@@ -11,5 +11,5 @@
 extern void fbcon_iplan2p2_putc(struct vc_data *conp, struct display *p, int c,
 				int yy, int xx);
 extern void fbcon_iplan2p2_putcs(struct vc_data *conp, struct display *p,
-				 const char *s, int count, int yy, int xx);
+				 const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_iplan2p2_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-iplan2p4.c linux/drivers/video/fbcon-iplan2p4.c
--- v2.1.108/linux/drivers/video/fbcon-iplan2p4.c	Wed Jul  1 19:38:55 1998
+++ linux/drivers/video/fbcon-iplan2p4.c	Fri Jul 10 15:18:31 1998
@@ -316,13 +316,11 @@
     int bytes = p->next_line;
     u32 eorx, fgx, bgx, fdx;
 
-    c &= 0xff;
-
     dest = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*8 + (xx & 1);
-    cdat = p->fontdata + (c * p->fontheight);
+    cdat = p->fontdata + (c & 0xff) * p->fontheight;
 
-    fgx = expand4l(attr_fgcol(p,conp));
-    bgx = expand4l(attr_bgcol(p,conp));
+    fgx = expand4l(attr_fgcol(p,c));
+    bgx = expand4l(attr_bgcol(p,c));
     eorx = fgx ^ bgx;
 
     for(rows = p->fontheight ; rows-- ; dest += bytes) {
@@ -332,7 +330,7 @@
 }
 
 void fbcon_iplan2p4_putcs(struct vc_data *conp, struct display *p,
-			  const char *s, int count, int yy, int xx)
+			  const unsigned short *s, int count, int yy, int xx)
 {
     u8 *dest, *dest0;
     u8 *cdat, c;
@@ -342,8 +340,8 @@
 
     bytes = p->next_line;
     dest0 = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*8 + (xx & 1);
-    fgx = expand4l(attr_fgcol(p,conp));
-    bgx = expand4l(attr_bgcol(p,conp));
+    fgx = expand4l(attr_fgcol(p,*s));
+    bgx = expand4l(attr_bgcol(p,*s));
     eorx = fgx ^ bgx;
 
     while (count--) {
@@ -398,6 +396,17 @@
     fbcon_iplan2p4_setup, fbcon_iplan2p4_bmove, fbcon_iplan2p4_clear,
     fbcon_iplan2p4_putc, fbcon_iplan2p4_putcs, fbcon_iplan2p4_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-iplan2p4.h linux/drivers/video/fbcon-iplan2p4.h
--- v2.1.108/linux/drivers/video/fbcon-iplan2p4.h	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcon-iplan2p4.h	Fri Jul 10 15:18:31 1998
@@ -11,5 +11,5 @@
 extern void fbcon_iplan2p4_putc(struct vc_data *conp, struct display *p, int c,
 				int yy, int xx);
 extern void fbcon_iplan2p4_putcs(struct vc_data *conp, struct display *p,
-				 const char *s, int count, int yy, int xx);
+				 const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_iplan2p4_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-iplan2p8.c linux/drivers/video/fbcon-iplan2p8.c
--- v2.1.108/linux/drivers/video/fbcon-iplan2p8.c	Wed Jul  1 19:38:55 1998
+++ linux/drivers/video/fbcon-iplan2p8.c	Fri Jul 10 15:18:31 1998
@@ -348,13 +348,11 @@
     int bytes = p->next_line;
     u32 eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx;
 
-    c &= 0xff;
-
     dest = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*16 + (xx & 1);
-    cdat = p->fontdata + (c * p->fontheight);
+    cdat = p->fontdata + (c & 0xff) * p->fontheight;
 
-    expand8dl(attr_fgcol(p,conp), &fgx1, &fgx2);
-    expand8dl(attr_bgcol(p,conp), &bgx1, &bgx2);
+    expand8dl(attr_fgcol(p,c), &fgx1, &fgx2);
+    expand8dl(attr_bgcol(p,c), &bgx1, &bgx2);
     eorx1 = fgx1 ^ bgx1; eorx2  = fgx2 ^ bgx2;
 
     for(rows = p->fontheight ; rows-- ; dest += bytes) {
@@ -364,7 +362,7 @@
 }
 
 void fbcon_iplan2p8_putcs(struct vc_data *conp, struct display *p,
-			  const char *s, int count, int yy, int xx)
+			  const unsigned short *s, int count, int yy, int xx)
 {
     u8 *dest, *dest0;
     u8 *cdat, c;
@@ -376,8 +374,8 @@
     dest0 = p->screen_base + yy * p->fontheight * bytes + (xx>>1)*16 +
 	    (xx & 1);
 
-    expand8dl(attr_fgcol(p,conp), &fgx1, &fgx2);
-    expand8dl(attr_bgcol(p,conp), &bgx1, &bgx2);
+    expand8dl(attr_fgcol(p,*s), &fgx1, &fgx2);
+    expand8dl(attr_bgcol(p,*s), &bgx1, &bgx2);
     eorx1 = fgx1 ^ bgx1; eorx2  = fgx2 ^ bgx2;
 
     while (count--) {
@@ -435,6 +433,17 @@
     fbcon_iplan2p8_setup, fbcon_iplan2p8_bmove, fbcon_iplan2p8_clear,
     fbcon_iplan2p8_putc, fbcon_iplan2p8_putcs, fbcon_iplan2p8_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-iplan2p8.h linux/drivers/video/fbcon-iplan2p8.h
--- v2.1.108/linux/drivers/video/fbcon-iplan2p8.h	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcon-iplan2p8.h	Fri Jul 10 15:18:31 1998
@@ -11,5 +11,5 @@
 extern void fbcon_iplan2p8_putc(struct vc_data *conp, struct display *p, int c,
 				int yy, int xx);
 extern void fbcon_iplan2p8_putcs(struct vc_data *conp, struct display *p,
-				 const char *s, int count, int yy, int xx);
+				 const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_iplan2p8_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-mac.c linux/drivers/video/fbcon-mac.c
--- v2.1.108/linux/drivers/video/fbcon-mac.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fbcon-mac.c	Fri Jul 10 15:18:31 1998
@@ -90,11 +90,15 @@
      w = (r&~7) - ((l+7)&~7);
      dw = (dr&~7) - ((dl+7)&~7);
      if (lo != dlo) {
-       char err_str[256];
-       unsigned long cnt;
+       unsigned char err_str[128];
+       unsigned short err_buf[256];
+       unsigned long cnt, len;
        sprintf( err_str, "ERROR: Shift algorithm: sx=%d,sy=%d,dx=%d,dy=%d,w=%d,h=%d,bpp=%d",
 		sx,sy,dx,dy,width,height,p->var.bits_per_pixel);
-       fbcon_mac_putcs(p->conp, p, err_str, strlen(err_str), 0, 0);
+       len = strlen(err_str);
+       for (cnt = 0; cnt < len; cnt++)
+         err_buf[cnt] = 0x700 | err_str[cnt];
+       fbcon_mac_putcs(p->conp, p, err_buf, len, 0, 0);
        /* pause for the user */
        for(cnt = 0; cnt < 50000; cnt++)
 		udelay(100);
@@ -191,7 +195,7 @@
    u8 *dest;
    int l,r,t,b,w,lo,s;
 
-   inverse = attr_reverse(p,conp);
+   inverse = attr_reverse(p,conp->vc_attr);
    pixel = inverse ? PIXEL_WHITE_MAC : PIXEL_BLACK_MAC;
    dest = (u8 *) (p->screen_base + sy * p->fontheight * p->next_line);
 
@@ -268,12 +272,10 @@
    u8 d;
    int j;
 
-   c &= 0xff;
-
-   cdat = p->fontdata+c*p->fontheight;
-   bold = attr_bold(p,conp);
-   ch_reverse = attr_reverse(p,conp);
-   ch_underline = attr_underline(p,conp);
+   cdat = p->fontdata+(c&0xff)*p->fontheight;
+   bold = attr_bold(p,c);
+   ch_reverse = attr_reverse(p,c);
+   ch_underline = attr_underline(p,c);
 
    for (rows = 0; rows < p->fontheight; rows++) {
       d = *cdat++;
@@ -293,10 +295,10 @@
 }
 
 
-void fbcon_mac_putcs(struct vc_data *conp, struct display *p, const char *s,
-		     int count, int yy, int xx)
+void fbcon_mac_putcs(struct vc_data *conp, struct display *p, 
+		     const unsigned short *s, int count, int yy, int xx)
 {
-   u8 c;
+   u16 c;
 
    while (count--) {
       c = *s++;
@@ -497,6 +499,17 @@
     fbcon_mac_setup, fbcon_mac_bmove, fbcon_mac_clear, fbcon_mac_putc,
     fbcon_mac_putcs, fbcon_mac_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-mac.h linux/drivers/video/fbcon-mac.h
--- v2.1.108/linux/drivers/video/fbcon-mac.h	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcon-mac.h	Fri Jul 10 15:18:31 1998
@@ -11,5 +11,5 @@
 extern void fbcon_mac_putc(struct vc_data *conp, struct display *p, int c,
 			   int yy, int xx);
 extern void fbcon_mac_putcs(struct vc_data *conp, struct display *p,
-			    const char *s, int count, int yy, int xx);
+			    const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_mac_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-mfb.c linux/drivers/video/fbcon-mfb.c
--- v2.1.108/linux/drivers/video/fbcon-mfb.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fbcon-mfb.c	Fri Jul 10 15:18:31 1998
@@ -70,13 +70,13 @@
     dest = p->screen_base+sy*p->fontheight*p->next_line+sx;
 
     if (sx == 0 && width == p->next_line) {
-	if (attr_reverse(p,conp))
+	if (attr_reverse(p,conp->vc_attr))
 	    mymemset(dest, height*p->fontheight*width);
 	else
 	    mymemclear(dest, height*p->fontheight*width);
     } else
 	for (rows = height*p->fontheight; rows--; dest += p->next_line)
-	    if (attr_reverse(p,conp))
+	    if (attr_reverse(p,conp->vc_attr))
 		mymemset(dest, width);
 	    else
 		mymemclear_small(dest, width);
@@ -89,13 +89,11 @@
     u_int rows, bold, revs, underl;
     u8 d;
 
-    c &= 0xff;
-
     dest = p->screen_base+yy*p->fontheight*p->next_line+xx;
-    cdat = p->fontdata+c*p->fontheight;
-    bold = attr_bold(p,conp);
-    revs = attr_reverse(p,conp);
-    underl = attr_underline(p,conp);
+    cdat = p->fontdata+(c&0xff)*p->fontheight;
+    bold = attr_bold(p,c);
+    revs = attr_reverse(p,c);
+    underl = attr_underline(p,c);
 
     for (rows = p->fontheight; rows--; dest += p->next_line) {
 	d = *cdat++;
@@ -109,17 +107,17 @@
     }
 }
 
-void fbcon_mfb_putcs(struct vc_data *conp, struct display *p, const char *s,
-		     int count, int yy, int xx)
+void fbcon_mfb_putcs(struct vc_data *conp, struct display *p, 
+		     const unsigned short *s, int count, int yy, int xx)
 {
     u8 *dest, *dest0, *cdat;
     u_int rows, bold, revs, underl;
     u8 c, d;
 
     dest0 = p->screen_base+yy*p->fontheight*p->next_line+xx;
-    bold = attr_bold(p,conp);
-    revs = attr_reverse(p,conp);
-    underl = attr_underline(p,conp);
+    bold = attr_bold(p,*s);
+    revs = attr_reverse(p,*s);
+    underl = attr_underline(p,*s);
 
     while (count--) {
 	c = *s++;
@@ -157,6 +155,17 @@
     fbcon_mfb_setup, fbcon_mfb_bmove, fbcon_mfb_clear, fbcon_mfb_putc,
     fbcon_mfb_putcs, fbcon_mfb_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-mfb.h linux/drivers/video/fbcon-mfb.h
--- v2.1.108/linux/drivers/video/fbcon-mfb.h	Thu Mar 26 15:57:04 1998
+++ linux/drivers/video/fbcon-mfb.h	Fri Jul 10 15:18:31 1998
@@ -11,5 +11,5 @@
 extern void fbcon_mfb_putc(struct vc_data *conp, struct display *p, int c,
 			   int yy, int xx);
 extern void fbcon_mfb_putcs(struct vc_data *conp, struct display *p,
-			    const char *s, int count, int yy, int xx);
+			    const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_mfb_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-vga.c linux/drivers/video/fbcon-vga.c
--- v2.1.108/linux/drivers/video/fbcon-vga.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fbcon-vga.c	Fri Jul 10 15:18:31 1998
@@ -77,21 +77,17 @@
      */
 
 static inline u16 fbcon_vga_attr(struct display *p,
-				 struct vc_data *conp)
+				 unsigned short s)
 {
-	if (conp->vc_can_do_color) {
-		return conp->vc_attr << 8;
-	}
-
         /* Underline and reverse-video are mutually exclusive on MDA.
          * Since reverse-video is used for cursors and selected areas, 
 	 * it takes precedence.
          */
 
-	return (attr_reverse(p, conp) ? 0x7000 :
-		(attr_underline(p, conp) ? 0x0100 : 0x0700)) |
-	       (attr_bold(p, conp) ? 0x0800 : 0) |
-	       (attr_blink(p, conp) ? 0x8000 : 0);
+	return (attr_reverse(p, s) ? 0x7000 :
+		(attr_underline(p, s) ? 0x0100 : 0x0700)) |
+	       (attr_bold(p, s) ? 0x0800 : 0) |
+	       (attr_blink(p, s) ? 0x8000 : 0);
 }
 
 void fbcon_vga_setup(struct display *p)
@@ -146,16 +142,25 @@
 		    int x)
 {
     u16 *dst = (u16 *)(p->screen_base+y*p->next_line+x*2);
-    vga_writew(fbcon_vga_attr(p, conp) | (c & 0xff), dst);
+    if (conp->vc_can_do_color)
+    	vga_writew(c, dst);
+    else
+    	vga_writew(fbcon_vga_attr(p, c) | (c & 0xff), dst);
 }
 
-void fbcon_vga_putcs(struct vc_data *conp, struct display *p, const char *s,
-		     int count, int y, int x)
+void fbcon_vga_putcs(struct vc_data *conp, struct display *p, 
+		     const unsigned short *s, int count, int y, int x)
 {
     u16 *dst = (u16 *)(p->screen_base+y*p->next_line+x*2);
-    u16 sattr = fbcon_vga_attr(p, conp);
-    while (count--)
-	vga_writew(sattr | ((int) (*s++) & 0xff), dst++);
+    u16 sattr;
+    if (conp->vc_can_do_color)
+    	while (count--)
+    	    vga_writew(*s++, dst++);
+    else {
+        sattr = fbcon_vga_attr(p, *s);
+        while (count--)
+	    vga_writew(sattr | ((int) (*s++) & 0xff), dst++);
+    }
 }
 
 void fbcon_vga_revc(struct display *p, int x, int y)
@@ -175,6 +180,17 @@
     fbcon_vga_setup, fbcon_vga_bmove, fbcon_vga_clear, fbcon_vga_putc,
     fbcon_vga_putcs, fbcon_vga_revc, NULL
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
 
 
     /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon-vga.h linux/drivers/video/fbcon-vga.h
--- v2.1.108/linux/drivers/video/fbcon-vga.h	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fbcon-vga.h	Fri Jul 10 15:18:31 1998
@@ -11,5 +11,5 @@
 extern void fbcon_vga_putc(struct vc_data *conp, struct display *p, int c,
 			   int yy, int xx);
 extern void fbcon_vga_putcs(struct vc_data *conp, struct display *p,
-			    const char *s, int count, int yy, int xx);
+			    const unsigned short *s, int count, int yy, int xx);
 extern void fbcon_vga_revc(struct display *p, int xx, int yy);
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon.c linux/drivers/video/fbcon.c
--- v2.1.108/linux/drivers/video/fbcon.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fbcon.c	Fri Jul 10 15:18:31 1998
@@ -25,6 +25,7 @@
  *			   Andreas Schwab
  *
  *  Hardware cursor support added by Emmanuel Marty (core@ggi-project.org)
+ *  Smart redraw scrolling added by Jakub Jelinek (jj@ultra.linux.cz)
  *
  *
  *  The low level operations for the various display memory organizations are
@@ -37,6 +38,7 @@
  *    o ilbm			Amiga interleaved bitplanes
  *    o iplan2p[248]		Atari interleaved bitplanes
  *    o mfb			Monochrome
+ *    o vga			VGA characters/attributes
  *
  *  To do:
  *
@@ -50,7 +52,6 @@
 
 #undef FBCONDEBUG
 
-#define SUPPORT_SCROLLBACK	0
 #define FLASHING_CURSOR		1
 
 #include <linux/config.h>
@@ -68,6 +69,7 @@
 #include <linux/fb.h>
 #include <linux/vt_kern.h>
 #include <linux/selection.h>
+#include <linux/smp.h>
 #include <linux/init.h>
 
 #include <asm/irq.h>
@@ -87,6 +89,7 @@
 #include <asm/machdep.h>
 #include <asm/setup.h>
 #endif
+#define INCLUDE_LINUX_LOGO_DATA
 #include <asm/linux_logo.h>
 
 #include "fbcon.h"
@@ -99,6 +102,10 @@
 #  define DPRINTK(fmt, args...)
 #endif
 
+#define LOGO_H			80
+#define LOGO_W			80
+#define LOGO_LINE	(LOGO_W/8)
+
 struct display fb_display[MAX_NR_CONSOLES];
 
 
@@ -131,7 +138,7 @@
     vbl_cursor_cnt = 0;
     cursor_was_drawn = cursor_drawn;
     cursor_drawn = 0;
-    return(cursor_was_drawn);
+    return cursor_was_drawn;
 }
 #endif
 
@@ -139,10 +146,6 @@
  *  Scroll Method
  */
 
-#define SCROLL_YWRAP	(0)
-#define SCROLL_YPAN	(1)
-#define SCROLL_YMOVE	(2)
-
 #define divides(a, b)	((!(a) || (b)%(a)) ? 0 : 1)
 
 
@@ -150,18 +153,17 @@
  *  Interface used by the world
  */
 
-static unsigned long fbcon_startup(unsigned long kmem_start,
-				   const char **display_desc);
-static void fbcon_init(struct vc_data *conp);
+static const char *fbcon_startup(void);
+static void fbcon_init(struct vc_data *conp, int init);
 static void fbcon_deinit(struct vc_data *conp);
 static int fbcon_changevar(int con);
 static void fbcon_clear(struct vc_data *conp, int sy, int sx, int height,
 		       int width);
 static void fbcon_putc(struct vc_data *conp, int c, int ypos, int xpos);
-static void fbcon_putcs(struct vc_data *conp, const char *s, int count,
+static void fbcon_putcs(struct vc_data *conp, const unsigned short *s, int count,
 			int ypos, int xpos);
 static void fbcon_cursor(struct vc_data *conp, int mode);
-static void fbcon_scroll(struct vc_data *conp, int t, int b, int dir,
+static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir,
 			 int count);
 static void fbcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
 			int height, int width);
@@ -177,21 +179,16 @@
  *  Internal routines
  */
 
-static void fbcon_setup(int con, int setcol, int init);
+static void fbcon_setup(int con, int init, int logo);
 static __inline__ int real_y(struct display *p, int ypos);
 #if FLASHING_CURSOR
 static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp);
 #endif
 static __inline__ void updatescrollmode(struct display *p);
-#if SUPPORT_SCROLLBACK
 static __inline__ void ywrap_up(int unit, struct vc_data *conp,
 				struct display *p, int count);
 static __inline__ void ywrap_down(int unit, struct vc_data *conp,
 				  struct display *p, int count);
-#else
-static __inline__ void ywrap_up(int unit, struct display *p, int count);
-static __inline__ void ywrap_down(int unit, struct display *p, int count);
-#endif
 static __inline__ void ypan_up(int unit, struct vc_data *conp,
 			       struct display *p, int count);
 static __inline__ void ypan_down(int unit, struct vc_data *conp,
@@ -237,21 +234,21 @@
 
 static struct display_switch fbcon_dummy;
 
+/* NOTE: fbcon cannot be __initfunc: it may be called from take_over_console later */
 
-__initfunc(static unsigned long fbcon_startup(unsigned long kmem_start,
-					      const char **display_desc))
+static const char *fbcon_startup(void)
 {
+    const char *display_desc = "frame buffer device";
     int irqres = 1;
+    static int done = 0;
 
-    /* Probe all frame buffer devices */
-    kmem_start = probe_framebuffers(kmem_start);
-
-    if (!num_registered_fb) {
-            DPRINTK("no framebuffer registered\n");
-	    return kmem_start;
-    }
-
-    *display_desc = "frame buffer device";
+    /*
+     *  If num_registered_fb is zero, this is a call for the dummy part.
+     *  The frame buffer devices weren't initialized yet.
+     */
+    if (!num_registered_fb || done)
+	return display_desc;
+    done = 1;
 
 #ifdef CONFIG_AMIGA
     if (MACH_IS_AMIGA) {
@@ -307,6 +304,11 @@
     }
 #endif /* CONFIG_MAC */
 
+#if defined(__arm__) && defined(IRQ_VSYNCPULSE)
+    irqres = request_irq(IRQ_VSYNCPULSE, fbcon_vbl_handler, 0,
+			 "console/cursor", fbcon_vbl_handler);
+#endif
+
     if (irqres) {
 	cursor_blink_rate = DEFAULT_CURSOR_BLINK_RATE;
 	cursor_timer.expires = jiffies+HZ/50;
@@ -315,14 +317,11 @@
 	add_timer(&cursor_timer);
     }
 
-    if (!console_show_logo)
-	console_show_logo = fbcon_show_logo;
-
-    return kmem_start;
+    return display_desc;
 }
 
 
-static void fbcon_init(struct vc_data *conp)
+static void fbcon_init(struct vc_data *conp, int init)
 {
     int unit = conp->vc_num;
     struct fb_info *info;
@@ -339,7 +338,7 @@
 	                     fb_display[unit].var.bits_per_pixel);
     fb_display[unit].conp = conp;
     fb_display[unit].fb_info = info;
-    fbcon_setup(unit, 1, 1);
+    fbcon_setup(unit, init, !init);
 }
 
 
@@ -356,13 +355,15 @@
 static int fbcon_changevar(int con)
 {
     if (fb_display[con].conp)
-	    fbcon_setup(con, 1, 0);
-    return(0);
+	    fbcon_setup(con, 0, 0);
+    return 0;
 }
 
 
 static __inline__ void updatescrollmode(struct display *p)
 {
+    if (p->scrollmode == SCROLL_YREDRAW)
+    	return;
     if (divides(p->ywrapstep, p->fontheight) &&
 	divides(p->fontheight, p->var.yres_virtual))
 	p->scrollmode = SCROLL_YWRAP;
@@ -374,11 +375,17 @@
 }
 
 
-static void fbcon_setup(int con, int setcol, int init)
+static void fbcon_setup(int con, int init, int logo)
 {
     struct display *p = &fb_display[con];
     struct vc_data *conp = p->conp;
     int nr_rows, nr_cols;
+    int old_rows, old_cols;
+    /* Only if not module */
+    extern int initmem_freed;
+    
+    if (con != fg_console || initmem_freed || p->type == FB_TYPE_TEXT)
+    	logo = 0;
 
     p->var.xoffset = p->var.yoffset = p->yscroll = 0;  /* reset wrap/pan */
 
@@ -401,7 +408,37 @@
 	}
     }
     updatescrollmode(p);
-
+    
+    if (logo) {
+    	/* Need to make room for the logo */
+    	int logo_lines = (LOGO_H + p->fontheight - 1) / p->fontheight;
+    	unsigned short *q = (unsigned short *)(conp->vc_origin + 
+    				conp->vc_size_row * conp->vc_rows);
+	unsigned short *r;
+    
+    	for (r = q - logo_lines * conp->vc_cols; r < q; r++)
+    	    if (*r != conp->vc_video_erase_char)
+    	    	break;
+    	if (r == q) {
+    	    /* We can scroll screen down */
+    	    int cnt;
+    	    int step = logo_lines * conp->vc_cols;
+
+	    r = q - step - conp->vc_cols;
+    	    for (cnt = conp->vc_rows - logo_lines; cnt > 0; cnt--) {
+    	    	scr_memcpyw(r + step, r, conp->vc_size_row);
+    	    	r -= conp->vc_cols;
+    	    }
+    	    conp->vc_y += logo_lines;
+    	    conp->vc_pos += logo_lines * conp->vc_size_row;
+    	}
+    	scr_memsetw((unsigned short *)conp->vc_origin, conp->vc_video_erase_char, 
+    		conp->vc_size_row * logo_lines);
+    }
+    
+    old_cols = conp->vc_cols;
+    old_rows = conp->vc_rows;
+    
     nr_cols = p->var.xres/p->fontwidth;
     nr_rows = p->var.yres/p->fontheight;
     /*
@@ -423,13 +460,18 @@
     }
     p->dispsw->setup(p);
 
-    if (setcol) {
-	p->fgcol = p->var.bits_per_pixel > 2 ? 7 : (1<<p->var.bits_per_pixel)-1;
-	p->bgcol = 0;
-    }
+    p->fgcol = p->var.bits_per_pixel > 2 ? 7 : (1<<p->var.bits_per_pixel)-1;
+    p->bgcol = 0;
 
-    if (!init)
+    if (!init) {
 	vc_resize_con(nr_rows, nr_cols, con);
+	if (con == fg_console &&
+	    old_rows == nr_rows && old_cols == nr_cols)
+	    update_screen(con); /* So that we set origin correctly */
+    }
+	
+    if (logo)
+    	fbcon_show_logo(); /* This is protected above by initmem_freed */
 }
 
 
@@ -463,7 +505,7 @@
     int rows = p->vrows;
 
     ypos += p->yscroll;
-    return(ypos < rows ? ypos : ypos-rows);
+    return ypos < rows ? ypos : ypos-rows;
 }
 
 
@@ -511,7 +553,7 @@
 }
 
 
-static void fbcon_putcs(struct vc_data *conp, const char *s, int count,
+static void fbcon_putcs(struct vc_data *conp, const unsigned short *s, int count,
 		       int ypos, int xpos)
 {
     int unit = conp->vc_num;
@@ -545,18 +587,21 @@
 	(mode == CM_ERASE) == !cursor_on)
 	return;
 
-	if (CURSOR_UNDRAWN ())
+	cursor_on = 0;
+	if (cursor_drawn)
 	    p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));
+
 	p->cursor_x = conp->vc_x;
 	p->cursor_y = conp->vc_y;
 
 	switch (mode) {
 	    case CM_ERASE:
-		cursor_on = 0;
-		break;
-
+	        cursor_drawn = 0;
+	        break;
 	    case CM_MOVE:
 	    case CM_DRAW:
+		if (cursor_drawn)
+		    p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));
 		vbl_cursor_cnt = CURSOR_DRAW_DELAY;
 		cursor_on = 1;
 		break;
@@ -568,33 +613,29 @@
 static void fbcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp)
 {
     struct display *p;
+    static int _vbl_cursor_cnt = 1, _vbl_cursor_drawn;
 
-    if (!cursor_on)
-	return;
+    if (!--_vbl_cursor_cnt) {
+	_vbl_cursor_cnt = cursor_blink_rate;
+	_vbl_cursor_drawn = !_vbl_cursor_drawn;
+    }
 
-    if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
-	/* Here no check is possible for console changing. The console
-	 * switching code should set vbl_cursor_cnt to an appropriate value.
-	 */
-	p = &fb_display[fg_console];
-	p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));
-	cursor_drawn ^= 1;
-	vbl_cursor_cnt = cursor_blink_rate;
+    if (cursor_on && vbl_cursor_cnt) {
+	if (cursor_drawn != _vbl_cursor_drawn) {
+	    p = &fb_display[fg_console];
+	    p->dispsw->revc(p, p->cursor_x, real_y(p, p->cursor_y));
+	}
+	cursor_drawn = _vbl_cursor_drawn;
     }
 }
 #endif
 
-#if SUPPORT_SCROLLBACK
+static int scrollback_phys_max = 0;
 static int scrollback_max = 0;
 static int scrollback_current = 0;
-#endif
 
-#if SUPPORT_SCROLLBACK
 static __inline__ void ywrap_up(int unit, struct vc_data *conp,
 				struct display *p, int count)
-#else
-static __inline__ void ywrap_up(int unit, struct display *p, int count)
-#endif
 {
     p->yscroll += count;
     if (p->yscroll >= p->vrows)	/* Deal with wrap */
@@ -603,21 +644,15 @@
     p->var.yoffset = p->yscroll*p->fontheight;
     p->var.vmode |= FB_VMODE_YWRAP;
     p->fb_info->updatevar(unit, p->fb_info);
-#if SUPPORT_SCROLLBACK
     scrollback_max += count;
-    if (scrollback_max > p->vrows-conp->vc_rows)
-	scrollback_max = p->vrows-conp->vc_rows;
+    if (scrollback_max > scrollback_phys_max)
+	scrollback_max = scrollback_phys_max;
     scrollback_current = 0;
-#endif
 }
 
 
-#if SUPPORT_SCROLLBACK
 static __inline__ void ywrap_down(int unit, struct vc_data *conp,
 				  struct display *p, int count)
-#else
-static __inline__ void ywrap_down(int unit, struct display *p, int count)
-#endif
 {
     p->yscroll -= count;
     if (p->yscroll < 0)		/* Deal with wrap */
@@ -626,12 +661,10 @@
     p->var.yoffset = p->yscroll*p->fontheight;
     p->var.vmode |= FB_VMODE_YWRAP;
     p->fb_info->updatevar(unit, p->fb_info);
-#if SUPPORT_SCROLLBACK
     scrollback_max -= count;
     if (scrollback_max < 0)
 	scrollback_max = 0;
     scrollback_current = 0;
-#endif
 }
 
 
@@ -639,15 +672,19 @@
 			       struct display *p, int count)
 {
     p->yscroll += count;
-    if (p->yscroll+conp->vc_rows > p->vrows) {
-	p->dispsw->bmove(p, p->yscroll, 0, 0, 0, conp->vc_rows-count,
-			 conp->vc_cols);
-	p->yscroll = 0;
+    if (p->yscroll > p->vrows-conp->vc_rows) {
+	p->dispsw->bmove(p, p->vrows-conp->vc_rows, 0, 0, 0,
+			 conp->vc_rows, conp->vc_cols);
+	p->yscroll -= p->vrows-conp->vc_rows;
     }
     p->var.xoffset = 0;
     p->var.yoffset = p->yscroll*p->fontheight;
     p->var.vmode &= ~FB_VMODE_YWRAP;
     p->fb_info->updatevar(unit, p->fb_info);
+    scrollback_max += count;
+    if (scrollback_max > scrollback_phys_max)
+	scrollback_max = scrollback_phys_max;
+    scrollback_current = 0;
 }
 
 
@@ -656,34 +693,92 @@
 {
     p->yscroll -= count;
     if (p->yscroll < 0) {
-	p->dispsw->bmove(p, p->yscroll + count, 0,
-			 p->vrows - conp->vc_rows + count, 0,
-			 conp->vc_rows - count, conp->vc_cols);
-	p->yscroll = p->vrows - conp->vc_rows;
+	p->dispsw->bmove(p, 0, 0, p->vrows-conp->vc_rows, 0,
+			 conp->vc_rows, conp->vc_cols);
+	p->yscroll += p->vrows-conp->vc_rows;
     }
     p->var.xoffset = 0;
     p->var.yoffset = p->yscroll*p->fontheight;
     p->var.vmode &= ~FB_VMODE_YWRAP;
     p->fb_info->updatevar(unit, p->fb_info);
+    scrollback_max -= count;
+    if (scrollback_max < 0)
+	scrollback_max = 0;
+    scrollback_current = 0;
+}
+
+
+static void fbcon_redraw(struct vc_data *conp, struct display *p, 
+			 int line, int count, int offset)
+{
+    unsigned short *d = (unsigned short *)
+	(conp->vc_origin + conp->vc_size_row * line);
+    unsigned short *s = d + offset;
+    while (count--) {
+	unsigned short *start = s;
+	unsigned short *le = (unsigned short *)
+	    ((unsigned long)s + conp->vc_size_row);
+	unsigned short c;
+	int x = 0;
+	unsigned short attr = 1;
+
+	do {
+	    c = scr_readw(s);
+	    if (attr != (c & 0xff00)) {
+		attr = c & 0xff00;
+		if (s > start) {
+		    p->dispsw->putcs(conp, p, start, s - start, line, x);
+		    x += s - start;
+		    start = s;
+		}
+	    }
+	    if (c == scr_readw(d)) {
+	    	if (s > start) {
+	    	    p->dispsw->putcs(conp, p, start, s - start, line, x);
+		    x += s - start + 1;
+		    start = s + 1;
+	    	} else {
+	    	    x++;
+	    	    start++;
+	    	}
+	    }
+	    scr_writew(c, d);
+	    s++;
+	    d++;
+	} while (s < le);
+	if (s > start)
+	    p->dispsw->putcs(conp, p, start, s - start, line, x);
+	if (offset > 0)
+		line++;
+	else {
+		line--;
+		/* NOTE: We subtract two lines from these pointers */
+		s -= conp->vc_size_row;
+		d -= conp->vc_size_row;
+	}
+    }
 }
 
 
-static void fbcon_scroll(struct vc_data *conp, int t, int b, int dir,
-			 int count)
+static int fbcon_scroll(struct vc_data *conp, int t, int b, int dir,
+			int count)
 {
     int unit = conp->vc_num;
     struct display *p = &fb_display[unit];
+    int is_txt = (p->type == FB_TYPE_TEXT);
 
     if (!p->can_soft_blank && console_blanked)
-	return;
+	return 0;
 
     if (!count)
-	return;
+	return 0;
 
     fbcon_cursor(conp, CM_ERASE);
 
     /*
      * ++Geert: Only use ywrap/ypan if the console is in text mode
+     * ++Andrew: Only use ypan on hardware text mode when scrolling the
+     *           whole screen (prevents flicker).
      */
 
     switch (dir) {
@@ -697,11 +792,7 @@
 			    if (t > 0)
 				fbcon_bmove(conp, 0, 0, count, 0, t,
 					    conp->vc_cols);
-#if SUPPORT_SCROLLBACK
 			    ywrap_up(unit, conp, p, count);
-#else
-			    ywrap_up(unit, p, count);
-#endif
 			    if (conp->vc_rows-b > 0)
 				fbcon_bmove(conp, b-count, 0, b, 0,
 					    conp->vc_rows-b, conp->vc_cols);
@@ -712,7 +803,8 @@
 			break;
 
 		    case SCROLL_YPAN:
-			if (b-t-count > 3*conp->vc_rows>>2) {
+			if (( is_txt && (b-t == conp->vc_rows)) ||
+			    (!is_txt && (b-t-count > 3*conp->vc_rows>>2))) {
 			    if (t > 0)
 				fbcon_bmove(conp, 0, 0, count, 0, t,
 					    conp->vc_cols);
@@ -732,6 +824,15 @@
 			p->dispsw->clear(conp, p, b-count, 0, count,
 					 conp->vc_cols);
 			break;
+		    case SCROLL_YREDRAW:
+		    	fbcon_redraw(conp, p, t, b-t-count, count*conp->vc_cols);
+		    	p->dispsw->clear(conp, p, b-count, 0, count,
+		    			 conp->vc_cols);
+		    	scr_memsetw((unsigned short *)(conp->vc_origin + 
+		    		    conp->vc_size_row * (b-count)), 
+		    		    conp->vc_video_erase_char,
+		    		    conp->vc_size_row * count);
+		    	return 1;
 		}
 	    else {
 		fbcon_bmove(conp, t+count, 0, t, 0, b-t-count, conp->vc_cols);
@@ -749,11 +850,7 @@
 			    if (conp->vc_rows-b > 0)
 				fbcon_bmove(conp, b, 0, b-count, 0,
 					    conp->vc_rows-b, conp->vc_cols);
-#if SUPPORT_SCROLLBACK
 			    ywrap_down(unit, conp, p, count);
-#else
-			    ywrap_down(unit, p, count);
-#endif
 			    if (t > 0)
 				fbcon_bmove(conp, count, 0, 0, 0, t,
 					    conp->vc_cols);
@@ -764,7 +861,8 @@
 			break;
 
 		    case SCROLL_YPAN:
-			if (b-t-count > 3*conp->vc_rows>>2) {
+			if (( is_txt && (b-t == conp->vc_rows)) ||
+			    (!is_txt && (b-t-count > 3*conp->vc_rows>>2))) {
 			    if (conp->vc_rows-b > 0)
 				fbcon_bmove(conp, b, 0, b-count, 0,
 					    conp->vc_rows-b, conp->vc_cols);
@@ -783,6 +881,15 @@
 					 conp->vc_cols);
 			p->dispsw->clear(conp, p, t, 0, count, conp->vc_cols);
 			break;
+			
+		    case SCROLL_YREDRAW:
+			fbcon_redraw(conp, p, b - 1, b-t-count, -count*conp->vc_cols);
+			p->dispsw->clear(conp, p, t, 0, count, conp->vc_cols);
+		    	scr_memsetw((unsigned short *)(conp->vc_origin + 
+		    		    conp->vc_size_row * t), 
+		    		    conp->vc_video_erase_char,
+		    		    conp->vc_size_row * count);
+		    	return 1;
 		}
 	    else {
 		/*
@@ -806,6 +913,7 @@
 	    fbcon_clear(conp, 0, t, conp->vc_rows, count);
 	    break;
     }
+    return 0;
 }
 
 
@@ -876,13 +984,26 @@
     struct display *p = &fb_display[unit];
     struct fb_info *info = p->fb_info;
 
-    if (info && info->switch_con)
-	(*info->switch_con)(conp->vc_num, info);
-#if SUPPORT_SCROLLBACK
+    p->var.yoffset = p->yscroll*p->fontheight;
+    switch (p->scrollmode) {
+	case SCROLL_YWRAP:
+	    scrollback_phys_max = p->vrows-conp->vc_rows;
+	    break;
+	case SCROLL_YPAN:
+	    scrollback_phys_max = p->vrows-2*conp->vc_rows;
+	    if (scrollback_phys_max < 0)
+		scrollback_phys_max = 0;
+	    break;
+	default:
+	    scrollback_phys_max = 0;
+	    break;
+    }
     scrollback_max = 0;
     scrollback_current = 0;
-#endif
-    return(0);
+
+    if (info && info->switch_con)
+	(*info->switch_con)(conp->vc_num, info);
+    return 1;
 }
 
 
@@ -896,28 +1017,28 @@
     if (!p->can_soft_blank) {
 	if (blank) {
 #ifdef CONFIG_MAC
-	    if (MACH_IS_MAC)
-		mymemset(p->screen_base,
-			 p->var.xres_virtual*p->var.yres_virtual*
-			 p->var.bits_per_pixel>>3);
-	    else
-#endif
-	    if (p->visual == FB_VISUAL_MONO01)
-		mymemset(p->screen_base,
-			 p->var.xres_virtual*p->var.yres_virtual*
-			 p->var.bits_per_pixel>>3);
-	     else
-		 mymemclear(p->screen_base,
-			    p->var.xres_virtual*p->var.yres_virtual*
-			    p->var.bits_per_pixel>>3);
-	    return(0);
+	    if (MACH_IS_MAC) {
+		if (p->screen_base)
+		    mymemset(p->screen_base,
+			     p->var.xres_virtual*p->var.yres_virtual*
+			     p->var.bits_per_pixel>>3);
+	    } else
+#endif
+	    if (p->visual == FB_VISUAL_MONO01) {
+		if (p->screen_base)
+		    mymemset(p->screen_base,
+			     p->var.xres_virtual*p->var.yres_virtual*
+			     p->var.bits_per_pixel>>3);
+	     } else
+		 p->dispsw->clear(p->conp, p, 0, 0, p->conp->vc_rows, p->conp->vc_cols);
+	    return 0;
 	} else {
 	    /* Tell console.c that it has to restore the screen itself */
-	    return(1);
+	    return 1;
 	}
     }
     (*info->blank)(blank, info);
-    return(0);
+    return 0;
 }
 
 
@@ -934,12 +1055,12 @@
 
     if (alloc < size)
 	/* allocation length not sufficient */
-	return( -ENAMETOOLONG );
+	return -ENAMETOOLONG;
 
     for (i = 0; i < 256; i++)
 	for (j = 0; j < p->fontheight; j++)
 	    data[i*32+j] = p->fontdata[i*p->fontheight+j];
-    return( 0 );
+    return 0;
 }
 
 
@@ -967,19 +1088,19 @@
 	name[sizeof(name)-1] = 0;
 
 	if (!findsoftfont( name, &w, &h, (u8 **)&data ))
-	    return( -ENOENT );
+	    return -ENOENT;
 	userspace = 0;
     } else if (w == 1) {
 	/* copy font from some other console in 'h'*/
 	struct display *op;
 
 	if (h < 0 || !vc_cons_allocated( h ))
-	    return( -ENOTTY );
+	    return -ENOTTY;
 	if (h == unit)
-	    return( 0 ); /* nothing to do */
+	    return 0; /* nothing to do */
 	op = &fb_display[h];
 	if (op->fontdata == p->fontdata)
-	    return( 0 ); /* already the same font... */
+	    return 0; /* already the same font... */
 
 	resize = (op->fontwidth != p->fontwidth) ||
 		 (op->fontheight != p->fontheight);
@@ -995,7 +1116,7 @@
 
     if (w != 8)
 	/* Currently only fontwidth == 8 supported */
-	return( -ENXIO );
+	return -ENXIO;
 
     resize = (w != p->fontwidth) || (h != p->fontheight);
     size = (w+7)/8 * h * 256;
@@ -1005,7 +1126,7 @@
 
     if (userspace) {
 	if (!(new_data = kmalloc( sizeof(int)+size, GFP_USER )))
-	    return( -ENOMEM );
+	    return -ENOMEM;
 	new_data += sizeof(int);
 	REFCOUNT(new_data) = 1; /* usage counter */
 
@@ -1037,7 +1158,7 @@
     if (old_data && (--REFCOUNT(old_data) == 0))
 	kfree( old_data - sizeof(int) );
 
-    return( 0 );
+    return 0;
 }
 
 static u16 palette_red[16];
@@ -1056,7 +1177,7 @@
     u8 val;
 
     if (!conp->vc_can_do_color || (!p->can_soft_blank && console_blanked))
-	return(-EINVAL);
+	return -EINVAL;
     for (i = j = 0; i < 16; i++) {
 	k = table[i];
 	val = conp->vc_palette[j++];
@@ -1075,43 +1196,52 @@
 
 static int fbcon_scrolldelta(struct vc_data *conp, int lines)
 {
-#if SUPPORT_SCROLLBACK
-    int unit = fg_console; /* xxx */
-    struct display *p = &fb_display[unit];
-    int offset;
+    int unit, offset, limit, scrollback_old;
+    struct display *p;
 
-    if (!p->can_soft_blank && console_blanked ||
-	vt_cons[unit]->vc_mode != KD_TEXT || !lines ||
-	p->scrollmode != SCROLL_YWRAP)
-	return 0;
+    /* FIXME: Sync to new code, remember to set visible_origin */
 
-    fbcon_cursor(conp, CM_ERASE);
+    if (!scrollback_phys_max)
+	return -ENOSYS;
 
+    scrollback_old = scrollback_current;
     scrollback_current -= lines;
     if (scrollback_current < 0)
 	scrollback_current = 0;
     else if (scrollback_current > scrollback_max)
 	scrollback_current = scrollback_max;
+    if (scrollback_current == scrollback_old)
+	return 0;
+
+    unit = fg_console;
+    p = &fb_display[unit];
+    if (!p->can_soft_blank &&
+	(console_blanked || vt_cons[unit]->vc_mode != KD_TEXT || !lines))
+	return 0;
+    fbcon_cursor(conp, CM_ERASE);
 
     offset = p->yscroll-scrollback_current;
+    limit = p->vrows;
+    switch (p->scrollmode) {
+	case SCROLL_YWRAP:
+	    p->var.vmode |= FB_VMODE_YWRAP;
+	    break;
+	case SCROLL_YPAN:
+	    limit -= conp->vc_rows;
+	    p->var.vmode &= ~FB_VMODE_YWRAP;
+	    break;
+    }
     if (offset < 0)
-	offset += p->vrows;
-    else if (offset > p->vrows)
-	offset -= p->vrows;
-    p->var.vmode |= FB_VMODE_YWRAP;
+	offset += limit;
+    else if (offset >= limit)
+	offset -= limit;
     p->var.xoffset = 0;
     p->var.yoffset = offset*p->fontheight;
     p->fb_info->updatevar(unit, p->fb_info);
-#else
-    return -ENOSYS;
-#endif
+    return 0;
 }
 
 
-#define LOGO_H			80
-#define LOGO_W			80
-#define LOGO_LINE	(LOGO_W/8)
-
 __initfunc(static int fbcon_show_logo( void ))
 {
     struct display *p = &fb_display[fg_console]; /* draw to vt in foreground */
@@ -1120,9 +1250,13 @@
     unsigned char *fb = p->screen_base;
     unsigned char *logo;
     unsigned char *dst, *src;
-    int i, j, n, x1, y1;
+    int i, j, n, x1, y1, x;
     int logo_depth, done = 0;
 
+    /* Return if the frame buffer is not mapped */
+    if (!fb)
+	return 0;
+
     /* Set colors if visual is PSEUDOCOLOR and we have enough colors, or for
      * TRUECOLOR */
     if ((p->visual == FB_VISUAL_PSEUDOCOLOR && depth >= 4) ||
@@ -1182,216 +1316,214 @@
 	logo_depth = 1;
     }
 
+    for (x = 0; x < smp_num_cpus * (LOGO_W + 8) &&
+    	 x < p->var.xres - (LOGO_W + 8); x += (LOGO_W + 8)) {
+    	 
 #if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CFB24) || \
-    defined(CONFIG_FBCON_CFB32)
-    if (p->visual == FB_VISUAL_TRUECOLOR) {
-	unsigned int val;		/* max. depth 32! */
-	int bdepth;
-	int redshift, greenshift, blueshift;
+    defined(CONFIG_FBCON_CFB32) || defined(CONFIG_FB_SBUS)
+        if (p->visual == FB_VISUAL_TRUECOLOR) {
+	    unsigned int val;		/* max. depth 32! */
+	    int bdepth;
+	    int redshift, greenshift, blueshift;
 		
-	/* Bug: Doesn't obey msb_right ... (who needs that?) */
-	redshift   = p->var.red.offset;
-	greenshift = p->var.green.offset;
-	blueshift  = p->var.blue.offset;
-
-	if (depth >= 24 && (depth % 8) == 0) {
-	    /* have at least 8 bits per color */
-	    src = logo;
-	    bdepth = depth/8;
-	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
-		dst = fb + y1*line;
-		for( x1 = 0; x1 < LOGO_W; x1++, src++ ) {
-		    val = (*src << redshift) |
-			  (*src << greenshift) |
-			  (*src << blueshift);
+	    /* Bug: Doesn't obey msb_right ... (who needs that?) */
+	    redshift   = p->var.red.offset;
+	    greenshift = p->var.green.offset;
+	    blueshift  = p->var.blue.offset;
+
+	    if (depth >= 24 && (depth % 8) == 0) {
+		/* have at least 8 bits per color */
+		src = logo;
+		bdepth = depth/8;
+		for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		    dst = fb + y1*line + x*bdepth;
+		    for( x1 = 0; x1 < LOGO_W; x1++, src++ ) {
+			val = (*src << redshift) |
+			      (*src << greenshift) |
+			      (*src << blueshift);
 #ifdef __LITTLE_ENDIAN
-		    for( i = 0; i < bdepth; ++i )
+			for( i = 0; i < bdepth; ++i )
 #else
-		    for( i = bdepth-1; i >= 0; --i )
+			for( i = bdepth-1; i >= 0; --i )
 #endif
-			*dst++ = val >> (i*8);
+			    *dst++ = val >> (i*8);
+		    }
 		}
 	    }
-	}
-	else if (depth >= 15 && depth <= 23) {
-	    /* have 5..7 bits per color, using 16 color image */
-	    unsigned int pix;
-	    src = linux_logo16;
-	    bdepth = (depth+7)/8;
-	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
-		dst = fb + y1*line;
-		for( x1 = 0; x1 < LOGO_W/2; x1++, src++ ) {
-		    pix = (*src >> 4) | 0x10; /* upper nibble */
-		    val = (pix << redshift) |
-			  (pix << greenshift) |
-			  (pix << blueshift);
-		    for( i = 0; i < bdepth; ++i )
-			*dst++ = val >> (i*8);
-		    pix = (*src & 0x0f) | 0x10; /* lower nibble */
-		    val = (pix << redshift) |
-			  (pix << greenshift) |
-			  (pix << blueshift);
-		    for( i = bdepth-1; i >= 0; --i )
-			*dst++ = val >> (i*8);
+	    else if (depth >= 15 && depth <= 23) {
+	        /* have 5..7 bits per color, using 16 color image */
+		unsigned int pix;
+		src = linux_logo16;
+		bdepth = (depth+7)/8;
+		for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		    dst = fb + y1*line + x*bdepth;
+		    for( x1 = 0; x1 < LOGO_W/2; x1++, src++ ) {
+			pix = (*src >> 4) | 0x10; /* upper nibble */
+			val = (pix << redshift) |
+			      (pix << greenshift) |
+			      (pix << blueshift);
+			for( i = 0; i < bdepth; ++i )
+			    *dst++ = val >> (i*8);
+			pix = (*src & 0x0f) | 0x10; /* lower nibble */
+			val = (pix << redshift) |
+			      (pix << greenshift) |
+			      (pix << blueshift);
+			for( i = bdepth-1; i >= 0; --i )
+			    *dst++ = val >> (i*8);
+		    }
 		}
 	    }
-	}
-	
-	done = 1;
-    }
+	    done = 1;
+        }
 #endif
 #if defined(CONFIG_FBCON_CFB16) || defined(CONFIG_FBCON_CFB24) || \
-    defined(CONFIG_FBCON_CFB32)
-    if ((depth % 8 == 0) && (p->visual == FB_VISUAL_DIRECTCOLOR)) {
-	/* Modes without color mapping, needs special data transformation... */
-	unsigned int val;		/* max. depth 32! */
-	int bdepth = depth/8;
-	unsigned char mask[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
-	unsigned char redmask, greenmask, bluemask;
-	int redshift, greenshift, blueshift;
+    defined(CONFIG_FBCON_CFB32) || defined(CONFIG_FB_SBUS)
+	if ((depth % 8 == 0) && (p->visual == FB_VISUAL_DIRECTCOLOR)) {
+	    /* Modes without color mapping, needs special data transformation... */
+	    unsigned int val;		/* max. depth 32! */
+	    int bdepth = depth/8;
+	    unsigned char mask[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
+	    unsigned char redmask, greenmask, bluemask;
+	    int redshift, greenshift, blueshift;
 		
-	/* Bug: Doesn't obey msb_right ... (who needs that?) */
-	redmask   = mask[p->var.red.length   < 8 ? p->var.red.length   : 8];
-	greenmask = mask[p->var.green.length < 8 ? p->var.green.length : 8];
-	bluemask  = mask[p->var.blue.length  < 8 ? p->var.blue.length  : 8];
-	redshift   = p->var.red.offset   - (8-p->var.red.length);
-	greenshift = p->var.green.offset - (8-p->var.green.length);
-	blueshift  = p->var.blue.offset  - (8-p->var.blue.length);
-
-	src = logo;
-	for( y1 = 0; y1 < LOGO_H; y1++ ) {
-	    dst = fb + y1*line;
-	    for( x1 = 0; x1 < LOGO_W; x1++, src++ ) {
-		val = ((linux_logo_red[*src-32]   & redmask)   << redshift) |
-		      ((linux_logo_green[*src-32] & greenmask) << greenshift) |
-		      ((linux_logo_blue[*src-32]  & bluemask)  << blueshift);
+	    /* Bug: Doesn't obey msb_right ... (who needs that?) */
+	    redmask   = mask[p->var.red.length   < 8 ? p->var.red.length   : 8];
+	    greenmask = mask[p->var.green.length < 8 ? p->var.green.length : 8];
+	    bluemask  = mask[p->var.blue.length  < 8 ? p->var.blue.length  : 8];
+	    redshift   = p->var.red.offset   - (8-p->var.red.length);
+	    greenshift = p->var.green.offset - (8-p->var.green.length);
+	    blueshift  = p->var.blue.offset  - (8-p->var.blue.length);
+
+	    src = logo;
+	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		dst = fb + y1*line + x*bdepth;
+		for( x1 = 0; x1 < LOGO_W; x1++, src++ ) {
+		    val = ((linux_logo_red[*src-32]   & redmask)   << redshift) |
+		          ((linux_logo_green[*src-32] & greenmask) << greenshift) |
+		          ((linux_logo_blue[*src-32]  & bluemask)  << blueshift);
 #ifdef __LITTLE_ENDIAN
-		for( i = 0; i < bdepth; ++i )
+		    for( i = 0; i < bdepth; ++i )
 #else
-		for( i = bdepth-1; i >= 0; --i )
+		    for( i = bdepth-1; i >= 0; --i )
 #endif
-		    *dst++ = val >> (i*8);
+			*dst++ = val >> (i*8);
+		}
 	    }
+	    done = 1;
 	}
-		
-	done = 1;
-    }
 #endif
-#if defined(CONFIG_FBCON_CFB8)
-    if (depth == 8 && p->type == FB_TYPE_PACKED_PIXELS) {
-	/* depth 8 or more, packed, with color registers */
+#if defined(CONFIG_FBCON_CFB8) || defined(CONFIG_FB_SBUS)
+	if (depth == 8 && p->type == FB_TYPE_PACKED_PIXELS) {
+	    /* depth 8 or more, packed, with color registers */
 		
-	src = logo;
-	for( y1 = 0; y1 < LOGO_H; y1++ ) {
-	    dst = fb + y1*line;
-	    for( x1 = 0; x1 < LOGO_W; x1++ ) {
-		*dst++ = *src++;
+	    src = logo;
+	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		dst = fb + y1*line + x;
+		for( x1 = 0; x1 < LOGO_W; x1++ )
+		    *dst++ = *src++;
 	    }
+	    done = 1;
 	}
-
-	done = 1;
-    }
 #endif
 #if defined(CONFIG_FBCON_AFB) || defined(CONFIG_FBCON_ILBM) || \
     defined(CONFIG_FBCON_IPLAN2P2) || defined(CONFIG_FBCON_IPLAN2P4) || \
     defined(CONFIG_FBCON_IPLAN2P8)
-    if (depth >= 2 && (p->type == FB_TYPE_PLANES ||
-		       p->type == FB_TYPE_INTERLEAVED_PLANES)) {
-	/* planes (normal or interleaved), with color registers */
-	int bit;
-	unsigned char val, mask;
-	int plane = p->next_plane;
+	if (depth >= 2 && (p->type == FB_TYPE_PLANES ||
+			   p->type == FB_TYPE_INTERLEAVED_PLANES)) {
+	    /* planes (normal or interleaved), with color registers */
+	    int bit;
+	    unsigned char val, mask;
+	    int plane = p->next_plane;
 
-	/* for support of Atari interleaved planes */
+	    /* for support of Atari interleaved planes */
 #define MAP_X(x)	(plane > line ? x : (x & ~1)*depth + (x & 1))
-	/* extract a bit from the source image */
+	    /* extract a bit from the source image */
 #define	BIT(p,pix,bit)	(p[pix*logo_depth/8] & \
 			 (1 << ((8-((pix*logo_depth)&7)-logo_depth) + bit)))
 		
-	src = logo;
-	for( y1 = 0; y1 < LOGO_H; y1++ ) {
-	    for( x1 = 0; x1 < LOGO_LINE; x1++, src += logo_depth ) {
-		dst = fb + y1*line + MAP_X(x1);
-		for( bit = 0; bit < logo_depth; bit++ ) {
-		    val = 0;
-		    for( mask = 0x80, i = 0; i < 8; mask >>= 1, i++ ) {
-			if (BIT( src, i, bit ))
-			    val |= mask;
+	    src = logo;
+	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		for( x1 = 0; x1 < LOGO_LINE; x1++, src += logo_depth ) {
+		    dst = fb + y1*line + MAP_X(x1);
+		    for( bit = 0; bit < logo_depth; bit++ ) {
+			val = 0;
+			for( mask = 0x80, i = 0; i < 8; mask >>= 1, i++ ) {
+			    if (BIT( src, i, bit ))
+				val |= mask;
+			}
+			*dst = val;
+			dst += plane;
 		    }
-		    *dst = val;
-		    dst += plane;
 		}
 	    }
-	}
 	
-	/* fill remaining planes
-	 * special case for logo_depth == 4: we used color registers 16..31,
-	 * so fill plane 4 with 1 bits instead of 0 */
-	if (depth > logo_depth) {
-	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
-		for( x1 = 0; x1 < LOGO_LINE; x1++ ) {
-		    dst = fb + y1*line + MAP_X(x1) + logo_depth*plane;
-		    for( i = logo_depth; i < depth; i++, dst += plane )
-			*dst = (i == logo_depth && logo_depth == 4)
-			       ? 0xff : 0x00;
+	    /* fill remaining planes
+	     * special case for logo_depth == 4: we used color registers 16..31,
+	     * so fill plane 4 with 1 bits instead of 0 */
+	    if (depth > logo_depth) {
+		for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		    for( x1 = 0; x1 < LOGO_LINE; x1++ ) {
+			dst = fb + y1*line + MAP_X(x1) + logo_depth*plane;
+			for( i = logo_depth; i < depth; i++, dst += plane )
+			    *dst = (i == logo_depth && logo_depth == 4)
+				   ? 0xff : 0x00;
+		    }
 		}
 	    }
+	    done = 1;
+	    break;
 	}
-	
-	done = 1;
-    }
 #endif
 #if defined(CONFIG_FBCON_MFB) || defined(CONFIG_FBCON_AFB) || \
     defined(CONFIG_FBCON_ILBM)
-    if (depth == 1 && (p->type == FB_TYPE_PACKED_PIXELS ||
-		       p->type == FB_TYPE_PLANES ||
-		       p->type == FB_TYPE_INTERLEAVED_PLANES)) {
-	/* monochrome */
-	unsigned char inverse = p->inverse ? 0x00 : 0xff;
-
-	/* can't use simply memcpy because need to apply inverse */
-	for( y1 = 0; y1 < LOGO_H; y1++ ) {
-	    src = logo + y1*LOGO_LINE;
-	    dst = fb + y1*line;
-	    for( x1 = 0; x1 < LOGO_LINE; ++x1 )
-		*dst++ = *src++ ^ inverse;
-	}
-
-	done = 1;
-    }
-#endif
-#ifdef CONFIG_FBCON_VGA
-    if (depth == 1 && p->type == FB_TYPE_VGA_TEXT) {
 
-        int height = 0;
-        char *p;
-    
-        printk(linux_mda_image);
+	if (depth == 1 && (p->type == FB_TYPE_PACKED_PIXELS ||
+			   p->type == FB_TYPE_PLANES ||
+			   p->type == FB_TYPE_INTERLEAVED_PLANES)) {
 
-        for (p = linux_mda_image; *p; p++)
-            if (*p == '\n')
-                height++;
+	    /* monochrome */
+	    unsigned char inverse = p->inverse ? 0x00 : 0xff;
 
-        return height;
-    }
+	    /* can't use simply memcpy because need to apply inverse */
+	    for( y1 = 0; y1 < LOGO_H; y1++ ) {
+		src = logo + y1*LOGO_LINE + x/8;
+		dst = fb + y1*line;
+		for( x1 = 0; x1 < LOGO_LINE; ++x1 )
+		    *dst++ = *src++ ^ inverse;
+	    }
+	    done = 1;
+	}
 #endif
+    }
+    
     /* Modes not yet supported: packed pixels with depth != 8 (does such a
      * thing exist in reality?) */
 
-    return( done ? LOGO_H/p->fontheight + 1 : 0 );
+    return done ? (LOGO_H + p->fontheight - 1) / p->fontheight : 0 ;
 }
 
-
-
 /*
  *  The console `switch' structure for the frame buffer based console
  */
-
+ 
 struct consw fb_con = {
-    fbcon_startup, fbcon_init, fbcon_deinit, fbcon_clear, fbcon_putc,
-    fbcon_putcs, fbcon_cursor, fbcon_scroll, fbcon_bmove, fbcon_switch,
-    fbcon_blank, fbcon_get_font, fbcon_set_font, fbcon_set_palette,
-    fbcon_scrolldelta
+    con_startup: 	fbcon_startup, 
+    con_init: 		fbcon_init,
+    con_deinit: 	fbcon_deinit,
+    con_clear: 		fbcon_clear,
+    con_putc: 		fbcon_putc,
+    con_putcs: 		fbcon_putcs,
+    con_cursor: 	fbcon_cursor,
+    con_scroll: 	fbcon_scroll,
+    con_bmove: 		fbcon_bmove,
+    con_switch: 	fbcon_switch,
+    con_blank: 		fbcon_blank,
+    con_get_font: 	fbcon_get_font,
+    con_set_font: 	fbcon_set_font,
+    con_set_palette: 	fbcon_set_palette,
+    con_scrolldelta: 	fbcon_scrolldelta,
+    con_set_origin: 	NULL,
+    con_save_screen: 	NULL,
 };
 
 
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fbcon.h linux/drivers/video/fbcon.h
--- v2.1.108/linux/drivers/video/fbcon.h	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fbcon.h	Fri Jul 10 15:18:31 1998
@@ -26,7 +26,7 @@
 		  int height, int width);
     void (*putc)(struct vc_data *conp, struct display *p, int c, int yy,
     		 int xx);
-    void (*putcs)(struct vc_data *conp, struct display *p, const char *s,
+    void (*putcs)(struct vc_data *conp, struct display *p, const unsigned short *s,
 		  int count, int yy, int xx);     
     void (*revc)(struct display *p, int xx, int yy);
     void (*cursor)(struct display *p, int mode, int xx, int yy);
@@ -38,22 +38,31 @@
      */
 
 /* Color */
-#define attr_fgcol(p,conp)    \
-	(((conp)->vc_attr >> ((p)->inverse ? 4 : 0)) & 0x0f)
-#define attr_bgcol(p,conp)    \
-	(((conp)->vc_attr >> ((p)->inverse ? 0 : 4)) & 0x0f)
+#define attr_fgcol(p,s)    \
+	(((s) >> ((p)->inverse ? 12 : 8)) & 0x0f)
+#define attr_bgcol(p,s)    \
+	(((s) >> ((p)->inverse ? 8 : 12)) & 0x0f)
 #define	attr_bgcol_ec(p,conp) \
 	(((conp)->vc_video_erase_char >> ((p)->inverse ? 8 : 12)) & 0x0f)
 
 /* Monochrome */
-#define attr_bold(p,conp) \
-	((conp)->vc_attr & 2)
-#define attr_reverse(p,conp) \
-	(((conp)->vc_attr & 8) ^ ((p)->inverse ? 8 : 0))
-#define attr_underline(p,conp) \
-	((conp)->vc_attr & 4)
-#define attr_blink(p,conp) \
-	((conp)->vc_attr & 0x80)
+#define attr_bold(p,s) \
+	((s) & 0x200)
+#define attr_reverse(p,s) \
+	(((s) & 0x800) ^ ((p)->inverse ? 0x800 : 0))
+#define attr_underline(p,s) \
+	((s) & 0x400)
+#define attr_blink(p,s) \
+	((s) & 0x8000)
+	
+    /*
+     *  Scroll Method
+     */
+
+#define SCROLL_YWRAP	(0)
+#define SCROLL_YPAN	(1)
+#define SCROLL_YMOVE	(2)
+#define SCROLL_YREDRAW	(3)
 
 
 /* ================================================================= */
diff -u --recursive --new-file v2.1.108/linux/drivers/video/font_sun8x16.c linux/drivers/video/font_sun8x16.c
--- v2.1.108/linux/drivers/video/font_sun8x16.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/font_sun8x16.c	Fri Jul 10 15:18:31 1998
@@ -0,0 +1,265 @@
+#define FONTDATAMAX 4096
+
+char fontname_sun8x16[] = "SUN8x16";
+
+int  fontheight_sun8x16 = 16;
+int  fontwidth_sun8x16  = 8;
+
+unsigned char fontdata_sun8x16[FONTDATAMAX] = {
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x7e,0x81,0xa5,0x81,0x81,0xbd,0x99,0x81,0x81,0x7e,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x7e,0xff,0xdb,0xff,0xff,0xc3,0xe7,0xff,0xff,0x7e,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x6c,0xfe,0xfe,0xfe,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x18,0x3c,0x3c,0xe7,0xe7,0xe7,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x18,0x3c,0x7e,0xff,0xff,0x7e,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0xff,0xff,0xff,0xff,0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff,0xff,0xff,0xff,0xff,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00,0x00,0x00,0x00,0x00,
+/* */ 0xff,0xff,0xff,0xff,0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff,0xff,0xff,0xff,0xff,
+/* */ 0x00,0x00,0x1e,0x0e,0x1a,0x32,0x78,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x3c,0x66,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18,0x18,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x3f,0x33,0x3f,0x30,0x30,0x30,0x30,0x70,0xf0,0xe0,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x7f,0x63,0x7f,0x63,0x63,0x63,0x63,0x67,0xe7,0xe6,0xc0,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x18,0x18,0xdb,0x3c,0xe7,0x3c,0xdb,0x18,0x18,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfe,0xf8,0xf0,0xe0,0xc0,0x80,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x02,0x06,0x0e,0x1e,0x3e,0xfe,0x3e,0x1e,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x66,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x7f,0xdb,0xdb,0xdb,0x7b,0x1b,0x1b,0x1b,0x1b,0x1b,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x7c,0xc6,0x60,0x38,0x6c,0xc6,0xc6,0x6c,0x38,0x0c,0xc6,0x7c,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xfe,0xfe,0xfe,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x7e,0x3c,0x18,0x7e,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x18,0x0c,0xfe,0x0c,0x18,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x30,0x60,0xfe,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x24,0x66,0xff,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x10,0x38,0x38,0x7c,0x7c,0xfe,0xfe,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0xfe,0xfe,0x7c,0x7c,0x38,0x38,0x10,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*!*/ 0x00,0x00,0x18,0x3c,0x3c,0x3c,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00,
+/*"*/ 0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*#*/ 0x00,0x00,0x00,0x6c,0x6c,0xfe,0x6c,0x6c,0x6c,0xfe,0x6c,0x6c,0x00,0x00,0x00,0x00,
+/*$*/ 0x18,0x18,0x7c,0xc6,0xc2,0xc0,0x7c,0x06,0x06,0x86,0xc6,0x7c,0x18,0x18,0x00,0x00,
+/*%*/ 0x00,0x00,0x00,0x00,0xc2,0xc6,0x0c,0x18,0x30,0x60,0xc6,0x86,0x00,0x00,0x00,0x00,
+/*&*/ 0x00,0x00,0x38,0x6c,0x6c,0x38,0x76,0xdc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+/*'*/ 0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*(*/ 0x00,0x00,0x0c,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0c,0x00,0x00,0x00,0x00,
+/*)*/ 0x00,0x00,0x30,0x18,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x18,0x30,0x00,0x00,0x00,0x00,
+/***/ 0x00,0x00,0x00,0x00,0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00,0x00,0x00,0x00,0x00,
+/*+*/ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,
+/*,*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00,
+/*-*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*.*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x02,0x06,0x0c,0x18,0x30,0x60,0xc0,0x80,0x00,0x00,0x00,0x00,
+/*0*/ 0x00,0x00,0x7c,0xc6,0xc6,0xce,0xde,0xf6,0xe6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/*1*/ 0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7e,0x00,0x00,0x00,0x00,
+/*2*/ 0x00,0x00,0x7c,0xc6,0x06,0x0c,0x18,0x30,0x60,0xc0,0xc6,0xfe,0x00,0x00,0x00,0x00,
+/*3*/ 0x00,0x00,0x7c,0xc6,0x06,0x06,0x3c,0x06,0x06,0x06,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/*4*/ 0x00,0x00,0x0c,0x1c,0x3c,0x6c,0xcc,0xfe,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00,
+/*5*/ 0x00,0x00,0xfe,0xc0,0xc0,0xc0,0xfc,0x06,0x06,0x06,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/*6*/ 0x00,0x00,0x38,0x60,0xc0,0xc0,0xfc,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/*7*/ 0x00,0x00,0xfe,0xc6,0x06,0x06,0x0c,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00,
+/*8*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0x7c,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/*9*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0x7e,0x06,0x06,0x06,0x0c,0x78,0x00,0x00,0x00,0x00,
+/*:*/ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,
+/*;*/ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00,
+/*<*/ 0x00,0x00,0x00,0x06,0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x06,0x00,0x00,0x00,0x00,
+/*=*/ 0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*>*/ 0x00,0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x60,0x00,0x00,0x00,0x00,
+/*?*/ 0x00,0x00,0x7c,0xc6,0xc6,0x0c,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00,
+/*@*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xde,0xde,0xde,0xdc,0xc0,0x7c,0x00,0x00,0x00,0x00,
+/*A*/ 0x00,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+/*B*/ 0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x66,0x66,0x66,0x66,0xfc,0x00,0x00,0x00,0x00,
+/*C*/ 0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xc0,0xc0,0xc2,0x66,0x3c,0x00,0x00,0x00,0x00,
+/*D*/ 0x00,0x00,0xf8,0x6c,0x66,0x66,0x66,0x66,0x66,0x66,0x6c,0xf8,0x00,0x00,0x00,0x00,
+/*E*/ 0x00,0x00,0xfe,0x66,0x62,0x68,0x78,0x68,0x60,0x62,0x66,0xfe,0x00,0x00,0x00,0x00,
+/*F*/ 0x00,0x00,0xfe,0x66,0x62,0x68,0x78,0x68,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00,
+/*G*/ 0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xde,0xc6,0xc6,0x66,0x3a,0x00,0x00,0x00,0x00,
+/*H*/ 0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+/*I*/ 0x00,0x00,0x3c,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+/*J*/ 0x00,0x00,0x1e,0x0c,0x0c,0x0c,0x0c,0x0c,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00,0x00,
+/*K*/ 0x00,0x00,0xe6,0x66,0x66,0x6c,0x78,0x78,0x6c,0x66,0x66,0xe6,0x00,0x00,0x00,0x00,
+/*L*/ 0x00,0x00,0xf0,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x66,0xfe,0x00,0x00,0x00,0x00,
+/*M*/ 0x00,0x00,0xc3,0xe7,0xff,0xff,0xdb,0xc3,0xc3,0xc3,0xc3,0xc3,0x00,0x00,0x00,0x00,
+/*N*/ 0x00,0x00,0xc6,0xe6,0xf6,0xfe,0xde,0xce,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+/*O*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/*P*/ 0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x60,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00,
+/*Q*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xd6,0xde,0x7c,0x0c,0x0e,0x00,0x00,
+/*R*/ 0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x6c,0x66,0x66,0x66,0xe6,0x00,0x00,0x00,0x00,
+/*S*/ 0x00,0x00,0x7c,0xc6,0xc6,0x60,0x38,0x0c,0x06,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/*T*/ 0x00,0x00,0xff,0xdb,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+/*U*/ 0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/*V*/ 0x00,0x00,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x00,0x00,0x00,0x00,
+/*W*/ 0x00,0x00,0xc3,0xc3,0xc3,0xc3,0xc3,0xdb,0xdb,0xff,0x66,0x66,0x00,0x00,0x00,0x00,
+/*X*/ 0x00,0x00,0xc3,0xc3,0x66,0x3c,0x18,0x18,0x3c,0x66,0xc3,0xc3,0x00,0x00,0x00,0x00,
+/*Y*/ 0x00,0x00,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+/*Z*/ 0x00,0x00,0xff,0xc3,0x86,0x0c,0x18,0x30,0x60,0xc1,0xc3,0xff,0x00,0x00,0x00,0x00,
+/*[*/ 0x00,0x00,0x3c,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3c,0x00,0x00,0x00,0x00,
+/*\*/ 0x00,0x00,0x00,0x80,0xc0,0xe0,0x70,0x38,0x1c,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,
+/*]*/ 0x00,0x00,0x3c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3c,0x00,0x00,0x00,0x00,
+/*^*/ 0x10,0x38,0x6c,0xc6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*_*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,
+/*`*/ 0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/*a*/ 0x00,0x00,0x00,0x00,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+/*b*/ 0x00,0x00,0xe0,0x60,0x60,0x78,0x6c,0x66,0x66,0x66,0x66,0x7c,0x00,0x00,0x00,0x00,
+/*c*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xc0,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/*d*/ 0x00,0x00,0x1c,0x0c,0x0c,0x3c,0x6c,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+/*e*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/*f*/ 0x00,0x00,0x38,0x6c,0x64,0x60,0xf0,0x60,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00,
+/*g*/ 0x00,0x00,0x00,0x00,0x00,0x76,0xcc,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0xcc,0x78,0x00,
+/*h*/ 0x00,0x00,0xe0,0x60,0x60,0x6c,0x76,0x66,0x66,0x66,0x66,0xe6,0x00,0x00,0x00,0x00,
+/*i*/ 0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+/*j*/ 0x00,0x00,0x06,0x06,0x00,0x0e,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3c,0x00,
+/*k*/ 0x00,0x00,0xe0,0x60,0x60,0x66,0x6c,0x78,0x78,0x6c,0x66,0xe6,0x00,0x00,0x00,0x00,
+/*l*/ 0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+/*m*/ 0x00,0x00,0x00,0x00,0x00,0xe6,0xff,0xdb,0xdb,0xdb,0xdb,0xdb,0x00,0x00,0x00,0x00,
+/*n*/ 0x00,0x00,0x00,0x00,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00,
+/*o*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/*p*/ 0x00,0x00,0x00,0x00,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x7c,0x60,0x60,0xf0,0x00,
+/*q*/ 0x00,0x00,0x00,0x00,0x00,0x76,0xcc,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0x0c,0x1e,0x00,
+/*r*/ 0x00,0x00,0x00,0x00,0x00,0xdc,0x76,0x66,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00,
+/*s*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0x60,0x38,0x0c,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/*t*/ 0x00,0x00,0x10,0x30,0x30,0xfc,0x30,0x30,0x30,0x30,0x36,0x1c,0x00,0x00,0x00,0x00,
+/*u*/ 0x00,0x00,0x00,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+/*v*/ 0x00,0x00,0x00,0x00,0x00,0xc3,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x00,0x00,0x00,0x00,
+/*w*/ 0x00,0x00,0x00,0x00,0x00,0xc3,0xc3,0xc3,0xdb,0xdb,0xff,0x66,0x00,0x00,0x00,0x00,
+/*x*/ 0x00,0x00,0x00,0x00,0x00,0xc3,0x66,0x3c,0x18,0x3c,0x66,0xc3,0x00,0x00,0x00,0x00,
+/*y*/ 0x00,0x00,0x00,0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7e,0x06,0x0c,0xf8,0x00,
+/*z*/ 0x00,0x00,0x00,0x00,0x00,0xfe,0xcc,0x18,0x30,0x60,0xc6,0xfe,0x00,0x00,0x00,0x00,
+/*{*/ 0x00,0x00,0x0e,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0e,0x00,0x00,0x00,0x00,
+/*|*/ 0x00,0x00,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,
+/*}*/ 0x00,0x00,0x70,0x18,0x18,0x18,0x0e,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00,
+/*~*/ 0x00,0x00,0x76,0xdc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xc6,0xfe,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xc0,0xc2,0x66,0x3c,0x0c,0x06,0x7c,0x00,0x00,
+/* */ 0x00,0x00,0xcc,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x0c,0x18,0x30,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x10,0x38,0x6c,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0xcc,0x00,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x60,0x30,0x18,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x38,0x6c,0x38,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x3c,0x66,0x60,0x60,0x66,0x3c,0x0c,0x06,0x3c,0x00,0x00,0x00,
+/* */ 0x00,0x10,0x38,0x6c,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0xc6,0x00,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x60,0x30,0x18,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x66,0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x18,0x3c,0x66,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x60,0x30,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0xc6,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+/* */ 0x38,0x6c,0x38,0x00,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+/* */ 0x18,0x30,0x60,0x00,0xfe,0x66,0x60,0x7c,0x60,0x60,0x66,0xfe,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x6e,0x3b,0x1b,0x7e,0xd8,0xdc,0x77,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x3e,0x6c,0xcc,0xcc,0xfe,0xcc,0xcc,0xcc,0xcc,0xce,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x10,0x38,0x6c,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0xc6,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x60,0x30,0x18,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x30,0x78,0xcc,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x60,0x30,0x18,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0xc6,0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7e,0x06,0x0c,0x78,0x00,
+/* */ 0x00,0xc6,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0xc6,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x18,0x18,0x7e,0xc3,0xc0,0xc0,0xc0,0xc3,0x7e,0x18,0x18,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x38,0x6c,0x64,0x60,0xf0,0x60,0x60,0x60,0x60,0xe6,0xfc,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0xc3,0x66,0x3c,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x00,0x00,0x00,0x00,
+/* */ 0x00,0xfc,0x66,0x66,0x7c,0x62,0x66,0x6f,0x66,0x66,0x66,0xf3,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x0e,0x1b,0x18,0x18,0x18,0x7e,0x18,0x18,0x18,0x18,0x18,0xd8,0x70,0x00,0x00,
+/* */ 0x00,0x18,0x30,0x60,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x0c,0x18,0x30,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x18,0x30,0x60,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x18,0x30,0x60,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x76,0xdc,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00,
+/* */ 0x76,0xdc,0x00,0xc6,0xe6,0xf6,0xfe,0xde,0xce,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x3c,0x6c,0x6c,0x3e,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x38,0x6c,0x6c,0x38,0x00,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x60,0xc0,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0xc0,0xc0,0xc2,0xc6,0xcc,0x18,0x30,0x60,0xce,0x9b,0x06,0x0c,0x1f,0x00,0x00,
+/* */ 0x00,0xc0,0xc0,0xc2,0xc6,0xcc,0x18,0x30,0x66,0xce,0x96,0x3e,0x06,0x06,0x00,0x00,
+/* */ 0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x18,0x3c,0x3c,0x3c,0x18,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x36,0x6c,0xd8,0x6c,0x36,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0xd8,0x6c,0x36,0x6c,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,
+/* */ 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,
+/* */ 0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,
+/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+/* */ 0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0x36,0x36,0x36,0x36,0x36,0xf6,0x06,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+/* */ 0x00,0x00,0x00,0x00,0x00,0xfe,0x06,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+/* */ 0x36,0x36,0x36,0x36,0x36,0xf6,0x06,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+/* */ 0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x3f,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+/* */ 0x36,0x36,0x36,0x36,0x36,0xf7,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xf7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+/* */ 0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+/* */ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x36,0x36,0x36,0x36,0x36,0xf7,0x00,0xf7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+/* */ 0x18,0x18,0x18,0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xff,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+/* */ 0x18,0x18,0x18,0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+/* */ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
+/* */ 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+/* */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x76,0xdc,0xd8,0xd8,0xd8,0xdc,0x76,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x78,0xcc,0xcc,0xcc,0xd8,0xcc,0xc6,0xc6,0xc6,0xcc,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0xfe,0xc6,0xc6,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0xfe,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0xfe,0xc6,0x60,0x30,0x18,0x30,0x60,0xc6,0xfe,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x7e,0xd8,0xd8,0xd8,0xd8,0xd8,0x70,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x7c,0x60,0x60,0xc0,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x76,0xdc,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x7e,0x18,0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0x6c,0x38,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x38,0x6c,0xc6,0xc6,0xc6,0x6c,0x6c,0x6c,0x6c,0xee,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x1e,0x30,0x18,0x0c,0x3e,0x66,0x66,0x66,0x66,0x3c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x7e,0xdb,0xdb,0xdb,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x03,0x06,0x7e,0xdb,0xdb,0xf3,0x7e,0x60,0xc0,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x1c,0x30,0x60,0x60,0x7c,0x60,0x60,0x60,0x30,0x1c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0xfe,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00,0xff,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x00,0x7e,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x00,0x7e,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x0e,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xd8,0xd8,0xd8,0x70,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x7e,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x76,0xdc,0x00,0x76,0xdc,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x38,0x6c,0x6c,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0xec,0x6c,0x6c,0x3c,0x1c,0x00,0x00,0x00,0x00,
+/* */ 0x00,0xd8,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x70,0xd8,0x30,0x60,0xc8,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x00,0x00,0x00,0x00,0x00,
+/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+};
diff -u --recursive --new-file v2.1.108/linux/drivers/video/fonts.c linux/drivers/video/fonts.c
--- v2.1.108/linux/drivers/video/fonts.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/fonts.c	Fri Jul 10 15:18:31 1998
@@ -42,12 +42,19 @@
 extern int fontwidth_6x11, fontheight_6x11;
 extern u8 fontdata_6x11[];
 
+/* SUN8x16 */
+extern char fontname_sun8x16[];
+extern int fontwidth_sun8x16, fontheight_sun8x16;
+extern u8 fontdata_sun8x16[];
+
+
 
    /*
     *    Font Descriptor Array
     */
 
 struct softfontdesc {
+   int idx;
    char *name;
    int *width;
    int *height;
@@ -58,13 +65,19 @@
 #define VGA8x16_IDX	1
 #define PEARL8x8_IDX	2
 #define VGA6x11_IDX	3
+#define SUN8x16_IDX	4
 
 static struct softfontdesc softfonts[] = {
-   { fontname_8x8, &fontwidth_8x8, &fontheight_8x8, fontdata_8x8 },
-   { fontname_8x16, &fontwidth_8x16, &fontheight_8x16, fontdata_8x16 },
-   { fontname_pearl8x8, &fontwidth_pearl8x8, &fontheight_pearl8x8,
+   { VGA8x8_IDX, fontname_8x8, &fontwidth_8x8, &fontheight_8x8, fontdata_8x8 },
+#ifndef __sparc__
+   { VGA8x16_IDX, fontname_8x16, &fontwidth_8x16, &fontheight_8x16, fontdata_8x16 },
+   { PEARL8x8_IDX, fontname_pearl8x8, &fontwidth_pearl8x8, &fontheight_pearl8x8,
      fontdata_pearl8x8 },
-   { fontname_6x11, &fontwidth_6x11, &fontheight_6x11, fontdata_6x11 },
+   { VGA6x11_IDX, fontname_6x11, &fontwidth_6x11, &fontheight_6x11, fontdata_6x11 },
+#else
+   { SUN8x16_IDX, fontname_sun8x16, &fontwidth_sun8x16, &fontheight_sun8x16, 
+     fontdata_sun8x16 },
+#endif
 };
 
 static unsigned int numsoftfonts = sizeof(softfonts)/sizeof(*softfonts);
@@ -99,7 +112,7 @@
 void getdefaultfont(int xres, int yres, char *name[], int *width, int *height,
                     u8 *data[])
 {
-    int i;
+    int i, j;
     
     if (yres < 400) {
 	i = VGA8x8_IDX;
@@ -120,12 +133,20 @@
     }
 #endif
 
+#ifdef __sparc__
+    i = SUN8x16_IDX;
+#endif
+
+    for (j = 0; j < numsoftfonts; j++)
+        if (softfonts[j].idx == i)
+            break;
+
     if (name)
-	*name = softfonts[i].name;
+	*name = softfonts[j].name;
     if (width)
-	*width = *softfonts[i].width;
+	*width = *softfonts[j].width;
     if (height)
-	*height = *softfonts[i].height;
+	*height = *softfonts[j].height;
     if (data)
-	*data = softfonts[i].data;
+	*data = softfonts[j].data;
 }
diff -u --recursive --new-file v2.1.108/linux/drivers/video/hpfb.c linux/drivers/video/hpfb.c
--- v2.1.108/linux/drivers/video/hpfb.c	Wed Jul  1 19:38:55 1998
+++ linux/drivers/video/hpfb.c	Fri Jul 10 15:18:31 1998
@@ -245,7 +245,7 @@
 	/* Not supported */
 }
 
-static int hpfb_open(struct fb_info *info)
+static int hpfb_open(struct fb_info *info, int user)
 {
 	/*
 	 * Nothing, only a usage count for the moment
@@ -280,7 +280,7 @@
 	display->dispsw = &fbcon_cfb8;
 }
 
-static int hpfb_release(struct fb_info *info)
+static int hpfb_release(struct fb_info *info, int user)
 {
 	MOD_DEC_USE_COUNT;
 	return(0);
@@ -304,7 +304,6 @@
 __initfunc(int hpfb_init_one(unsigned long base))
 {
 	unsigned long fboff;
-	int err;
 
 	fboff = (readb(base + TOPCAT_FBOMSB) << 8) 
 		| readb(base + TOPCAT_FBOLSB);
@@ -363,12 +362,11 @@
 	fb_info.blank = &hpfb_blank;
 	do_fb_set_var(&hpfb_defined, 1);
 
-	err = register_framebuffer(&fb_info);
-	if (err < 0)
-		return 1;
-
 	hpfb_get_var(&disp.var, -1, &fb_info);
 	hpfb_set_disp(-1);
+
+	if (register_framebuffer(&fb_info) < 0)
+		return 1;
 
 	return 0;
 }
diff -u --recursive --new-file v2.1.108/linux/drivers/video/macfb.c linux/drivers/video/macfb.c
--- v2.1.108/linux/drivers/video/macfb.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/macfb.c	Fri Jul 10 15:18:31 1998
@@ -76,7 +76,7 @@
 	 * Open/Release the frame buffer device
 	 */
 
-static int macfb_open(struct fb_info *info)
+static int macfb_open(struct fb_info *info, int user)
 {
 	/*
 	 * Nothing, only a usage count for the moment
@@ -85,7 +85,7 @@
 	return(0);
 }
 
-static int macfb_release(struct fb_info *info)
+static int macfb_release(struct fb_info *info, int user)
 {
 	MOD_DEC_USE_COUNT;
 	return(0);
@@ -374,13 +374,12 @@
 	NULL
 };
 
-__initfunc(unsigned long macfb_init(unsigned long mem_start))
+__initfunc(void macfb_init(void))
 {
 	/* nubus_remap the video .. */
-	int err;
 
 	if (!MACH_IS_MAC) 
-		return mem_start;
+		return;
 
 	mac_xres=mac_bi_data.dimensions&0xFFFF;
 	mac_yres=(mac_bi_data.dimensions&0xFFFF0000)>>16;
@@ -419,13 +418,6 @@
 	fb_info.blank=&macfb_blank;
 	do_fb_set_var(&macfb_defined,1);
 
-	err=register_framebuffer(&fb_info);
-	if(err<0)
-	{
-		mac_boom(6);
-		return mem_start;
-	}
-
 	macfb_get_var(&disp.var, -1, &fb_info);
 	macfb_set_disp(-1);
 
@@ -435,10 +427,14 @@
 	 
 	register_nubus_device(&nb_video);
 
+	if (register_framebuffer(&fb_info) < 0)
+	{
+		mac_boom(6);
+		return;
+	}
+
 	printk("fb%d: %s frame buffer device using %ldK of video memory\n",
 	       GET_FB_IDX(fb_info.node), fb_info.modename, mac_videosize>>10);
-
-	return mem_start;
 }
 
 #if 0
diff -u --recursive --new-file v2.1.108/linux/drivers/video/mdafb.c linux/drivers/video/mdafb.c
--- v2.1.108/linux/drivers/video/mdafb.c	Wed Jun 24 22:54:07 1998
+++ linux/drivers/video/mdafb.c	Fri Jul 10 15:18:31 1998
@@ -107,32 +107,31 @@
 	restore_flags(flags);
 }
 
-static inline void mda_set_origin(unsigned short offset)
+static inline void mda_set_origin(unsigned short location)
 {
-	write_mda(12, offset);
+	write_mda(12, location >> 1);
 }
 
-    /*
-     *  Move hardware mda cursor
-     */
-    
-static int mda_write_cursor (int location) 
+static inline void mda_set_cursor(int location) 
 {
 	write_mda(14, location >> 1);
-	return 0;
 }
 
 
+    /*
+     *  Move hardware mda cursor
+     */
+    
 void fbcon_mdafb_cursor(struct display *p, int mode, int x, int y)
 {
 	switch (mode) {
 		case CM_ERASE:
-			mda_write_cursor(mda_video_mem_len - 1);
+			mda_set_cursor(mda_video_mem_len - 1);
 			break;
 
 		case CM_MOVE:
 		case CM_DRAW:
-			mda_write_cursor(y*p->next_line + (x << 1));
+			mda_set_cursor(y*p->next_line + (x << 1));
 			break;
 	}
 }
@@ -152,13 +151,13 @@
      *  Open/Release the frame buffer device
      */
 
-static int mdafb_open(struct fb_info *info)
+static int mdafb_open(struct fb_info *info, int user)
 {
 	MOD_INC_USE_COUNT;
 	return 0;
 }
 
-static int mdafb_release(struct fb_info *info)
+static int mdafb_release(struct fb_info *info, int user)
 {
 	MOD_DEC_USE_COUNT;
 	return 0;
@@ -215,7 +214,7 @@
 
     if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
 	display->var = *var;
-	mda_set_origin(var->yoffset/mda_font_height*fb_fix.line_length/2);
+	mda_set_origin(var->yoffset/mda_font_height*fb_fix.line_length);
     }
 
     return 0;
@@ -234,7 +233,7 @@
 	if (var->xoffset || var->yoffset+var->yres > var->yres_virtual)
 		return -EINVAL;
 
-	mda_set_origin(var->yoffset/mda_font_height*fb_fix.line_length/2);
+	mda_set_origin(var->yoffset/mda_font_height*fb_fix.line_length);
 	return 0;
 }
 
@@ -295,9 +294,8 @@
      *  Initialisation
      */
 
-__initfunc(unsigned long mdafb_init(unsigned long mem_start))
+__initfunc(void mdafb_init(void))
 {
-    int err;
     u16 saved;
     u16 *p;
 
@@ -322,19 +320,19 @@
     mda_writew(0xAA55, p);
     if (mda_readw(p) != 0xAA55) {
 	mda_writew(saved, p);
-	return mem_start;
+	return;
     }
     mda_writew(0x55AA, p);
     if (mda_readw(p) != 0x55AA) {
 	mda_writew(saved, p);
-	return mem_start;
+	return;
     }
     mda_writew(saved, p);
 
     fb_fix.smem_start = (char *) mda_video_mem_base;
     fb_fix.smem_len = mda_video_mem_len;
-    fb_fix.type = FB_TYPE_VGA_TEXT;
-    fb_fix.type_aux = 0;
+    fb_fix.type = FB_TYPE_TEXT;
+    fb_fix.type_aux = FB_AUX_TEXT_MDA;
     fb_fix.visual = FB_VISUAL_PSEUDOCOLOR;
     fb_fix.ypanstep = mda_font_height;
     fb_fix.xpanstep = 0;
@@ -414,17 +412,13 @@
     fb_info.updatevar = &mdafbcon_updatevar;
     fb_info.blank = &mdafbcon_blank;
 
-    err = register_framebuffer(&fb_info);
-
-    if (err < 0)
-	return mem_start;
-
     mdafb_set_var(&fb_var, -1, &fb_info);
 
+    if (register_framebuffer(&fb_info) < 0)
+	return;
+
     printk("fb%d: MDA frame buffer device, using %dK of video memory\n",
 	   GET_FB_IDX(fb_info.node), fb_fix.smem_len>>10);
-
-    return mem_start;
 }
 
 __initfunc(void mdafb_setup(char *options, int *ints))
@@ -444,7 +438,7 @@
 		/* hardware scrolling */
 
 		mda_set_origin(var->yoffset / mda_font_height *
-			fb_fix.line_length / 2);
+			fb_fix.line_length);
 	}
 
 	return 0;
diff -u --recursive --new-file v2.1.108/linux/drivers/video/offb.c linux/drivers/video/offb.c
--- v2.1.108/linux/drivers/video/offb.c	Wed Jun 24 22:54:08 1998
+++ linux/drivers/video/offb.c	Fri Jul 10 15:18:31 1998
@@ -60,11 +60,11 @@
      *  Interface used by the world
      */
 
-unsigned long offb_init(unsigned long mem_start);
+void offb_init(void);
 void offb_setup(char *options, int *ints);
 
-static int offb_open(struct fb_info *info);
-static int offb_release(struct fb_info *info);
+static int offb_open(struct fb_info *info, int user);
+static int offb_release(struct fb_info *info, int user);
 static int offb_get_fix(struct fb_fix_screeninfo *fix, int con,
 			struct fb_info *info);
 static int offb_get_var(struct fb_var_screeninfo *var, int con,
@@ -123,7 +123,7 @@
      *  Open/Release the frame buffer device
      */
 
-static int offb_open(struct fb_info *info)
+static int offb_open(struct fb_info *info, int user)
 {
     /*
      *  Nothing, only a usage count for the moment
@@ -133,7 +133,7 @@
     return(0);
 }
 
-static int offb_release(struct fb_info *info)
+static int offb_release(struct fb_info *info, int user)
 {
     MOD_DEC_USE_COUNT;
     return(0);
@@ -275,8 +275,7 @@
 
 
 #ifdef CONFIG_FB_ATY
-extern unsigned long atyfb_of_init(unsigned long mem_start,
-				   struct device_node *dp);
+extern void atyfb_of_init(struct device_node *dp);
 
 static const char *aty_names[] = {
     "ATY,mach64", "ATY,XCLAIM", "ATY,264VT", "ATY,mach64ii", "ATY,264GT-B", 
@@ -285,7 +284,7 @@
 };
 #endif /* CONFIG_FB_ATY */
 #ifdef CONFIG_FB_S3TRIO
-extern void s3triofb_init_of(unsigned long mem_start, struct device_node *dp);
+extern void s3triofb_init_of(struct device_node *dp);
 #endif /* CONFIG_FB_S3TRIO */
 
 
@@ -293,10 +292,10 @@
      *  Initialisation
      */
 
-__initfunc(unsigned long offb_init(unsigned long mem_start))
+__initfunc(void offb_init(void))
 {
     struct device_node *dp;
-    int dpy, i, err, *pp, len;
+    int dpy, i, *pp, len;
     unsigned *up, address;
     struct fb_fix_screeninfo *fix;
     struct fb_var_screeninfo *var;
@@ -313,7 +312,7 @@
 		if (!strcmp(dp->name, aty_names[i]))
 		    break;
 	    if (i < sizeof(aty_names)/sizeof(*aty_names)) {
-		mem_start = atyfb_of_init(mem_start, dp);
+		atyfb_of_init(dp);
 		continue;
 	    }
 #endif /* CONFIG_FB_ATY */
@@ -428,12 +427,6 @@
 	info->info.updatevar = &offbcon_updatevar;
 	info->info.blank = &offbcon_blank;
 
-	err = register_framebuffer(&info->info);
-	if (err < 0) {
-	    kfree(info);
-	    return mem_start;
-	}
-
 	for (i = 0; i < 16; i++) {
 	    int j = color_table[i];
 	    info->palette[i].red = default_red[j];
@@ -442,6 +435,11 @@
 	}
 	offb_set_var(var, -1, &info->info);
 
+	if (register_framebuffer(&info->info) < 0) {
+	    kfree(info);
+	    return;
+	}
+
 	printk("fb%d: Open Firmware frame buffer device on %s\n",
 	       GET_FB_IDX(info->info.node), dp->full_name);
 
@@ -468,7 +466,6 @@
 	}
 #endif /* CONFIG_FB_COMPAT_XPMAC) */
     }
-    return mem_start;
 }
 
 
diff -u --recursive --new-file v2.1.108/linux/drivers/video/promfb.c linux/drivers/video/promfb.c
--- v2.1.108/linux/drivers/video/promfb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/promfb.c	Mon Jul 13 12:43:04 1998
@@ -0,0 +1,487 @@
+/* $Id: promfb.c,v 1.3 1998/07/08 07:36:49 ecd Exp $
+ *
+ * Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+#include <linux/fb.h>
+
+#include "promfb.h"
+
+extern int prom_stdout;
+
+#define PROM_FONT_HEIGHT	22
+#define PROM_FONT_WIDTH		12
+
+void promfb_init(void);
+void promfb_setup(char *options, int *ints);
+
+
+static int
+promfb_open(struct fb_info *info, int user)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int
+promfb_release(struct fb_info *info, int user)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int
+promfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
+{
+	struct fb_info_promfb *fb = promfbinfo(info);
+
+	memcpy(fix, &fb->fix, sizeof(struct fb_fix_screeninfo));
+	return 0;
+}
+
+static int
+promfb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+	struct fb_info_promfb *fb = promfbinfo(info);
+
+	memcpy(var, &fb->var, sizeof(struct fb_var_screeninfo));
+	return 0;
+}
+
+static int
+promfb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+	return -EINVAL;
+}
+
+static int
+promfb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
+{
+	return 0;
+}
+
+static int
+promfb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
+{
+	return -EINVAL;
+}
+
+static int
+promfb_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+	if (var->xoffset || var->yoffset)
+		return -EINVAL;
+	return 0;
+}
+
+static int
+promfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+	     u_long arg, int con, struct fb_info *info)
+{
+	return -EINVAL;
+}
+
+static int
+promfb_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
+{
+	return -EINVAL;
+}
+
+static struct fb_ops promfb_ops = {
+	promfb_open,
+	promfb_release,
+	promfb_get_fix,
+	promfb_get_var,
+	promfb_set_var,
+	promfb_get_cmap,
+	promfb_set_cmap,
+	promfb_pan_display,
+	promfb_ioctl,
+	promfb_mmap
+};
+
+
+static int
+promfbcon_switch(int con, struct fb_info *info)
+{
+	return 0;
+}
+
+static int
+promfbcon_updatevar(int con, struct fb_info *info)
+{
+	return 0;
+}
+
+static void
+promfbcon_blank(int blank, struct fb_info *info)
+{
+	/* nothing */
+}
+
+
+static void
+prom_start(struct fb_info_promfb *fb, struct display *d)
+{
+	unsigned short *dst = fb->data + d->next_line * fb->cury + fb->curx;
+
+	if (fb->curx == fb->maxx && fb->cury == fb->maxy)
+		return;
+
+	if (*dst & 0x0800)
+		prom_printf("\033[7m%c\033[m\033[%d;%dH",
+			    *dst, fb->cury + 1, fb->curx + 1);
+	else
+		prom_printf("%c\033[%d;%dH",
+			    *dst, fb->cury + 1, fb->curx + 1);
+}
+
+static void
+prom_stop(struct fb_info_promfb *fb, struct display *d)
+{
+	unsigned short *dst = fb->data + d->next_line * fb->cury + fb->curx;
+
+	if (fb->curx == fb->maxx && fb->cury == fb->maxy)
+		return;
+
+	if (*dst & 0x0800)
+		prom_printf("\033[%d;%dH%c\033[%d;%dH",
+			    fb->cury + 1, fb->curx + 1, *dst,
+			    fb->cury + 1, fb->curx + 1);
+	else
+		prom_printf("\033[%d;%dH\033[7m%c\033[m\033[%d;%dH",
+			    fb->cury + 1, fb->curx + 1, *dst,
+			    fb->cury + 1, fb->curx + 1);
+}
+
+static void
+fbcon_prom_setup(struct display *d)
+{
+	d->next_line = d->line_length;
+	d->next_plane = 0;
+}
+
+static void
+fbcon_prom_putc(struct vc_data *con, struct display *d, int c, int y, int x)
+{
+	struct fb_info_promfb *fb = promfbinfod(d);
+	unsigned short *dst;
+
+	dst = fb->data + y * d->line_length + x;
+	if (*dst != (c & 0xffff)) {
+		*dst = c & 0xffff;
+
+		prom_start(fb, d);
+
+		if (fb->curx != x || fb->cury != y) {
+			prom_printf("\033[%d;%dH", y + 1, x + 1);
+			fb->curx = x;
+			fb->cury = y;
+		}
+
+		if (x == fb->maxx && y == fb->maxy)
+			/* Sorry */ return;
+
+		if (c & 0x0800)
+			prom_printf("\033[7m%c\033[m", c);
+		else
+			prom_putchar(c);
+
+		prom_stop(fb, d);
+	}
+}
+
+static void
+fbcon_prom_putcs(struct vc_data *con, struct display *d,
+		 const unsigned short *s, int count, int y, int x)
+{
+	struct fb_info_promfb *fb = promfbinfod(d);
+	unsigned short *dst;
+	unsigned short attr = *s;
+	int i;
+
+	dst = fb->data + y * d->line_length + x;
+
+	prom_start(fb, d);
+
+	if (attr & 0x0800)
+		prom_printf("\033[7m");
+
+	for (i = 0; i < count; i++) {
+		if (*dst != *s) {
+			*dst = *s;
+
+			if (fb->curx != x + i || fb->cury != y) {
+				prom_printf("\033[%d;%dH", y + 1, x + i + 1);
+				fb->curx = x + i;
+				fb->cury = y;
+			}
+
+			if (fb->curx == fb->maxx && fb->cury == fb->maxy)
+				/* Sorry */ goto out;
+
+			prom_putchar(*s);
+
+			if (i < count - 1)
+				fb->curx++;
+		}
+		dst++;
+		s++;
+	}
+out:
+	if (attr & 0x0800)
+		prom_printf("\033[m");
+
+	prom_stop(fb, d);
+}
+
+static void
+fbcon_prom_clear(struct vc_data *con, struct display *d, int sy, int sx,
+		 int h, int w)
+{
+	int x, y;
+
+	for (y = sy; y < sy + h; y++)
+		for (x = sx; x < sx + w; x++)
+			fbcon_prom_putc(con, d, con->vc_video_erase_char, y, x);
+}
+
+static void
+fbcon_prom_bmove(struct display *d, int sy, int sx, int dy, int dx,
+		 int h, int w)
+{
+	struct fb_info_promfb *fb = promfbinfod(d);
+	int linesize = d->next_line;
+	unsigned short *src, *dst;
+	int x, y;
+
+	if (dx == 0 && sx == 0 && dy == 0 && sy == 1 &&
+	    w == linesize && h == fb->maxy) {
+		memcpy(fb->data, fb->data + linesize, 2 * linesize * fb->maxy);
+		prom_start(fb, d);
+		prom_printf("\033[%d;%dH", fb->maxy + 1, fb->maxx + 1);
+		prom_putchar(*(fb->data + linesize * fb->maxy + fb->maxx));
+		fb->curx = 0;
+		fb->cury = fb->maxy;
+		prom_stop(fb, d);
+	} else if (dy < sy || (dy == sy && dx < sx)) {
+		src = fb->data + sy * linesize + sx;
+		dst = fb->data + dy * linesize + dx;
+		for (y = dy; y < dy + h; y++) {
+			for (x = dx; x < dx + w; x++) {
+				if (*src != *dst)
+					fbcon_prom_putc(d->conp, d, *src, y, x);
+				src++;
+				dst++;
+			}
+		}
+	} else {
+		src = fb->data + (sy + h - 1) * linesize + sx + w - 1;
+		dst = fb->data + (dy + h - 1) * linesize + dx + w - 1;
+		for (y = dy + h - 1; y >= dy; y--) {
+			for (x = dx + w - 1; x >= dx; x--) {
+				if (*src != *dst)
+					fbcon_prom_putc(d->conp, d, *src, y, x);
+				src--;
+				dst--;
+			}
+		}
+	}
+}
+
+static void
+fbcon_prom_revc(struct display *d, int x, int y)
+{
+	struct fb_info_promfb *fb = promfbinfod(d);
+	unsigned short *dst = fb->data + y * d->next_line + x;
+	unsigned short val = *dst ^ 0x0800;
+
+	fbcon_prom_putc(d->conp, d, val, y, x);
+}
+
+static void
+fbcon_prom_cursor(struct display *d, int mode, int x, int y)
+{
+	struct fb_info_promfb *fb = promfbinfod(d);
+
+	switch (mode) {
+		case CM_ERASE:
+			break;
+
+		case CM_MOVE:
+		case CM_DRAW:
+			prom_start(fb, d);
+			if (fb->curx != x || fb->cury != y) {
+				prom_printf("\033[%d;%dH", y + 1, x + 1);
+				fb->curx = x;
+				fb->cury = y;
+			}
+			break;
+	}
+}
+
+static struct display_switch fbcon_promfb = {
+	fbcon_prom_setup,
+	fbcon_prom_bmove,
+	fbcon_prom_clear,
+	fbcon_prom_putc,
+	fbcon_prom_putcs,
+	fbcon_prom_revc,
+	fbcon_prom_cursor
+};
+
+
+struct promfb_size {
+	int xres, yres;
+	int width, height;
+	int x_margin, y_margin;
+} promfb_sizes[] __initdata = {
+	{ 1024, 768, 80, 34, 32, 10 },
+	{ 80 * PROM_FONT_WIDTH, 24 * PROM_FONT_HEIGHT, 80, 24, 0, 0 },
+	{ 0 }
+};
+
+__initfunc(void promfb_init(void))
+{
+	extern int con_is_present(void);
+	struct fb_info_promfb *fb;
+	struct fb_fix_screeninfo *fix;
+	struct fb_var_screeninfo *var;
+	struct display *disp;
+	struct promfb_size *size;
+	int i, w, h;
+
+	if (!con_is_present())
+		return;
+
+	fb = kmalloc(sizeof(struct fb_info_promfb), GFP_ATOMIC);
+	if (!fb) {
+		prom_printf("Could not allocate promfb structure\n");
+		return;
+	}
+
+	memset(fb, 0, sizeof(struct fb_info_promfb));
+	fix = &fb->fix;
+	var = &fb->var;
+	disp = &fb->disp;
+
+	fb->node = prom_inst2pkg(prom_stdout);
+	w = prom_getintdefault(fb->node, "width", 80 * PROM_FONT_WIDTH);
+	h = prom_getintdefault(fb->node, "height", 24 * PROM_FONT_HEIGHT);
+
+	size = promfb_sizes;
+	while (size->xres) {
+		if (size->xres == w && size->yres == h)
+			break;
+		size++;
+	}
+	if (!size->xres) {
+		size->xres = w;
+		size->yres = h;
+		size->width = w / PROM_FONT_WIDTH;
+		size->height = h / PROM_FONT_HEIGHT;
+		size->x_margin = (w - size->width * PROM_FONT_WIDTH) >> 1;
+		size->y_margin = (h - size->width * PROM_FONT_HEIGHT) >> 1;
+	}
+
+	fb->data = kmalloc(2 * size->width * size->height, GFP_ATOMIC);
+	if (!fb->data) {
+		kfree(fb);
+		return;
+	}
+
+	fb->maxx = size->width - 1;
+	fb->maxy = size->height - 1;
+
+	strcpy(fb->info.modename, "PROM pseudo");
+	fb->info.node = -1;
+	fb->info.fbops = &promfb_ops;
+	fb->info.disp = disp;
+	fb->info.fontname[0] = '\0';
+	fb->info.changevar = NULL;
+	fb->info.switch_con = promfbcon_switch;
+	fb->info.updatevar = promfbcon_updatevar;
+	fb->info.blank = promfbcon_blank;
+
+	strcpy(fix->id, "PROM pseudo");
+	fix->type = FB_TYPE_TEXT;
+	fix->type_aux = FB_AUX_TEXT_MDA;
+	fix->visual = FB_VISUAL_MONO01;
+	fix->xpanstep = 0;
+	fix->ypanstep = 16;
+	fix->ywrapstep = 0;
+	fix->line_length = size->width;
+	fix->accel = FB_ACCEL_NONE;
+
+	var->xres = size->width * 8;
+	var->yres = size->height * 16;
+	var->xres_virtual = var->xres;
+	var->yres_virtual = var->yres;
+	var->bits_per_pixel = 1;
+	var->xoffset = 0;
+	var->yoffset = 0;
+	var->activate = 0;
+	var->height = -1;
+	var->width = -1;
+	var->accel_flags = FB_ACCELF_TEXT;
+	var->vmode = FB_VMODE_NONINTERLACED;
+
+	disp->var = *var;
+	disp->visual = fix->visual;
+	disp->type = fix->type;
+	disp->type_aux = fix->type_aux;
+	disp->ypanstep = fix->ypanstep;
+	disp->ywrapstep = fix->ywrapstep;
+	disp->line_length = fix->line_length;
+	disp->can_soft_blank = 1;
+	disp->dispsw = &fbcon_promfb;
+
+	for (i = 0; i < fb->maxy * size->width + fb->maxx; i++)
+		fb->data[i] = ' ';
+	prom_printf("\033[H\033[J");
+
+	if (register_framebuffer(&fb->info) < 0) {
+		kfree(fb->data);
+		kfree(fb);
+		return;
+	}
+
+	printk("fb%d: %s frame buffer device\n", GET_FB_IDX(fb->info.node),
+	       fb->info.modename);
+	MOD_INC_USE_COUNT;
+}
+
+__initfunc(void promfb_setup(char *options, int *ints)) {}
+
+void
+promfb_cleanup(struct fb_info *info)
+{
+	struct fb_info_promfb *fb = promfbinfo(info);
+
+	unregister_framebuffer(info);
+	kfree(fb->data);
+	kfree(fb);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	promfb_init();
+	return 0
+}
+
+void cleanup_module(void)
+{
+	promfb_cleanup();
+}
+#endif /* MODULE */
+
diff -u --recursive --new-file v2.1.108/linux/drivers/video/promfb.h linux/drivers/video/promfb.h
--- v2.1.108/linux/drivers/video/promfb.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/promfb.h	Fri Jul 10 15:18:31 1998
@@ -0,0 +1,28 @@
+/* $Id: promfb.h,v 1.1 1998/07/05 22:50:43 ecd Exp $
+ *
+ * Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
+ */
+
+#ifndef _PROMFB_H
+#define _PROMFB_H 1
+
+#include <asm/oplib.h>
+
+#include "fbcon.h"
+
+struct fb_info_promfb {
+	struct fb_info info;
+	struct fb_fix_screeninfo fix;
+	struct fb_var_screeninfo var;
+	struct display disp;
+	struct display_switch dispsw;
+	int node;
+	unsigned short *data;
+	int curx, cury;
+	int maxx, maxy;
+};
+
+#define promfbinfo(info)	((struct fb_info_promfb *)(info))
+#define promfbinfod(disp)	((struct fb_info_promfb *)(disp->fb_info))
+
+#endif /* !(_PROM_FB_H) */
diff -u --recursive --new-file v2.1.108/linux/drivers/video/retz3fb.c linux/drivers/video/retz3fb.c
--- v2.1.108/linux/drivers/video/retz3fb.c	Wed Jun 24 22:54:08 1998
+++ linux/drivers/video/retz3fb.c	Fri Jul 10 15:18:31 1998
@@ -257,8 +257,8 @@
 
 void retz3fb_setup(char *options, int *ints);
 
-static int retz3fb_open(struct fb_info *info);
-static int retz3fb_release(struct fb_info *info);
+static int retz3fb_open(struct fb_info *info, int user);
+static int retz3fb_release(struct fb_info *info, int user);
 static int retz3fb_get_fix(struct fb_fix_screeninfo *fix, int con,
 			   struct fb_info *info);
 static int retz3fb_get_var(struct fb_var_screeninfo *var, int con,
@@ -280,7 +280,7 @@
  *    Interface to the low level console driver
  */
 
-unsigned long retz3fb_init(unsigned long mem_start);
+void retz3fb_init(void);
 static int z3fb_switch(int con, struct fb_info *info);
 static int z3fb_updatevar(int con, struct fb_info *info);
 static void z3fb_blank(int blank, struct fb_info *info);
@@ -1201,7 +1201,7 @@
  *    Open/Release the frame buffer device
  */
 
-static int retz3fb_open(struct fb_info *info)
+static int retz3fb_open(struct fb_info *info, int user)
 {
 	/*
 	 * Nothing, only a usage count for the moment
@@ -1211,7 +1211,7 @@
 	return 0;
 }
 
-static int retz3fb_release(struct fb_info *info)
+static int retz3fb_release(struct fb_info *info, int user)
 {
 	MOD_DEC_USE_COUNT;
 	return 0;
@@ -1487,9 +1487,8 @@
  *    Initialization
  */
 
-__initfunc(unsigned long retz3fb_init(unsigned long mem_start))
+__initfunc(void retz3fb_init(void))
 {
-	int err;
 	unsigned long board_addr, board_size;
 	unsigned int key;
 	const struct ConfigDev *cd;
@@ -1497,7 +1496,7 @@
 	struct retz3fb_par par;
 
 	if (!(key = zorro_find(ZORRO_PROD_MACROSYSTEMS_RETINA_Z3, 0, 0)))
-		return mem_start;
+		return;
 
 	cd = zorro_get_board (key);
 	zorro_config_board (key, 0);
@@ -1505,7 +1504,7 @@
 	board_size = (unsigned long)cd->cd_BoardSize;
 
 	z3_mem = kernel_map (board_addr, board_size,
-			     KERNELMAP_NOCACHE_SER, &mem_start);
+			     KERNELMAP_NOCACHE_SER, NULL);
 
 	z3_regs = (char*) z3_mem;
 	z3_fbmem = z3_mem + VIDEO_MEM_OFFSET;
@@ -1527,10 +1526,6 @@
 	fb_info.updatevar = &z3fb_updatevar;
 	fb_info.blank = &z3fb_blank;
 
-	err = register_framebuffer(&fb_info);
-	if (err < 0)
-		return mem_start;
-
 	if (z3fb_mode == -1)
 		retz3fb_default = retz3fb_predefined[0].var;
 
@@ -1544,13 +1539,14 @@
 
 	do_install_cmap(0, &fb_info);
 
+	if (register_framebuffer(&fb_info) < 0)
+		return;
+
 	printk("fb%d: %s frame buffer device, using %ldK of video memory\n",
 	       GET_FB_IDX(fb_info.node), fb_info.modename, z3_size>>10);
 
 	/* TODO: This driver cannot be unloaded yet */
 	MOD_INC_USE_COUNT;
-
-	return mem_start;
 }
 
 
@@ -1613,7 +1609,8 @@
 #ifdef MODULE
 int init_module(void)
 {
-	return(retz3fb_init(NULL));
+	retz3fb_init();
+	return 0;
 }
 
 void cleanup_module(void)
diff -u --recursive --new-file v2.1.108/linux/drivers/video/sbusfb.c linux/drivers/video/sbusfb.c
--- v2.1.108/linux/drivers/video/sbusfb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/sbusfb.c	Fri Jul 10 15:18:32 1998
@@ -0,0 +1,1012 @@
+/*
+ *  linux/drivers/video/sbusfb.c -- SBUS or UPA based frame buffer device
+ *
+ *	Copyright (C) 1998 Jakub Jelinek
+ *
+ *  This driver is partly based on the Open Firmware console driver
+ *
+ *	Copyright (C) 1997 Geert Uytterhoeven
+ *
+ *  and SPARC console subsystem
+ *
+ *      Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
+ *      Copyright (C) 1995-1997 David S. Miller (davem@caip.rutgers.edu)
+ *      Copyright (C) 1995-1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *      Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
+ *      Copyright (C) 1996-1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ *      Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ *
+ *  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.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/selection.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/kd.h>
+#include <linux/vt_kern.h>
+
+#include <asm/uaccess.h>
+
+#include "sbusfb.h"
+
+    /*
+     *  Interface used by the world
+     */
+
+void sbusfb_init(void);
+void sbusfb_setup(char *options, int *ints);
+
+static int currcon;
+static int nomargins __initdata = 0;
+static struct {
+	int depth;
+	int xres, yres;
+	int x_margin, y_margin;
+} def_margins [] __initdata = {
+	{ 8, 1280, 1024, 64, 80 },
+	{ 8, 1152, 1024, 64, 80 },
+	{ 8, 1152, 900,  64, 18 },
+	{ 8, 1024, 768,  0,  0 },
+	{ 8, 800, 600, 16, 12 },
+	{ 8, 640, 480, 0, 0 },
+	{ 1, 1152, 900,  8,  18 },
+	{ 0 },
+};
+
+static int sbusfb_open(struct fb_info *info, int user);
+static int sbusfb_release(struct fb_info *info, int user);
+static int sbusfb_mmap(struct fb_info *info, struct file *file, 
+			struct vm_area_struct *vma);
+static int sbusfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			struct fb_info *info);
+static int sbusfb_get_var(struct fb_var_screeninfo *var, int con,
+			struct fb_info *info);
+static int sbusfb_set_var(struct fb_var_screeninfo *var, int con,
+			struct fb_info *info);
+static int sbusfb_pan_display(struct fb_var_screeninfo *var, int con,
+			struct fb_info *info);
+static int sbusfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			struct fb_info *info);
+static int sbusfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			struct fb_info *info);
+static int sbusfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+			    u_long arg, int con, struct fb_info *info);
+static void sbusfb_cursor(struct display *p, int mode, int x, int y);
+static void sbusfb_clear_margin(struct display *p, int s);
+extern int io_remap_page_range(unsigned long from, unsigned long offset, 
+			unsigned long size, pgprot_t prot, int space);
+			    
+
+    /*
+     *  Interface to the low level console driver
+     */
+
+static int sbusfbcon_switch(int con, struct fb_info *info);
+static int sbusfbcon_updatevar(int con, struct fb_info *info);
+static void sbusfbcon_blank(int blank, struct fb_info *info);
+
+
+    /*
+     *  Internal routines
+     */
+
+static int sbusfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+			    u_int *transp, struct fb_info *info);
+static int sbusfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			    u_int transp, struct fb_info *info);
+static void do_install_cmap(int con, struct fb_info *info);
+
+static struct fb_ops sbusfb_ops = {
+	sbusfb_open, sbusfb_release, sbusfb_get_fix, sbusfb_get_var, sbusfb_set_var,
+	sbusfb_get_cmap, sbusfb_set_cmap, sbusfb_pan_display, sbusfb_ioctl, sbusfb_mmap
+};
+
+
+    /*
+     *  Open/Release the frame buffer device
+     */
+
+static int sbusfb_open(struct fb_info *info, int user)
+{
+	struct fb_info_sbusfb *fb = sbusfbinfo(info);
+	
+	if (user) {
+		if (fb->open) return -EBUSY;
+		fb->mmaped = 0;
+		fb->open = 1;
+		fb->vtconsole = -1;
+	} else
+		fb->consolecnt++;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int sbusfb_release(struct fb_info *info, int user)
+{
+	struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+	if (user) {	
+		if (fb->vtconsole != -1) {
+			vt_cons[fb->vtconsole]->vc_mode = KD_TEXT;
+			if (fb->mmaped)
+				sbusfb_clear_margin(&fb_display[fb->vtconsole], 0);
+		}
+		fb->open = 0;
+	} else
+		fb->consolecnt--;
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int sbusfb_mmap(struct fb_info *info, struct file *file, 
+			struct vm_area_struct *vma)
+{
+	struct fb_info_sbusfb *fb = sbusfbinfo(info);
+	unsigned int size, page, r, map_size;
+	unsigned long map_offset = 0;
+	int i;
+                                        
+	size = vma->vm_end - vma->vm_start;
+	if (vma->vm_offset & ~PAGE_MASK)
+		return -ENXIO;
+		
+	/* To stop the swapper from even considering these pages */
+	vma->vm_flags |= (VM_SHM| VM_LOCKED);
+	
+#ifdef __sparc_v9__
+	/* Align it as much as desirable */
+	{
+		int j, max = -1, alignment;
+		
+		map_offset = vma->vm_offset+size;
+		for (i = 0; fb->mmap_map[i].size; i++) {
+			if (fb->mmap_map[i].voff < vma->vm_offset)
+				continue;
+			if (fb->mmap_map[i].voff >= map_offset)
+				break;
+			if (max < 0 || fb->mmap_map[i].size > fb->mmap_map[max].size)
+				max = i;
+		}
+		if (max >= 0) {
+			j = fb->mmap_map[max].size;
+			if (fb->mmap_map[max].voff + j > map_offset)
+				j = map_offset - fb->mmap_map[max].voff;
+			for (alignment = 0x400000; alignment > PAGE_SIZE; alignment >>= 3)
+				if (j >= alignment && !(fb->mmap_map[max].poff & (alignment - 1)))
+					break;
+			if (alignment > PAGE_SIZE) {
+				j = alignment;
+				alignment = j - ((vma->vm_start + fb->mmap_map[max].voff - vma->vm_offset) & (j - 1));
+				if (alignment != j) {
+					struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start);
+					if (!vmm || vmm->vm_start >= vma->vm_end + alignment) {
+						vma->vm_start += alignment;
+						vma->vm_end += alignment;
+					}
+				}
+			}
+		}
+	}
+#endif	
+
+	/* Each page, see which map applies */
+	for (page = 0; page < size; ){
+		map_size = 0;
+		for (i = 0; fb->mmap_map[i].size; i++)
+			if (fb->mmap_map[i].voff == vma->vm_offset+page) {
+				map_size = fb->mmap_map[i].size;
+				map_offset = (fb->physbase + fb->mmap_map[i].poff) & _PAGE_PADDR;
+				break;
+			}
+		if (!map_size){
+			page += PAGE_SIZE;
+			continue;
+		}
+		if (page + map_size > size)
+			map_size = size - page;
+		r = io_remap_page_range (vma->vm_start+page, map_offset, map_size, vma->vm_page_prot, fb->iospace);
+		if (r)
+			return -EAGAIN;
+		page += map_size;
+	}
+	
+	vma->vm_file = file;
+	file->f_count++;
+	vma->vm_flags |= VM_IO;
+	if (!fb->mmaped) {
+		fb->mmaped = 1;
+		if (fb->consolecnt && fb_display[fb->lastconsole].fb_info == info) {
+			fb->vtconsole = fb->lastconsole;
+			vt_cons [fb->lastconsole]->vc_mode = KD_GRAPHICS;
+		} else if (fb->unblank && !fb->blanked)
+			(*fb->unblank)(fb);
+	}
+	return 0;
+}
+
+static void sbusfb_clear_margin(struct display *p, int s)
+{
+	struct fb_info_sbusfb *fb = sbusfbinfod(p);
+
+	if (fb->fill) {
+		unsigned short rects [16];
+
+		rects [0] = 0;
+		rects [1] = 0;
+		rects [2] = fb->var.xres_virtual;
+		rects [3] = fb->y_margin;
+		rects [4] = 0;
+		rects [5] = fb->y_margin;
+		rects [6] = fb->x_margin;
+		rects [7] = fb->var.yres_virtual;
+		rects [8] = fb->var.xres_virtual - fb->x_margin;
+		rects [9] = fb->y_margin;
+		rects [10] = fb->var.xres_virtual;
+		rects [11] = fb->var.yres_virtual;
+		rects [12] = fb->x_margin;
+		rects [13] = fb->var.yres_virtual - fb->y_margin;
+		rects [14] = fb->var.xres_virtual - fb->x_margin;
+		rects [15] = fb->var.yres_virtual;
+		(*fb->fill)(fb, s, 4, rects);
+	} else {
+		unsigned char *fb_base = p->screen_base, *q;
+		int skip_bytes = fb->y_margin * fb->var.xres_virtual;
+		int scr_size = fb->var.xres_virtual * fb->var.yres_virtual;
+		int h, he, incr, size;
+
+		he = fb->var.yres;
+		if (fb->var.bits_per_pixel == 1) {
+			fb_base -= (skip_bytes + fb->x_margin) / 8;
+			skip_bytes /= 8;
+			scr_size /= 8;
+			memset (fb_base, ~0, skip_bytes - fb->x_margin / 8);
+			memset (fb_base + scr_size - skip_bytes + fb->x_margin / 8, ~0, skip_bytes - fb->x_margin / 8);
+			incr = fb->var.xres_virtual / 8;
+			size = fb->x_margin / 8 * 2;
+			for (q = fb_base + skip_bytes - fb->x_margin / 8, h = 0;
+			     h <= he; q += incr, h++)
+				memset (q, ~0, size);
+		} else {
+			fb_base -= (skip_bytes + fb->x_margin);
+			memset (fb_base, attr_bg_col(s), skip_bytes - fb->x_margin);
+			memset (fb_base + scr_size - skip_bytes + fb->x_margin, attr_bg_col(s), skip_bytes - fb->x_margin);
+			incr = fb->var.xres_virtual;
+			size = fb->x_margin * 2;
+			for (q = fb_base + skip_bytes - fb->x_margin, h = 0;
+			     h <= he; q += incr, h++)
+				memset (q, attr_bg_col(s), size);
+		}
+	}
+	if (fb->switch_from_graph)
+		(*fb->switch_from_graph)(fb);
+}
+
+static void sbusfb_disp_setup(struct display *p)
+{
+	struct fb_info_sbusfb *fb = sbusfbinfod(p);
+
+	if (fb->setup)
+		fb->setup(p);	
+	sbusfb_clear_margin(p, 0);
+}
+
+    /*
+     *  Get the Fixed Part of the Display
+     */
+
+static int sbusfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			  struct fb_info *info)
+{
+	struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+	memcpy(fix, &fb->fix, sizeof(struct fb_fix_screeninfo));
+	return 0;
+}
+
+    /*
+     *  Get the User Defined Part of the Display
+     */
+
+static int sbusfb_get_var(struct fb_var_screeninfo *var, int con,
+			  struct fb_info *info)
+{
+	struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+	memcpy(var, &fb->var, sizeof(struct fb_var_screeninfo));
+	return 0;
+}
+
+    /*
+     *  Set the User Defined Part of the Display
+     */
+
+static int sbusfb_set_var(struct fb_var_screeninfo *var, int con,
+			struct fb_info *info)
+{
+#if 1
+	return -EINVAL;
+#else
+    struct display *display;
+    int oldbpp = -1, err;
+    int activate = var->activate;
+    struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+    if (con >= 0)
+	display = &fb_display[con];
+    else
+	display = &fb->disp;	/* used during initialization */
+
+    if (var->xres > fb->var.xres || var->yres > fb->var.yres ||
+	var->xres_virtual > fb->var.xres_virtual ||
+	var->yres_virtual > fb->var.yres_virtual ||
+	var->bits_per_pixel > fb->var.bits_per_pixel ||
+	var->nonstd ||
+	(var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
+	return -EINVAL;
+    memcpy(var, &fb->var, sizeof(struct fb_var_screeninfo));
+
+    if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+	oldbpp = display->var.bits_per_pixel;
+	display->var = *var;
+    }
+    if (oldbpp != var->bits_per_pixel) {
+	if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
+	    return err;
+	do_install_cmap(con, info);
+    }
+    return 0;
+#endif
+}
+
+    /*
+     *  Pan or Wrap the Display
+     *
+     *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+     */
+
+static int sbusfb_pan_display(struct fb_var_screeninfo *var, int con,
+			      struct fb_info *info)
+{
+	if (var->xoffset || var->yoffset)
+		return -EINVAL;
+	else
+		return 0;
+}
+
+    /*
+     *  Hardware cursor
+     */
+     
+static int sbus_hw_scursor (struct fbcursor *cursor, struct fb_info_sbusfb *fb)
+{
+	int op;
+	int i, bytes = 0;
+	struct fbcursor f;
+	char red[2], green[2], blue[2];
+	
+	if (copy_from_user (&f, cursor, sizeof(struct fbcursor)))
+		return -EFAULT;
+	op = f.set;
+	if (op & FB_CUR_SETSHAPE){
+		if ((u32) f.size.fbx > fb->cursor.hwsize.fbx)
+			return -EINVAL;
+		if ((u32) f.size.fby > fb->cursor.hwsize.fby)
+			return -EINVAL;
+		if (f.size.fbx > 32)
+			bytes = f.size.fby << 3;
+		else
+			bytes = f.size.fby << 2;
+	}
+	if (op & FB_CUR_SETCMAP){
+		if (f.cmap.index || f.cmap.count != 2)
+			return -EINVAL;
+		if (copy_from_user (red, f.cmap.red, 2) ||
+		    copy_from_user (green, f.cmap.green, 2) ||
+		    copy_from_user (blue, f.cmap.blue, 2))
+			return -EFAULT;
+	}
+	if (op & FB_CUR_SETCMAP)
+		(*fb->setcursormap) (fb, red, green, blue);
+	if (op & FB_CUR_SETSHAPE){
+		u32 u;
+		
+		fb->cursor.size = f.size;
+		memset ((void *)&fb->cursor.bits, 0, sizeof (fb->cursor.bits));
+		if (copy_from_user (fb->cursor.bits [0], f.mask, bytes) ||
+		    copy_from_user (fb->cursor.bits [1], f.image, bytes))
+			return -EFAULT;
+		if (f.size.fbx <= 32) {
+			u = ~(0xffffffff >> f.size.fbx);
+			for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
+				fb->cursor.bits [0][i] &= u;
+				fb->cursor.bits [1][i] &= fb->cursor.bits [0][i];
+			}
+		} else {
+			u = ~(0xffffffff >> (f.size.fbx - 32));
+			for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
+				fb->cursor.bits [0][2*i+1] &= u;
+				fb->cursor.bits [1][2*i] &= fb->cursor.bits [0][2*i];
+				fb->cursor.bits [1][2*i+1] &= fb->cursor.bits [0][2*i+1];
+			}
+		}
+		(*fb->setcurshape) (fb);
+	}
+	if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){
+		if (op & FB_CUR_SETCUR)
+			fb->cursor.enable = f.enable;
+		if (op & FB_CUR_SETPOS)
+			fb->cursor.cpos = f.pos;
+		if (op & FB_CUR_SETHOT)
+			fb->cursor.chot = f.hot;
+		(*fb->setcursor) (fb);
+	}
+	return 0;
+}
+
+static unsigned char hw_cursor_cmap[2] = { 0, 0xff };
+
+static void sbusfb_cursor(struct display *p, int mode, int x, int y)
+{
+	struct fb_info_sbusfb *fb = sbusfbinfod(p);
+	
+	switch (mode) {
+	case CM_ERASE:
+		fb->cursor.enable = 0;
+		(*fb->setcursor)(fb);
+		fb->hw_cursor_shown = 0;
+		break;
+				  
+	case CM_MOVE:
+	case CM_DRAW:
+		if (!fb->hw_cursor_shown) {
+			fb->cursor.size.fbx = p->fontwidth;
+			fb->cursor.size.fby = p->fontheight;
+			fb->cursor.chot.fbx = 0;
+			fb->cursor.chot.fby = 0;
+			fb->cursor.enable = 1;
+			memset (fb->cursor.bits, 0, sizeof (fb->cursor.bits));
+			fb->cursor.bits[0][p->fontheight - 2] = (0xffffffff << (32 - p->fontwidth));
+			fb->cursor.bits[1][p->fontheight - 2] = (0xffffffff << (32 - p->fontwidth));
+			fb->cursor.bits[0][p->fontheight - 1] = (0xffffffff << (32 - p->fontwidth));
+			fb->cursor.bits[1][p->fontheight - 1] = (0xffffffff << (32 - p->fontwidth));
+			(*fb->setcursormap) (fb, hw_cursor_cmap, hw_cursor_cmap, hw_cursor_cmap);
+			(*fb->setcurshape) (fb);
+			fb->hw_cursor_shown = 1;
+		}
+		fb->cursor.cpos.fbx = (x << 3) + fb->x_margin; /* x * p->fontwidth */
+		if (p->fontheight == 16)
+			fb->cursor.cpos.fby = (y << 4) + fb->y_margin;
+		else
+			fb->cursor.cpos.fby = (y * p->fontheight) + fb->y_margin;
+		(*fb->setcursor)(fb);
+		break;
+	}
+}
+
+    /*
+     *  Get the Colormap
+     */
+
+static int sbusfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			 struct fb_info *info)
+{
+	if (con == currcon) /* current console? */
+		return fb_get_cmap(cmap, &fb_display[con].var, kspc, sbusfb_getcolreg, info);
+	else if (fb_display[con].cmap.len) /* non default colormap? */
+		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+	else
+		fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2);
+	return 0;
+}
+
+    /*
+     *  Set the Colormap
+     */
+
+static int sbusfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			 struct fb_info *info)
+{
+	int err;
+
+	if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
+		if ((err = fb_alloc_cmap(&fb_display[con].cmap, 1<<fb_display[con].var.bits_per_pixel, 0)))
+			return err;
+	}
+	if (con == currcon) {			/* current console? */
+		err = fb_set_cmap(cmap, &fb_display[con].var, kspc, sbusfb_setcolreg, info);
+		if (!err) {
+			struct fb_info_sbusfb *fb = sbusfbinfo(info);
+			
+			if (fb->loadcmap)
+				(*fb->loadcmap)(fb, cmap->start, cmap->len);
+		}
+		return err;
+	} else
+		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+	return 0;
+}
+
+static int sbusfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+			u_long arg, int con, struct fb_info *info)
+{
+	struct fb_info_sbusfb *fb = sbusfbinfo(info);
+	int i;
+	
+	switch (cmd){
+	case FBIOGTYPE:		/* return frame buffer type */
+		copy_to_user_ret((struct fbtype *)arg, &fb->type, sizeof(struct fbtype), -EFAULT);
+		break;
+	case FBIOGATTR: {
+		struct fbgattr *fba = (struct fbgattr *) arg;
+		
+		i = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct fbgattr));
+		if (i) return i;
+		__put_user_ret(fb->emulations[0], &fba->real_type, -EFAULT);
+		__put_user_ret(0, &fba->owner, -EFAULT);
+		__copy_to_user_ret(&fba->fbtype, &fb->type,
+				   sizeof(struct fbtype), -EFAULT);
+		__put_user_ret(0, &fba->sattr.flags, -EFAULT);
+		__put_user_ret(fb->type.fb_type, &fba->sattr.emu_type, -EFAULT);
+		__put_user_ret(-1, &fba->sattr.dev_specific[0], -EFAULT);
+		for (i = 0; i < 4; i++)
+			put_user_ret(fb->emulations[i], &fba->emu_types[i], -EFAULT);
+		break;
+	}
+	case FBIOSATTR:
+		i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbsattr));
+		if (i) return i;
+		return -EINVAL;
+	case FBIOSVIDEO:
+		if (fb->consolecnt &&
+		    vt_cons[fb->lastconsole]->vc_mode == KD_TEXT)
+ 			break;
+		get_user_ret(i, (int *)arg, -EFAULT);
+		if (i){
+			if (!fb->blanked || !fb->unblank)
+				break;
+			if (fb->consolecnt || (fb->open && fb->mmaped))
+				(*fb->unblank)(fb);
+			fb->blanked = 0;
+		} else {
+			if (fb->blanked || !fb->blank)
+				break;
+			(*fb->blank)(fb);
+			fb->blanked = 1;
+		}
+		break;
+	case FBIOGVIDEO:
+		put_user_ret(fb->blanked, (int *) arg, -EFAULT);
+		break;
+	case FBIOGETCMAP: {
+		char *rp, *gp, *bp;
+		int end, count, index;
+		struct fbcmap *cmap;
+
+		if (!fb->loadcmap)
+			return -EINVAL;
+		i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbcmap));
+		if (i) return i;
+		cmap = (struct fbcmap *) arg;
+		__get_user_ret(count, &cmap->count, -EFAULT);
+		__get_user_ret(index, &cmap->index, -EFAULT);
+		if ((index < 0) || (index > 255))
+			return -EINVAL;
+		if (index + count > 256)
+			count = 256 - index;
+		__get_user_ret(rp, &cmap->red, -EFAULT);
+		__get_user_ret(gp, &cmap->green, -EFAULT);
+		__get_user_ret(bp, &cmap->blue, -EFAULT);
+		if(verify_area (VERIFY_WRITE, rp, count))  return -EFAULT;
+		if(verify_area (VERIFY_WRITE, gp, count))  return -EFAULT;
+		if(verify_area (VERIFY_WRITE, bp, count))  return -EFAULT;
+		end = index + count;
+		for (i = index; i < end; i++){
+			__put_user_ret(fb->color_map CM(i,0), rp, -EFAULT);
+			__put_user_ret(fb->color_map CM(i,1), gp, -EFAULT);
+			__put_user_ret(fb->color_map CM(i,2), bp, -EFAULT);
+			rp++; gp++; bp++;
+		}
+		(*fb->loadcmap)(fb, index, count);
+		break;			
+	}
+	case FBIOPUTCMAP: {	/* load color map entries */
+		char *rp, *gp, *bp;
+		int end, count, index;
+		struct fbcmap *cmap;
+		
+		if (!fb->loadcmap)
+			return -EINVAL;
+		i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbcmap));
+		if (i) return i;
+		cmap = (struct fbcmap *) arg;
+		__get_user_ret(count, &cmap->count, -EFAULT);
+		__get_user_ret(index, &cmap->index, -EFAULT);
+		if ((index < 0) || (index > 255))
+			return -EINVAL;
+		if (index + count > 256)
+			count = 256 - index;
+		__get_user_ret(rp, &cmap->red, -EFAULT);
+		__get_user_ret(gp, &cmap->green, -EFAULT);
+		__get_user_ret(bp, &cmap->blue, -EFAULT);
+		if(verify_area (VERIFY_READ, rp, count)) return -EFAULT;
+		if(verify_area (VERIFY_READ, gp, count)) return -EFAULT;
+		if(verify_area (VERIFY_READ, bp, count)) return -EFAULT;
+
+		end = index + count;
+		for (i = index; i < end; i++){
+			__get_user_ret(fb->color_map CM(i,0), rp, -EFAULT);
+			__get_user_ret(fb->color_map CM(i,1), gp, -EFAULT);
+			__get_user_ret(fb->color_map CM(i,2), bp, -EFAULT);
+			rp++; gp++; bp++;
+		}
+		(*fb->loadcmap)(fb, index, count);
+		break;			
+	}
+	case FBIOGCURMAX: {
+		struct fbcurpos *p = (struct fbcurpos *) arg;
+		if (!fb->setcursor) return -EINVAL;
+		if(verify_area (VERIFY_WRITE, p, sizeof (struct fbcurpos)))
+			return -EFAULT;
+		__put_user_ret(fb->cursor.hwsize.fbx, &p->fbx, -EFAULT);
+		__put_user_ret(fb->cursor.hwsize.fby, &p->fby, -EFAULT);
+		break;
+	}
+	case FBIOSCURSOR:
+		if (!fb->setcursor) return -EINVAL;
+ 		if (fb->consolecnt) {
+ 			if (vt_cons[fb->lastconsole]->vc_mode == KD_TEXT)
+ 				return -EINVAL; /* Don't let graphics programs hide our nice text cursor */
+			fb->hw_cursor_shown = 0; /* Forget state of our text cursor */
+		}
+		return sbus_hw_scursor ((struct fbcursor *) arg, fb);
+
+	case FBIOSCURPOS:
+		if (!fb->setcursor) return -EINVAL;
+		/* Don't let graphics programs move our nice text cursor */
+ 		if (fb->consolecnt) {
+ 			if (vt_cons[fb->lastconsole]->vc_mode == KD_TEXT)
+ 				return -EINVAL; /* Don't let graphics programs move our nice text cursor */
+ 		}
+		if (copy_from_user(&fb->cursor.cpos, (void *)arg, sizeof(struct fbcurpos)))
+			return -EFAULT;
+		(*fb->setcursor) (fb);
+		break;
+	default:
+		/* FIXME: Call here possible fb specific ioctl */
+		return -EINVAL;
+	}		
+	return 0;
+}
+
+    /*
+     *  Setup: parse used options
+     */
+
+void sbusfb_setup(char *options, int *ints)
+{
+	if (!strncmp(options, "nomargins", 9))
+		nomargins = 1;
+}
+
+static int sbusfbcon_switch(int con, struct fb_info *info)
+{
+	/* Do we have to save the colormap? */
+	if (fb_display[currcon].cmap.len)
+		fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1, sbusfb_getcolreg, info);
+
+	sbusfbinfo(info)->lastconsole = con;
+	currcon = con;
+	/* Install new colormap */
+	do_install_cmap(con, info);
+	return 0;
+}
+
+    /*
+     *  Update the `var' structure (called by fbcon.c)
+     */
+
+static int sbusfbcon_updatevar(int con, struct fb_info *info)
+{
+	/* Nothing */
+	return 0;
+}
+
+    /*
+     *  Blank the display.
+     */
+
+static void sbusfbcon_blank(int blank, struct fb_info *info)
+{
+#if 0
+    struct fb_info_sbusfb *fb = sbusfbinfo(info);
+    int i, j;
+
+    if (!fb->cmap_adr)
+	return;
+
+    if (blank)
+	for (i = 0; i < 256; i++) {
+	    *fb->cmap_adr = i;
+	    for (j = 0; j < 3; j++) {
+		*fb->cmap_data = 0;
+	    }
+	}
+    else
+	do_install_cmap(currcon, info);
+#endif
+}
+
+    /*
+     *  Read a single color register and split it into
+     *  colors/transparent. Return != 0 for invalid regno.
+     */
+
+static int sbusfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+			  u_int *transp, struct fb_info *info)
+{
+	struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+	if (!fb->color_map || regno > 255)
+		return 1;
+	*red = fb->color_map CM(regno, 0);
+	*green = fb->color_map CM(regno, 1);
+	*blue = fb->color_map CM(regno, 2);
+	return 0;
+}
+
+
+    /*
+     *  Set a single color register. The values supplied are already
+     *  rounded down to the hardware's capabilities (according to the
+     *  entries in the var structure). Return != 0 for invalid regno.
+     */
+
+static int sbusfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			    u_int transp, struct fb_info *info)
+{
+	struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+	if (!fb->color_map || regno > 255)
+		return 1;
+	fb->color_map CM(regno, 0) = red;
+	fb->color_map CM(regno, 1) = green;
+	fb->color_map CM(regno, 2) = blue;
+	return 0;
+}
+
+
+static void do_install_cmap(int con, struct fb_info *info)
+{
+	struct fb_info_sbusfb *fb = sbusfbinfo(info);
+	
+	if (con != currcon)
+		return;
+	if (fb_display[con].cmap.len)
+		fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
+			    sbusfb_setcolreg, info);
+	else
+		fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+			    &fb_display[con].var, 1, sbusfb_setcolreg, info);
+	if (fb->loadcmap)
+		(*fb->loadcmap)(fb, 0, 256);
+}
+
+    /*
+     *  Initialisation
+     */
+
+__initfunc(static void sbusfb_init_fb(int node, int parent, int fbtype,
+				      struct linux_sbus_device *sbdp))
+{
+	struct fb_fix_screeninfo *fix;
+	struct fb_var_screeninfo *var;
+	struct display *disp;
+	struct fb_info_sbusfb *fb;
+	struct fbtype *type;
+	int linebytes, w, h, depth;
+	char *p = NULL;
+	int margin;
+
+	fb = kmalloc(sizeof(struct fb_info_sbusfb), GFP_ATOMIC);
+	if (!fb) {
+		prom_printf("Could not allocate sbusfb structure\n");
+		return;
+	}
+	memset(fb, 0, sizeof(struct fb_info_sbusfb));
+	fix = &fb->fix;
+	var = &fb->var;
+	disp = &fb->disp;
+	type = &fb->type;
+	
+	fb->prom_node = node;
+	fb->prom_parent = parent;
+	fb->sbdp = sbdp;
+	if (sbdp)
+		fb->iospace = sbdp->reg_addrs[0].which_io;
+
+	type->fb_type = fbtype;
+	memset(&fb->emulations, 0xff, sizeof(fb->emulations));
+	fb->emulations[0] = fbtype;
+	
+#ifndef __sparc_v9__
+	disp->screen_base = prom_getintdefault(node, "address", 0);
+#endif
+	
+	type->fb_height = h = prom_getintdefault(node, "height", 900);
+	type->fb_width  = w = prom_getintdefault(node, "width", 1152);
+	type->fb_depth  = depth = (fbtype == FBTYPE_SUN2BW) ? 1 : 8;
+	linebytes = prom_getintdefault(node, "linebytes", w * depth / 8);
+	type->fb_size   = PAGE_ALIGN((linebytes) * h);
+	
+	if (!nomargins)
+		for (margin = 0; def_margins[margin].depth; margin++)
+			if (w == def_margins[margin].xres &&
+			    h == def_margins[margin].yres &&
+			    depth == def_margins[margin].depth) {
+				fb->x_margin = def_margins[margin].x_margin;
+				fb->y_margin = def_margins[margin].y_margin;
+				break;
+			}
+	fb->x_margin += ((w - 2*fb->x_margin) & 15) / 2;
+	fb->y_margin += ((h - 2*fb->y_margin) & 15) / 2;
+
+	var->xres_virtual = w;
+	var->yres_virtual = h;
+	var->xres = w - 2*fb->x_margin;
+	var->yres = h - 2*fb->y_margin;
+	
+	var->bits_per_pixel = depth;
+	var->height = var->width = -1;
+	var->pixclock = 10000;
+	var->vmode = FB_VMODE_NONINTERLACED;
+	var->red.length = var->green.length = var->blue.length = 8;
+
+	fix->line_length = linebytes;
+	fix->smem_len = type->fb_size;
+	fix->type = FB_TYPE_PACKED_PIXELS;
+	fix->visual = FB_VISUAL_PSEUDOCOLOR;
+	
+	fb->info.node = -1;
+	fb->info.fbops = &sbusfb_ops;
+	fb->info.disp = disp;
+	fb->info.fontname[0] = '\0';
+	fb->info.changevar = NULL;
+	fb->info.switch_con = &sbusfbcon_switch;
+	fb->info.updatevar = &sbusfbcon_updatevar;
+	fb->info.blank = &sbusfbcon_blank;
+	
+	fb->cursor.hwsize.fbx = 32;
+	fb->cursor.hwsize.fby = 32;
+	
+	if (depth > 1)
+		fb->color_map = kmalloc(256 * 3, GFP_ATOMIC);
+		
+	switch(fbtype) {
+#ifdef CONFIG_FB_CREATOR
+	case FBTYPE_CREATOR:
+		p = creatorfb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_CGSIX
+	case FBTYPE_SUNFAST_COLOR:
+		p = cgsixfb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_CGTHREE
+	case FBTYPE_SUN3COLOR:
+		p = cgthreefb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_TCX
+	case FBTYPE_TCXCOLOR:
+		p = tcxfb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_LEO
+	case FBTYPE_SUNLEO:
+		p = leofb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_BWTWO
+	case FBTYPE_SUN2BW:
+		p = bwtwofb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_CGFOURTEEN
+	case FBTYPE_MDICOLOR:
+		p = cgfourteenfb_init(fb); break;
+#endif
+	}
+	
+	if (!p) {
+		kfree(fb);
+		return;
+	}
+
+	disp->dispsw = &fb->dispsw;
+	if (fb->setcursor)
+		fb->dispsw.cursor = sbusfb_cursor;
+	fb->setup = fb->dispsw.setup;
+	fb->dispsw.setup = sbusfb_disp_setup;
+
+	disp->var = *var;
+	disp->visual = fix->visual;
+	disp->type = fix->type;
+	disp->type_aux = fix->type_aux;
+	disp->line_length = fix->line_length;
+
+	sbusfb_set_var(var, -1, &fb->info);
+
+	if (register_framebuffer(&fb->info) < 0) {
+		kfree(fb);
+		return;
+	}
+	printk("fb%d: %s\n", GET_FB_IDX(fb->info.node), p);
+}
+
+static inline int known_card(char *name)
+{
+	char *p;
+	for (p = name; *p && *p != ','; p++);
+	if (*p == ',') name = p + 1;
+	if (!strcmp(p, "cgsix") || !strcmp(p, "cgthree+"))
+		return FBTYPE_SUNFAST_COLOR;
+	if (!strcmp(p, "cgthree") || !strcmp(p, "cgRDI"))
+		return FBTYPE_SUN3COLOR;
+	if (!strcmp(p, "cgfourteen"))
+		return FBTYPE_MDICOLOR;
+	if (!strcmp(p, "leo"))
+		return FBTYPE_SUNLEO;
+	if (!strcmp(p, "bwtwo"))
+		return FBTYPE_SUN2BW;
+	if (!strcmp(p, "tcx"))
+		return FBTYPE_TCXCOLOR;
+	return FBTYPE_NOTYPE;
+}
+
+__initfunc(void sbusfb_init(void))
+{
+	int node, root, type;
+	struct linux_sbus_device *sbdp;
+	struct linux_sbus *sbus;
+	char prom_name[40];
+    
+#ifdef CONFIG_FB_CREATOR
+	root = prom_getchild(prom_root_node);
+	for (node = prom_searchsiblings(root, "SUNW,ffb"); node;
+	     node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb")) {
+		sbusfb_init_fb(node, prom_root_node, FBTYPE_CREATOR, NULL);
+	}
+#endif
+#ifdef CONFIG_SUN4
+	sbusfb_init_fb(0, 0, FBTYPE_SUN2BW, NULL);
+#endif
+#if defined(CONFIG_FB_CGFOURTEEN) && !defined(__sparc_v9__)
+	root = prom_getchild(prom_root_node);
+	root = prom_searchsiblings(root, "obio");
+	if (root && 
+	    (node = prom_searchsiblings(prom_getchild(root), "cgfourteen"))) {
+		sbusfb_init_fb(node, root, FBTYPE_MDICOLOR, NULL);
+	}
+#endif
+	if (!SBus_chain) return;
+	for_all_sbusdev(sbdp, sbus) {
+		type = known_card(sbdp->prom_name);
+		if (type == FBTYPE_NOTYPE) continue;
+		if (prom_getproperty(sbdp->prom_node, "emulation", prom_name, sizeof(prom_name)) > 0) {
+			type = known_card(prom_name);
+			if (type == FBTYPE_NOTYPE) type = known_card(sbdp->prom_name);
+		}
+		prom_apply_sbus_ranges(sbdp->my_bus, &sbdp->reg_addrs[0],
+				       sbdp->num_registers, sbdp);
+		sbusfb_init_fb(sbdp->prom_node, sbdp->my_bus->prom_node, type, sbdp);
+	}
+}	
diff -u --recursive --new-file v2.1.108/linux/drivers/video/sbusfb.h linux/drivers/video/sbusfb.h
--- v2.1.108/linux/drivers/video/sbusfb.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/sbusfb.h	Fri Jul 10 15:18:32 1998
@@ -0,0 +1,101 @@
+#include <asm/sbus.h>
+#include <asm/oplib.h>
+#include <asm/fbio.h>
+
+#include "fbcon.h"
+
+struct bt_regs {
+	volatile unsigned int addr;           /* address register */
+	volatile unsigned int color_map;      /* color map */
+	volatile unsigned int control;        /* control register */
+	volatile unsigned int cursor;         /* cursor map register */
+};
+
+struct fb_info_creator {
+	struct ffb_fbc *fbc;
+	struct ffb_dac *dac;
+	int dac_rev;
+	int xy_margin;
+};
+struct fb_info_cgsix {
+	struct bt_regs *bt;
+	struct cg6_fbc *fbc;
+	struct cg6_thc *thc;
+	volatile u32 *fhc;
+};
+
+struct cg_cursor {
+	short	enable;         /* cursor is enabled */
+	struct	fbcurpos cpos;  /* position */
+	struct	fbcurpos chot;  /* hot-spot */
+	struct	fbcurpos size;  /* size of mask & image fields */
+	struct	fbcurpos hwsize; /* hw max size */
+	int	bits[2][128];   /* space for mask & image bits */
+	char	color [6];      /* cursor colors */
+};
+
+struct sbus_mmap_map {
+	unsigned long voff;
+	unsigned long poff;
+	unsigned long size;
+};
+
+struct fb_info_sbusfb {
+	struct fb_info info;
+	struct fb_fix_screeninfo fix;
+	struct fb_var_screeninfo var;
+	struct display disp;
+	struct display_switch dispsw;
+	struct fbtype type;
+	struct linux_sbus_device *sbdp;
+	int prom_node, prom_parent;
+	union {
+		struct fb_info_creator ffb;
+		struct fb_info_cgsix cg6;
+	} s;
+	unsigned char *color_map;
+	struct cg_cursor cursor;
+	unsigned char hw_cursor_shown;
+	unsigned char open;
+	unsigned char mmaped;
+	unsigned char blanked;
+	int x_margin;
+	int y_margin;
+	int vtconsole;
+	int lastconsole;
+	int consolecnt;
+	int emulations[4];
+	struct sbus_mmap_map *mmap_map;
+	unsigned long physbase;
+	int iospace;
+	/* Methods */
+	void (*setup)(struct display *);
+	void (*setcursor)(struct fb_info_sbusfb *);
+	void (*setcurshape)(struct fb_info_sbusfb *);
+	void (*setcursormap)(struct fb_info_sbusfb *, unsigned char *, unsigned char *, unsigned char *);
+	void (*loadcmap)(struct fb_info_sbusfb *, int, int);
+	void (*blank)(struct fb_info_sbusfb *);
+	void (*unblank)(struct fb_info_sbusfb *);
+	void (*reset)(struct fb_info_sbusfb *);
+	void (*fill)(struct fb_info_sbusfb *, int, int, unsigned short *);
+	void (*switch_from_graph)(struct fb_info_sbusfb *);
+};
+
+extern char *creatorfb_init(struct fb_info_sbusfb *);
+extern char *cgsixfb_init(struct fb_info_sbusfb *);
+extern char *cgthreefb_init(struct fb_info_sbusfb *);
+extern char *tcxfb_init(struct fb_info_sbusfb *);
+extern char *leofb_init(struct fb_info_sbusfb *);
+extern char *bwtwofb_init(struct fb_info_sbusfb *);
+extern char *cgfourteenfb_init(struct fb_info_sbusfb *);
+
+#define attr_fg_col(s)    \
+	(((s) >> 8) & 0x0f)
+#define attr_bg_col(s)    \
+	(((s) >> 12) & 0x0f)
+#define attr_bg_col_ec(conp) \
+	(((conp)->vc_video_erase_char >> 12) & 0x0f)
+
+#define sbusfbinfod(disp) ((struct fb_info_sbusfb *)(disp->fb_info))
+#define sbusfbinfo(info) ((struct fb_info_sbusfb *)(info))
+#define CM(i, j) [3*(i)+(j)]
diff -u --recursive --new-file v2.1.108/linux/drivers/video/skeletonfb.c linux/drivers/video/skeletonfb.c
--- v2.1.108/linux/drivers/video/skeletonfb.c	Wed Jun 24 22:54:08 1998
+++ linux/drivers/video/skeletonfb.c	Fri Jul 10 15:18:32 1998
@@ -60,8 +60,7 @@
 
     /*
      *  If your driver supports multiple boards, you should make these arrays,
-     *  or allocate them dynamically (using mem_start for builtin drivers, and
-     *  kmalloc() for loaded modules).
+     *  or allocate them dynamically (using kmalloc()).
      */
 
 static struct xxxfb_info fb_info;
@@ -278,9 +277,8 @@
      *  Initialization
      */
 
-__initfunc(unsigned long xxxfb_init(unsigned long mem_start))
+__initfunc(void xxxfb_init(void))
 {
-    int err;
     struct fb_var_screeninfo var;
 
     fb_info.fbhw = &xxx_switch;
@@ -296,18 +294,15 @@
     /* This should give a reasonable default video mode */
     fbgen_get_var(&disp.var, -1, &fb_info.gen);
     fbgen_do_set_var(var, 1, &fbinfo.gen);
-    err = register_framebuffer(&fb_info.gen.info);
-    if (err < 0)
-	return mem_start;
     fbgen_set_disp(-1, &fb_info.gen.info);
     fbgen_install_cmap(0, &fb_info.gen);
+    if (register_framebuffer(&fb_info.gen.info) < 0)
+	return;
     printk("fb%d: %s frame buffer device\n", GET_FB_IDX(fb_info.node),
 	   fb_info.modename);
 
     /* uncomment this if your driver cannot be unloaded */
     /* MOD_INC_USE_COUNT; */
-
-    return mem_start;
 }
 
 
@@ -344,14 +339,14 @@
      *  Frame buffer operations
      */
 
-static int xxxfb_open(const struct fb_info *info)
+static int xxxfb_open(const struct fb_info *info, int user)
 {
     /* Nothing, only a usage count for the moment */
     MOD_INC_USE_COUNT;
     return 0;
 }
 
-static int xxxfb_release(const struct fb_info *info)
+static int xxxfb_release(const struct fb_info *info, int user)
 {
     MOD_DEC_USE_COUNT;
     return 0;
@@ -379,7 +374,8 @@
 #ifdef MODULE
 int init_module(void)
 {
-    return xxxfb_init(NULL);
+    xxxfb_init();
+    return 0;
 }
 
 void cleanup_module(void)
diff -u --recursive --new-file v2.1.108/linux/drivers/video/tgafb.c linux/drivers/video/tgafb.c
--- v2.1.108/linux/drivers/video/tgafb.c	Wed Jun 24 22:54:08 1998
+++ linux/drivers/video/tgafb.c	Fri Jul 10 15:18:32 1998
@@ -252,8 +252,8 @@
      *  Interface used by the world
      */
 
-static int tgafb_open(struct fb_info *info);
-static int tgafb_release(struct fb_info *info);
+static int tgafb_open(struct fb_info *info, int user);
+static int tgafb_release(struct fb_info *info, int user);
 static int tgafb_get_fix(struct fb_fix_screeninfo *fix, int con,
 			 struct fb_info *info);
 static int tgafb_get_var(struct fb_var_screeninfo *var, int con,
@@ -274,7 +274,7 @@
      *  Interface to the low level console driver
      */
 
-unsigned long tgafb_init(unsigned long mem_start);
+void tgafb_init(void);
 static int tgafbcon_switch(int con, struct fb_info *info);
 static int tgafbcon_updatevar(int con, struct fb_info *info);
 static void tgafbcon_blank(int blank, struct fb_info *info);
@@ -304,7 +304,7 @@
      *  Open/Release the frame buffer device
      */
 
-static int tgafb_open(struct fb_info *info)                                       
+static int tgafb_open(struct fb_info *info, int user)
 {
     /*                                                                     
      *  Nothing, only a usage count for the moment                          
@@ -314,7 +314,7 @@
     return(0);                              
 }
         
-static int tgafb_release(struct fb_info *info)
+static int tgafb_release(struct fb_info *info, int user)
 {
     MOD_DEC_USE_COUNT;
     return(0);                                                    
@@ -453,15 +453,15 @@
      *  Initialisation
      */
 
-__initfunc(unsigned long tgafb_init(unsigned long mem_start))
+__initfunc(void tgafb_init(void))
 {
-    int i, j, temp, err;
+    int i, j, temp;
     unsigned char *cbp;
     struct pci_dev *pdev;
 
     pdev = pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, NULL);
     if (!pdev)
-	return mem_start;
+	return;
     tga_mem_base = pdev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK;
 #ifdef DEBUG
     printk("tgafb_init: mem_base 0x%x\n", tga_mem_base);
@@ -480,7 +480,7 @@
 	    break;
 	default:
 	    printk("TGA type (0x%x) unrecognized!\n", tga_type);
-	    return mem_start;
+	    return;
     }
 
     tga_regs_base = ((unsigned long)tga_mem_base + TGA_REGS_OFFSET);
@@ -689,7 +689,7 @@
     fb_var.xres = fb_var.xres_virtual = 640;
     fb_var.yres = fb_var.yres_virtual = 480;
     fb_fix.line_length = 80*fb_var.bits_per_pixel;
-    fb_fix.smem_start = (char *)__pa(tga_fb_base + LCA_DENSE_MEM);
+    fb_fix.smem_start = (char *)__pa(tga_fb_base + DENSE_MEM(tga_fb_base));
     fb_fix.smem_len = fb_fix.line_length*fb_var.yres;
     fb_fix.type = FB_TYPE_PACKED_PIXELS;
     fb_fix.type_aux = 0;
@@ -730,7 +730,7 @@
     disp.cmap.start = 0;
     disp.cmap.len = 0;
     disp.cmap.red = disp.cmap.green = disp.cmap.blue = disp.cmap.transp = NULL;
-    disp.screen_base = (char *)tga_fb_base + LCA_DENSE_MEM;
+    disp.screen_base = (char *)tga_fb_base + DENSE_MEM(tga_fb_base);
     disp.visual = fb_fix.visual;
     disp.type = fb_fix.type;
     disp.type_aux = fb_fix.type_aux;
@@ -765,15 +765,13 @@
     fb_info.updatevar = &tgafbcon_updatevar;
     fb_info.blank = &tgafbcon_blank;
 
-    err = register_framebuffer(&fb_info);
-    if (err < 0)
-	return mem_start;
-
     tgafb_set_var(&fb_var, -1, &fb_info);
 
+    if (register_framebuffer(&fb_info) < 0)
+	return;
+
     printk("fb%d: %s frame buffer device\n", GET_FB_IDX(fb_info.node),
 	   fb_fix.id);
-    return mem_start;
 }
 
 
@@ -927,9 +925,6 @@
 
   if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS)
     return;
-
-  if (__real_origin != __origin)
-    __set_origin(__real_origin);
 
   save_flags(flags); cli();
 
diff -u --recursive --new-file v2.1.108/linux/drivers/video/txtcon.c linux/drivers/video/txtcon.c
--- v2.1.108/linux/drivers/video/txtcon.c	Wed Jun 24 22:54:08 1998
+++ linux/drivers/video/txtcon.c	Wed Dec 31 16:00:00 1969
@@ -1,172 +0,0 @@
-/*
- * linux/drivers/video/txtcon.c -- Low level text mode based console driver
- *
- *    Copyright (C) 1995 Geert Uytterhoeven
- *
- *
- * This file is currently only a skeleton, since all Amigas and Ataris have
- * bitmapped graphics.
- *
- *
- * 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.
- */
-
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/kdev_t.h>
-#include <linux/console.h>
-#include <linux/init.h>
-
-
-    /*
-     *  Interface used by the world
-     */
-
-static unsigned long txtcon_startup(unsigned long kmem_start,
-				    const char **display_desc);
-static void txtcon_init(struct vc_data *conp);
-static void txtcon_deinit(struct vc_data *conp);
-static void txtcon_clear(struct vc_data *conp, int sy, int sx, int height,
-			 int width);
-static void txtcon_putc(struct vc_data *conp, int c, int y, int x);
-static void txtcon_putcs(struct vc_data *conp, const char *s, int count, int y,
-			 int x);
-static void txtcon_cursor(struct vc_data *conp, int mode);
-static void txtcon_scroll(struct vc_data *conp, int t, int b, int dir,
-			  int count);
-static void txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
-			 int height, int width);
-static int txtcon_switch(struct vc_data *conp);
-static int txtcon_blank(int blank);
-static int txtcon_get_font(struct vc_data *conp, int *w, int *h, char *data);
-static int txtcon_set_font(struct vc_data *conp, int w, int h, char *data);
-static int txtcon_set_palette(struct vc_data *conp, unsigned char *table);
-static int txtcon_scrolldelta(struct vc_data *conp, int lines);
-
-
-static unsigned long txtcon_startup(unsigned long kmem_start,
-				    const char **display_desc)
-{
-    return kmem_start;
-}
-
-
-static void txtcon_init(struct vc_data *conp)
-{
-    /* ... */
-}
-
-
-static void txtcon_deinit(struct vc_data *conp)
-{
-    /* ... */
-}
-
-
-/* ====================================================================== */
-
-/* txtcon_XXX routines - interface used by the world */
-
-
-static void txtcon_clear(struct vc_data *conp, int sy, int sx, int height,
-			 int width)
-{
-    /* ... */
-}
-
-
-static void txtcon_putc(struct vc_data *conp, int c, int y, int x)
-{
-    /* ... */
-}
-
-
-static void txtcon_putcs(struct vc_data *conp, const char *s, int count, int y,
-			 int x)
-{
-    /* ... */
-}
-
-
-static void txtcon_cursor(struct vc_data *conp, int mode)
-{
-    /* ... */
-}
-
-
-static void txtcon_scroll(struct vc_data *conp, int t, int b, int dir,
-			  int count)
-{
-    /* ... */
-}
-
-
-static void txtcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
-			 int height, int width)
-{
-    /* ... */
-}
-
-
-static int txtcon_switch(struct vc_data *conp)
-{
-    return -ENOSYS;
-}
-
-
-static int txtcon_blank(int blank)
-{
-    return -ENOSYS;
-}
-
-
-static int txtcon_get_font(struct vc_data *conp, int *w, int *h, char *data)
-{
-    return -ENOSYS;
-}
-
-
-static int txtcon_set_font(struct vc_data *conp, int w, int h, char *data)
-{
-    return -ENOSYS;
-}
-
-
-static int txtcon_set_palette(struct vc_data *conp, unsigned char *table)
-{
-    return -ENOSYS;
-}
-
-
-static int txtcon_scrolldelta(struct vc_data *conp, int lines)
-{
-    return -ENOSYS;
-}
-
-
-/* ====================================================================== */
-
-    /*
-     *  The console `switch' structure for the text mode based console
-     */
-
-struct consw txt_con = {
-    txtcon_startup,
-    txtcon_init,
-    txtcon_deinit,
-    txtcon_clear,
-    txtcon_putc,
-    txtcon_putcs,
-    txtcon_cursor,
-    txtcon_scroll,
-    txtcon_bmove,
-    txtcon_switch,
-    txtcon_blank,
-    txtcon_get_font,
-    txtcon_set_font,
-    txtcon_set_palette,
-    txtcon_scrolldelta
-};
diff -u --recursive --new-file v2.1.108/linux/drivers/video/vesafb.c linux/drivers/video/vesafb.c
--- v2.1.108/linux/drivers/video/vesafb.c	Wed Jun 24 22:54:08 1998
+++ linux/drivers/video/vesafb.c	Fri Jul 10 15:18:32 1998
@@ -117,7 +117,7 @@
 	 * Open/Release the frame buffer device
 	 */
 
-static int vesafb_open(struct fb_info *info)
+static int vesafb_open(struct fb_info *info, int user)
 {
 	/*
 	 * Nothing, only a usage count for the moment
@@ -126,7 +126,7 @@
 	return(0);
 }
 
-static int vesafb_release(struct fb_info *info)
+static int vesafb_release(struct fb_info *info, int user)
 {
 	MOD_DEC_USE_COUNT;
 	return(0);
@@ -443,12 +443,12 @@
 	/* Not supported */
 }
 
-__initfunc(unsigned long vesafb_init(unsigned long mem_start))
+__initfunc(void vesafb_init(void))
 {
 	int i,j;
 
 	if (screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB)
-		return mem_start;
+		return;
 
 	video_base          = (char*)screen_info.lfb_base;
 	video_bpp           = screen_info.lfb_depth;
@@ -517,16 +517,14 @@
 	fb_info.blank=&vesafb_blank;
 	do_fb_set_var(&vesafb_defined,1);
 
-	if (register_framebuffer(&fb_info)<0)
-		return mem_start;
-
 	vesafb_get_var(&disp.var, -1, &fb_info);
 	vesafb_set_disp(-1);
 
+	if (register_framebuffer(&fb_info)<0)
+		return;
+
 	printk("fb%d: %s frame buffer device\n",
 	       GET_FB_IDX(fb_info.node), fb_info.modename);
-
-	return mem_start;
 }
 
 /*
diff -u --recursive --new-file v2.1.108/linux/drivers/video/vfb.c linux/drivers/video/vfb.c
--- v2.1.108/linux/drivers/video/vfb.c	Wed Jun 24 22:54:08 1998
+++ linux/drivers/video/vfb.c	Fri Jul 10 15:18:32 1998
@@ -69,8 +69,8 @@
 
 void vfb_setup(char *options, int *ints);
 
-static int vfb_open(struct fb_info *info);
-static int vfb_release(struct fb_info *info);
+static int vfb_open(struct fb_info *info, int user);
+static int vfb_release(struct fb_info *info, int user);
 static int vfb_get_fix(struct fb_fix_screeninfo *fix, int con,
 		       struct fb_info *info);
 static int vfb_get_var(struct fb_var_screeninfo *var, int con,
@@ -91,7 +91,7 @@
      *  Interface to the low level console driver
      */
 
-unsigned long vfb_init(unsigned long mem_start);
+void vfb_init(void);
 static int vfbcon_switch(int con, struct fb_info *info);
 static int vfbcon_updatevar(int con, struct fb_info *info);
 static void vfbcon_blank(int blank, struct fb_info *info);
@@ -122,7 +122,7 @@
      *  Open/Release the frame buffer device
      */
 
-static int vfb_open(struct fb_info *info)                                       
+static int vfb_open(struct fb_info *info, int user)
 {
     /*                                                                     
      *  Nothing, only a usage count for the moment                          
@@ -132,7 +132,7 @@
     return(0);                              
 }
         
-static int vfb_release(struct fb_info *info)
+static int vfb_release(struct fb_info *info, int user)
 {
     MOD_DEC_USE_COUNT;
     return(0);                                                    
@@ -417,21 +417,13 @@
      *  Initialisation
      */
 
-__initfunc(unsigned long vfb_init(unsigned long mem_start))
+__initfunc(void vfb_init(void))
 {
-    int err;
-
     if (!vfb_enable)
-	return mem_start;
-
-    if (mem_start) {
-	videomemory = mem_start;
-	mem_start += videomemorysize;
-    } else
-	videomemory = (u_long)vmalloc(videomemorysize);
+	return;
 
-    if (!videomemory)
-	return mem_start;
+    if (!(videomemory = (u_long)vmalloc(videomemorysize)))
+	return;
 
     strcpy(fb_info.modename, vfb_name);
     fb_info.changevar = NULL;
@@ -442,15 +434,15 @@
     fb_info.updatevar = &vfbcon_updatevar;
     fb_info.blank = &vfbcon_blank;
 
-    err = register_framebuffer(&fb_info);
-    if (err < 0)
-	return mem_start;
-
     vfb_set_var(&vfb_default, -1, &fb_info);
 
+    if (register_framebuffer(&fb_info) < 0) {
+	vfree((void *)videomemory);
+	return;
+    }
+
     printk("fb%d: Virtual frame buffer device, using %ldK of video memory\n",
 	   GET_FB_IDX(fb_info.node), videomemorysize>>10);
-    return mem_start;
 }
 
 
@@ -629,13 +621,14 @@
 #ifdef MODULE
 int init_module(void)
 {
-    return(vfb_init(NULL));
+    vfb_init();
+    return 0;
 }
 
 void cleanup_module(void)
 {
     unregister_framebuffer(&fb_info);
-    vfree(videomemory);
+    vfree((void *)videomemory);
 }
 
 #endif /* MODULE */
diff -u --recursive --new-file v2.1.108/linux/drivers/video/vgacon.c linux/drivers/video/vgacon.c
--- v2.1.108/linux/drivers/video/vgacon.c	Wed Jun 24 22:54:08 1998
+++ linux/drivers/video/vgacon.c	Fri Jul 10 15:18:32 1998
@@ -3,6 +3,8 @@
  *
  *	Created 28 Sep 1997 by Geert Uytterhoeven
  *
+ *	Rewritten by Martin Mares <mj@ucw.cz>, July 1998
+ *
  *  This file is based on the old console.c, vga.c and vesa_blank.c drivers.
  *
  *	Copyright (C) 1991, 1992  Linus Torvalds
@@ -31,16 +33,13 @@
  *  more details.
  */
 
-
-
-
-/* KNOWN PROBLEMS/TO DO ===================================================== *
+/* KNOWN PROBLEMS/TO DO ========================================FIXME======== *
  *
  *	- monochrome attribute encoding (convert abscon <-> VGA style)
  *
- *	- speed up scrolling by changing the screen origin
+ *	- add support for VESA blanking
  *
- *	- add support for loadable fonts and VESA blanking
+ *	- Cursor shape fixes
  *
  * KNOWN PROBLEMS/TO DO ==================================================== */
 
@@ -61,8 +60,6 @@
 #include <linux/init.h>
 
 #include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/linux_logo.h>
 
 
 #define BLANK 0x0020
@@ -70,116 +67,51 @@
 #define CAN_LOAD_EGA_FONTS	/* undefine if the user must not do this */
 #define CAN_LOAD_PALETTE	/* undefine if the user must not do this */
 
+#undef VGA_CAN_DO_64KB
+
 #define dac_reg	(0x3c8)
 #define dac_val	(0x3c9)
 
-#ifdef __powerpc__
-#define VGA_OFFSET _ISA_MEM_BASE;
-#else
-#define VGA_OFFSET 0x0
-#endif
-
-
 /*
  *  Interface used by the world
  */
 
-static unsigned long vgacon_startup(unsigned long kmem_start,
-				   const char **display_desc);
-static void vgacon_init(struct vc_data *conp);
-static void vgacon_deinit(struct vc_data *conp);
-static void vgacon_clear(struct vc_data *conp, int sy, int sx, int height,
-			 int width);
-static void vgacon_putc(struct vc_data *conp, int c, int ypos, int xpos);
-static void vgacon_putcs(struct vc_data *conp, const char *s, int count,
-			 int ypos, int xpos);
-static void vgacon_cursor(struct vc_data *conp, int mode);
-static void vgacon_scroll(struct vc_data *conp, int t, int b, int dir,
-			  int count);
-static void vgacon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
-		       int height, int width);
-static int vgacon_switch(struct vc_data *conp);
+static const char *vgacon_startup(void);
+static void vgacon_init(struct vc_data *c, int init);
+static void vgacon_cursor(struct vc_data *c, int mode);
+static int vgacon_switch(struct vc_data *c);
 static int vgacon_blank(int blank);
-static int vgacon_get_font(struct vc_data *conp, int *w, int *h, char *data);
-static int vgacon_set_font(struct vc_data *conp, int w, int h, char *data);
-static int vgacon_set_palette(struct vc_data *conp, unsigned char *table);
-static int vgacon_scrolldelta(struct vc_data *conp, int lines);
-
-
-/*
- *  Internal routines
- */
-
-static int vgacon_show_logo(void);
+static int vgacon_get_font(struct vc_data *c, int *w, int *h, char *data);
+static int vgacon_set_font(struct vc_data *c, int w, int h, char *data);
+static int vgacon_set_palette(struct vc_data *c, unsigned char *table);
+static int vgacon_scrolldelta(struct vc_data *c, int lines);
+static int vgacon_set_origin(struct vc_data *c);
+static void vgacon_save_screen(struct vc_data *c);
+static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, int lines);
 
 
 /* Description of the hardware situation */
-static unsigned long   vga_video_mem_base;	/* Base of video memory */
-static unsigned long   vga_video_mem_term;	/* End of video memory */
+static unsigned long   vga_vram_base;		/* Base of video memory */
+static unsigned long   vga_vram_end;		/* End of video memory */
 static u16             vga_video_port_reg;	/* Video register select port */
 static u16             vga_video_port_val;	/* Video register value port */
-static unsigned long   vga_video_num_columns;	/* Number of text columns */
-static unsigned long   vga_video_num_lines;	/* Number of text lines */
-static unsigned long   vga_video_size_row;
-static unsigned long   vga_video_screen_size;
-
-static int vga_can_do_color = 0;
-static unsigned long vga_default_font_height;	/* Height of default screen font */
-
-static unsigned char vga_video_type;
-static unsigned char vga_has_wrapped;		/* all of videomem is data of fg_console */
-static unsigned char vga_hardscroll_enabled;
-static unsigned char vga_hardscroll_disabled_by_init = 0;
-
-
+static unsigned int    vga_video_num_columns;	/* Number of text columns */
+static unsigned int    vga_video_num_lines;	/* Number of text lines */
+static int	       vga_can_do_color = 0;	/* Do we support colors? */
+static unsigned int    vga_default_font_height;	/* Height of default screen font */
+static unsigned char   vga_video_type;		/* Card type */
+static unsigned char   vga_hardscroll_enabled;
+static unsigned char   vga_hardscroll_user_enable = 1;
+static unsigned char   vga_font_is_default = 1;
 
-    /*
-     *  VGA screen access
-     */ 
-
-static inline void vga_writew(u16 val, u16 * addr)
-{
-#ifdef __powerpc__
-	st_le16(addr, val);
-#else
-	writew(val, (unsigned long) addr);
-#endif /* !__powerpc__ */
-}
-
-static inline u16 vga_readw(u16 * addr)
-{
-#ifdef __powerpc__
-	return ld_le16(addr);
-#else
-	return readw((unsigned long) addr);
-#endif /* !__powerpc__ */	
-}
-
-static inline void vga_memsetw(void * s, u16 c, unsigned int count)
+void no_scroll(char *str, int *ints)
 {
-	u16 * addr = (u16 *) s;
-
-	while (count) {
-		count--;
-		vga_writew(c, addr++);
-	}
-}
-
-static inline void vga_memmovew(u16 *to, u16 *from, unsigned int count)
-{
-	if (to < from) {
-	    while (count) {
-		    count--;
-		    vga_writew(vga_readw(from++), to++);
-	    }
-	} else {
-	    from += count;
-	    to += count;
-	    while (count) {
-		    count--;
-		    vga_writew(vga_readw(--from), --to);
-	    }
-	}
+	/*
+	 * Disabling scrollback is required for the Braillex ib80-piezo
+	 * Braille reader made by F.H. Papenmeier (Germany).
+	 * Use the "no-scroll" bootflag.
+	 */
+	vga_hardscroll_user_enable = vga_hardscroll_enabled = 0;
 }
 
 
@@ -208,34 +140,32 @@
 }
 
 
-__initfunc(static unsigned long vgacon_startup(unsigned long kmem_start,
-					       const char **display_desc))
+__initfunc(static const char *vgacon_startup(void))
 {
+	const char *display_desc = NULL;
 	u16 saved;
 	u16 *p;
 
 	vga_video_num_lines = ORIG_VIDEO_LINES;
 	vga_video_num_columns = ORIG_VIDEO_COLS;
-	vga_video_size_row = 2 * ORIG_VIDEO_COLS;
-	vga_video_screen_size = vga_video_num_lines * vga_video_size_row;
 
 	if (ORIG_VIDEO_MODE == 7)	/* Is this a monochrome display? */
 	{
-		vga_video_mem_base = 0xb0000 + VGA_OFFSET;
+		vga_vram_base = 0xb0000;
 		vga_video_port_reg = 0x3b4;
 		vga_video_port_val = 0x3b5;
 		if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
 		{
 			vga_video_type = VIDEO_TYPE_EGAM;
-			vga_video_mem_term = 0xb8000 + VGA_OFFSET;
-			*display_desc = "EGA+";
+			vga_vram_end = 0xb8000;
+			display_desc = "EGA+";
 			request_region(0x3b0,16,"ega");
 		}
 		else
 		{
 			vga_video_type = VIDEO_TYPE_MDA;
-			vga_video_mem_term = 0xb2000 + VGA_OFFSET;
-			*display_desc = "*MDA";
+			vga_vram_end = 0xb2000;
+			display_desc = "*MDA";
 			request_region(0x3b0,12,"mda");
 			request_region(0x3bf, 1,"mda");
 		}
@@ -243,22 +173,22 @@
 	else				/* If not, it is color. */
 	{
 		vga_can_do_color = 1;
-		vga_video_mem_base = 0xb8000  + VGA_OFFSET;
-		vga_video_port_reg	= 0x3d4;
-		vga_video_port_val	= 0x3d5;
+		vga_vram_base = 0xb8000;
+		vga_video_port_reg = 0x3d4;
+		vga_video_port_val = 0x3d5;
 		if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10)
 		{
 			int i ;
 
-			vga_video_mem_term = 0xc0000 + VGA_OFFSET;
+			vga_vram_end = 0xc0000;
 
 			if (!ORIG_VIDEO_ISVGA) {
 				vga_video_type = VIDEO_TYPE_EGAC;
-				*display_desc = "EGA";
+				display_desc = "EGA";
 				request_region(0x3c0,32,"ega");
 			} else {
 				vga_video_type = VIDEO_TYPE_VGAC;
-				*display_desc = "VGA+";
+				display_desc = "VGA+";
 				request_region(0x3c0,32,"vga+");
 
 #ifdef VGA_CAN_DO_64KB
@@ -268,8 +198,8 @@
 				 * controllers (it seems like setting MM=01
 				 * and COE=1 isn't necessarily a good idea)
 				 */
-				vga_video_mem_base = 0xa0000  + VGA_OFFSET;
-				vga_video_mem_term = 0xb0000  + VGA_OFFSET;
+				vga_vram_base = 0xa0000;
+				vga_vram_end = 0xb0000;
 				outb_p (6, 0x3ce) ;
 				outb_p (6, 0x3cf) ;
 #endif
@@ -301,40 +231,36 @@
 		else
 		{
 			vga_video_type = VIDEO_TYPE_CGA;
-			vga_video_mem_term = 0xba000 + VGA_OFFSET;
-			*display_desc = "*CGA";
+			vga_vram_end = 0xba000;
+			display_desc = "*CGA";
 			request_region(0x3d4,2,"cga");
 		}
 	}
+	vga_vram_base = VGA_MAP_MEM(vga_vram_base);
+	vga_vram_end = VGA_MAP_MEM(vga_vram_end);
 
 	/*
 	 *	Find out if there is a graphics card present.
 	 *	Are there smarter methods around?
 	 */
-	p = (u16 *)vga_video_mem_base;
-	saved = vga_readw(p);
-	vga_writew(0xAA55, p);
-	if (vga_readw(p) != 0xAA55) {
-		vga_writew(saved, p);
-		return kmem_start;
-	}
-	vga_writew(0x55AA, p);
-	if (vga_readw(p) != 0x55AA) {
-		vga_writew(saved, p);
-		return kmem_start;
+	p = (u16 *)vga_vram_base;
+	saved = scr_readw(p);
+	scr_writew(0xAA55, p);
+	if (scr_readw(p) != 0xAA55) {
+		scr_writew(saved, p);
+		return NULL;
+	}
+	scr_writew(0x55AA, p);
+	if (scr_readw(p) != 0x55AA) {
+		scr_writew(saved, p);
+		return NULL;
 	}
-	vga_writew(saved, p);
+	scr_writew(saved, p);
 
-	vga_hardscroll_enabled = (vga_hardscroll_disabled_by_init ? 0 :
-	  (vga_video_type == VIDEO_TYPE_EGAC
+	if (vga_video_type == VIDEO_TYPE_EGAC
 	    || vga_video_type == VIDEO_TYPE_VGAC
-	    || vga_video_type == VIDEO_TYPE_EGAM));
-	vga_has_wrapped = 0;
-
-	if (vga_video_type == VIDEO_TYPE_VGAC
-	    || vga_video_type == VIDEO_TYPE_EGAC
-	    || vga_video_type == VIDEO_TYPE_EGAM)
-	{
+	    || vga_video_type == VIDEO_TYPE_EGAM) {
+		vga_hardscroll_enabled = vga_hardscroll_user_enable;
 		vga_default_font_height = ORIG_VIDEO_POINTS;
 		video_font_height = ORIG_VIDEO_POINTS;
 		/* This may be suboptimal but is a safe bet - go with it */
@@ -342,228 +268,407 @@
 			video_font_height * vga_video_num_lines;
 	}
 
-	if (!console_show_logo)
-	    console_show_logo = vgacon_show_logo;
-
-	return kmem_start;
-}
-
-
-static void vgacon_init(struct vc_data *conp)
-{
-    conp->vc_cols = vga_video_num_columns;
-    conp->vc_rows = vga_video_num_lines;
-    conp->vc_can_do_color = vga_can_do_color;
-}
-
-static void vgacon_deinit(struct vc_data *conp)
-{
+	return display_desc;
 }
 
 
-/* ====================================================================== */
-
-static void vgacon_clear(struct vc_data *conp, int sy, int sx, int height,
-			 int width)
+static void vgacon_init(struct vc_data *c, int init)
 {
-    int rows;
-    unsigned long dest;
-
-    if (console_blanked)
-	return;
-
-    dest = vga_video_mem_base + sy*vga_video_size_row + sx*2;
-    if (sx == 0 && width == vga_video_num_columns)      
-	vga_memsetw((void *)dest, conp->vc_video_erase_char, height * width);
-    else
-        for (rows = height; rows-- ; dest += vga_video_size_row)
-	    vga_memsetw((void *)dest, conp->vc_video_erase_char, width);
+	/* We cannot be loaded as a module, therefore init is always 1 */
+	c->vc_can_do_color = vga_can_do_color;
+	c->vc_cols = vga_video_num_columns;
+	c->vc_rows = vga_video_num_lines;
 }
 
 
-static void vgacon_putc(struct vc_data *conp, int c, int ypos, int xpos)
+static inline void vga_set_mem_top(struct vc_data *c)
 {
-    u16 *p;
-
-    if (console_blanked)
-	return;
-
-    p = (u16 *)(vga_video_mem_base+ypos*vga_video_size_row+xpos*2);
-    vga_writew(conp->vc_attr << 8 | c, p);
+	write_vga(12, (c->vc_visible_origin-vga_vram_base)/2);
 }
 
 
-static void vgacon_putcs(struct vc_data *conp, const char *s, int count,
-			 int ypos, int xpos)
-{
-    u16 *p;
-    u16 sattr;
-
-    if (console_blanked)
-	return;
-
-    p = (u16 *)(vga_video_mem_base+ypos*vga_video_size_row+xpos*2);
-    sattr = conp->vc_attr << 8;
-    while (count--)
-	vga_writew(sattr | ((int) (*s++) & 0xff), p++);
-}
-
-
-static void vgacon_cursor(struct vc_data *conp, int mode)
+static void vgacon_cursor(struct vc_data *c, int mode)
 {
+    if (c->vc_origin != c->vc_visible_origin)
+	vgacon_scrolldelta(c, 0);
     switch (mode) {
 	case CM_ERASE:
-	    write_vga(14, (vga_video_mem_term - vga_video_mem_base - 1)>>1);
+	    write_vga(14, (vga_vram_end - vga_vram_base - 1)/2);
 	    break;
 
 	case CM_MOVE:
 	case CM_DRAW:
-	    write_vga(14, conp->vc_y*vga_video_num_columns+conp->vc_x);
+	    write_vga(14, (c->vc_pos-vga_vram_base)/2);
 	    break;
     }
 }
 
 
-static void vgacon_scroll(struct vc_data *conp, int t, int b, int dir,
-			  int count)
+static int vgacon_switch(struct vc_data *c)
 {
-    if (console_blanked)
-	return;
+	/*
+	 * We need to save screen size here as it's the only way
+	 * we can spot the screen has been resized and we need to
+	 * set size of freshly allocated screens ourselves.
+	 */
+	vga_video_num_columns = c->vc_cols;
+	vga_video_num_lines = c->vc_rows;
+	scr_memcpyw_to((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf, c->vc_screenbuf_size);
+	return 0;	/* Redrawing not needed */
+}
 
-    vgacon_cursor(conp, CM_ERASE);
 
-    switch (dir) {
-	case SM_UP:
-	    if (count > conp->vc_rows)	/* Maximum realistic size */
-		count = conp->vc_rows;
-	    vgacon_bmove(conp, t+count, 0, t, 0, b-t-count, conp->vc_cols);
-	    vgacon_clear(conp, b-count, 0, count, conp->vc_cols);
-	    break;
+static int vgacon_blank(int blank)
+{
+    /* FIXME: Implement! */
+    /* FIXME: Check if we really ignore everything when the console is blanked. */
+    if (blank) {
+	scr_memsetw((void *)vga_vram_base, BLANK, vc_cons[0].d->vc_screenbuf_size);
+	return 0;
+    } else {
+	/* Tell console.c that it has to restore the screen itself */
+	return 1;
+    }
+    return 0;
+}
 
-	case SM_DOWN:
-	    if (count > conp->vc_rows)	/* Maximum realistic size */
-		count = conp->vc_rows;
-	    /*
-	     *  Fixed bmove() should end Arno's frustration with copying?
-	     *  Confucius says:
-	     *	Man who copies in wrong direction, end up with trashed
-	     *	data
-	     */
-	    vgacon_bmove(conp, t, 0, t+count, 0, b-t-count, conp->vc_cols);
-	    vgacon_clear(conp, t, 0, count, conp->vc_cols);
-	    break;
 
-	case SM_LEFT:
-	    vgacon_bmove(conp, 0, t+count, 0, t, conp->vc_rows, b-t-count);
-	    vgacon_clear(conp, 0, b-count, conp->vc_rows, count);
-	    break;
+/*
+ * PIO_FONT support.
+ *
+ * The font loading code goes back to the codepage package by
+ * Joel Hoffman (joel@wam.umd.edu). (He reports that the original
+ * reference is: "From: p. 307 of _Programmer's Guide to PC & PS/2
+ * Video Systems_ by Richard Wilton. 1987.  Microsoft Press".)
+ *
+ * Change for certain monochrome monitors by Yury Shevchuck
+ * (sizif@botik.yaroslavl.su).
+ */
 
-	case SM_RIGHT:
-	    vgacon_bmove(conp, 0, t, 0, t+count, conp->vc_rows, b-t-count);
-	    vgacon_clear(conp, 0, t, conp->vc_rows, count);
-	    break;
-    }
-}
+#define colourmap 0xa0000
+/* Pauline Middelink <middelin@polyware.iaf.nl> reports that we
+   should use 0xA0000 for the bwmap as well.. */
+#define blackwmap 0xa0000
+#define cmapsz 8192
+#define attrib_port (0x3c0)
+#define seq_port_reg (0x3c4)
+#define seq_port_val (0x3c5)
+#define gr_port_reg (0x3ce)
+#define gr_port_val (0x3cf)
+
+static int
+vgacon_font_op(char *arg, int set)
+{
+#ifdef CAN_LOAD_EGA_FONTS
+	int ch512 = video_mode_512ch;
+	static int ch512enabled = 0;
+	int i;
+	char *charmap;
+	int beg;
+	unsigned short video_port_status = vga_video_port_reg + 6;
+	int font_select = 0x00;
+
+	if (vga_video_type == VIDEO_TYPE_EGAC || vga_video_type == VIDEO_TYPE_VGAC) {
+		charmap = (char *)VGA_MAP_MEM(colourmap);
+		beg = 0x0e;
+#ifdef VGA_CAN_DO_64KB
+		if (video_type == VIDEO_TYPE_VGAC)
+			beg = 0x06;
+#endif
+	} else if (vga_video_type == VIDEO_TYPE_EGAM) {
+		charmap = (char *)VGA_MAP_MEM(blackwmap);
+		beg = 0x0a;
+	} else
+		return -EINVAL;
+	
+#ifdef BROKEN_GRAPHICS_PROGRAMS
+	/*
+	 * All fonts are loaded in slot 0 (0:1 for 512 ch)
+	 */
 
+	if (!arg)
+		return -EINVAL;		/* Return to default font not supported */
 
-static void vgacon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
-			 int height, int width)
-{
-    unsigned long src, dst;
-    int rows;
+	vga_font_is_default = 0;
+	font_select = ch512 ? 0x04 : 0x00;
+#else	
+	/*
+	 * The default font is kept in slot 0 and is never touched.
+	 * A custom font is loaded in slot 2 (256 ch) or 2:3 (512 ch)
+	 */
+
+	if (set) {
+		vga_font_is_default = !arg;
+		if (!arg)
+			ch512 = 0;		/* Default font is always 256 */
+		font_select = arg ? (ch512 ? 0x0e : 0x0a) : 0x00;
+	}
 
-    if (console_blanked)
-	return;
+	if ( !vga_font_is_default )
+		charmap += 4*cmapsz;
+#endif
 
-    if (sx == 0 && dx == 0 && width == vga_video_num_columns) {
-	src = vga_video_mem_base + sy * vga_video_size_row;
-	dst = vga_video_mem_base + dy * vga_video_size_row;
-	vga_memmovew((u16 *)dst, (u16 *)src, height * width);
-    } else if (dy < sy || (dy == sy && dx < sx)) {
-	src = vga_video_mem_base + sy * vga_video_size_row + sx * 2;
-	dst = vga_video_mem_base + dy * vga_video_size_row + dx * 2;
-	for (rows = height; rows-- ;) {
-	    vga_memmovew((u16 *)dst, (u16 *)src, width);
-	    src += vga_video_size_row;
-	    dst += vga_video_size_row;
+	cli();
+	outb_p( 0x00, seq_port_reg );   /* First, the sequencer */
+	outb_p( 0x01, seq_port_val );   /* Synchronous reset */
+	outb_p( 0x02, seq_port_reg );
+	outb_p( 0x04, seq_port_val );   /* CPU writes only to map 2 */
+	outb_p( 0x04, seq_port_reg );
+	outb_p( 0x07, seq_port_val );   /* Sequential addressing */
+	outb_p( 0x00, seq_port_reg );
+	outb_p( 0x03, seq_port_val );   /* Clear synchronous reset */
+
+	outb_p( 0x04, gr_port_reg );    /* Now, the graphics controller */
+	outb_p( 0x02, gr_port_val );    /* select map 2 */
+	outb_p( 0x05, gr_port_reg );
+	outb_p( 0x00, gr_port_val );    /* disable odd-even addressing */
+	outb_p( 0x06, gr_port_reg );
+	outb_p( 0x00, gr_port_val );    /* map start at A000:0000 */
+	sti();
+	
+	if (arg) {
+		if (set)
+			for (i=0; i<cmapsz ; i++)
+				vga_writeb(arg[i], charmap + i);
+		else
+			for (i=0; i<cmapsz ; i++)
+				arg[i] = vga_readb(charmap + i);
+
+		/*
+		 * In 512-character mode, the character map is not contiguous if
+		 * we want to remain EGA compatible -- which we do
+		 */
+
+		if (ch512) {
+			charmap += 2*cmapsz;
+			arg += cmapsz;
+			if (set)
+				for (i=0; i<cmapsz ; i++)
+					vga_writeb(arg[i], charmap+i);
+			else
+				for (i=0; i<cmapsz ; i++)
+					arg[i] = vga_readb(charmap+i);
+		}
 	}
-    } else {
-	src = vga_video_mem_base + (sy+height-1) * vga_video_size_row + sx * 2;
-	dst = vga_video_mem_base + (dy+height-1) * vga_video_size_row + dx * 2;
-	for (rows = height; rows-- ;) {
-	    vga_memmovew((u16 *)dst, (u16 *)src, width);
-	    src -= vga_video_size_row;
-	    dst -= vga_video_size_row;
+	
+	cli();
+	outb_p( 0x00, seq_port_reg );   /* First, the sequencer */
+	outb_p( 0x01, seq_port_val );   /* Synchronous reset */
+	outb_p( 0x02, seq_port_reg );
+	outb_p( 0x03, seq_port_val );   /* CPU writes to maps 0 and 1 */
+	outb_p( 0x04, seq_port_reg );
+	outb_p( 0x03, seq_port_val );   /* odd-even addressing */
+	if (set) {
+		outb_p( 0x03, seq_port_reg ); /* Character Map Select */
+		outb_p( font_select, seq_port_val );
 	}
-    }
-}
+	outb_p( 0x00, seq_port_reg );
+	outb_p( 0x03, seq_port_val );   /* clear synchronous reset */
 
+	outb_p( 0x04, gr_port_reg );    /* Now, the graphics controller */
+	outb_p( 0x00, gr_port_val );    /* select map 0 for CPU */
+	outb_p( 0x05, gr_port_reg );
+	outb_p( 0x10, gr_port_val );    /* enable even-odd addressing */
+	outb_p( 0x06, gr_port_reg );
+	outb_p( beg, gr_port_val );     /* map starts at b800:0 or b000:0 */
+
+	/* if 512 char mode is already enabled don't re-enable it. */
+	if ((set)&&(ch512!=ch512enabled)) {	/* attribute controller */
+		ch512enabled=ch512;
+		/* 256-char: enable intensity bit
+		   512-char: disable intensity bit */
+		inb_p( video_port_status );	/* clear address flip-flop */
+		outb_p ( 0x12, attrib_port ); /* color plane enable register */
+		outb_p ( ch512 ? 0x07 : 0x0f, attrib_port );
+		/* Wilton (1987) mentions the following; I don't know what
+		   it means, but it works, and it appears necessary */
+		inb_p( video_port_status );
+		outb_p ( 0x20, attrib_port );
+	}
+	sti();
 
-static int vgacon_switch(struct vc_data *conp)
-{
-    return 0;
+	return 0;
+#else
+	return -EINVAL;
+#endif
 }
 
-
-static int vgacon_blank(int blank)
+/*
+ * Adjust the screen to fit a font of a certain height
+ */
+static int
+vgacon_adjust_height(unsigned fontheight)
 {
-    if (blank) {
-	vga_memsetw((void *)vga_video_mem_base, BLANK, vga_video_screen_size/2);
+	int rows, maxscan;
+	unsigned char ovr, vde, fsr, curs, cure;
+
+	if (fontheight > 32 || (vga_video_type != VIDEO_TYPE_VGAC &&
+	    vga_video_type != VIDEO_TYPE_EGAC && vga_video_type != VIDEO_TYPE_EGAM))
+		return -EINVAL;
+
+	if (fontheight == video_font_height)
+		return 0;
+
+	video_font_height = fontheight;
+
+	rows = video_scan_lines/fontheight;	/* Number of video rows we end up with */
+	maxscan = rows*fontheight - 1;		/* Scan lines to actually display-1 */
+
+	/* Reprogram the CRTC for the new font size
+	   Note: the attempt to read the overflow register will fail
+	   on an EGA, but using 0xff for the previous value appears to
+	   be OK for EGA text modes in the range 257-512 scan lines, so I
+	   guess we don't need to worry about it.
+
+	   The same applies for the spill bits in the font size and cursor
+	   registers; they are write-only on EGA, but it appears that they
+	   are all don't care bits on EGA, so I guess it doesn't matter. */
+
+	cli();
+	outb_p( 0x07, vga_video_port_reg );		/* CRTC overflow register */
+	ovr = inb_p(vga_video_port_val);
+	outb_p( 0x09, vga_video_port_reg );		/* Font size register */
+	fsr = inb_p(vga_video_port_val);
+	outb_p( 0x0a, vga_video_port_reg );		/* Cursor start */
+	curs = inb_p(vga_video_port_val);
+	outb_p( 0x0b, vga_video_port_reg );		/* Cursor end */
+	cure = inb_p(vga_video_port_val);
+	sti();
+
+	vde = maxscan & 0xff;			/* Vertical display end reg */
+	ovr = (ovr & 0xbd) +			/* Overflow register */
+	      ((maxscan & 0x100) >> 7) +
+	      ((maxscan & 0x200) >> 3);
+	fsr = (fsr & 0xe0) + (fontheight-1);    /*  Font size register */
+	curs = (curs & 0xc0) + fontheight - (fontheight < 10 ? 2 : 3);
+	cure = (cure & 0xe0) + fontheight - (fontheight < 10 ? 1 : 2);
+
+	cli();
+	outb_p( 0x07, vga_video_port_reg );		/* CRTC overflow register */
+	outb_p( ovr, vga_video_port_val );
+	outb_p( 0x09, vga_video_port_reg );		/* Font size */
+	outb_p( fsr, vga_video_port_val );
+	outb_p( 0x0a, vga_video_port_reg );		/* Cursor start */
+	outb_p( curs, vga_video_port_val );
+	outb_p( 0x0b, vga_video_port_reg );		/* Cursor end */
+	outb_p( cure, vga_video_port_val );
+	outb_p( 0x12, vga_video_port_reg );		/* Vertical display limit */
+	outb_p( vde, vga_video_port_val );
+	sti();
+
+	vc_resize_all(rows, 0);			/* Adjust console size */
 	return 0;
-    } else {
-	/* Tell console.c that it has to restore the screen itself */
-	return 1;
-    }
-    return 0;
 }
 
-
-static int vgacon_get_font(struct vc_data *conp, int *w, int *h, char *data)
+static int vgacon_get_font(struct vc_data *c, int *w, int *h, char *data)
 {
-    /* TODO */
-    return -ENOSYS;
+	*w = 8;
+	*h = video_font_height;
+	return vgacon_font_op(data, 0);
 }
 
-
-static int vgacon_set_font(struct vc_data *conp, int w, int h, char *data)
+static int vgacon_set_font(struct vc_data *c, int w, int h, char *data)
 {
-    /* TODO */
-    return -ENOSYS;
+	int rc;
+	if (w != 8 || h > 32)
+		return -EINVAL;
+	rc = vgacon_font_op(data, 1);
+	if (!rc)
+		rc = vgacon_adjust_height(h);
+	return rc;
 }
 
-static int vgacon_set_palette(struct vc_data *conp, unsigned char *table)
+static int vgacon_set_palette(struct vc_data *c, unsigned char *table)
 {
+#ifdef CAN_LOAD_PALETTE
 	int i, j ;
 
-	if (vga_video_type != VIDEO_TYPE_VGAC || console_blanked ||
-	    vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+	if (vga_video_type != VIDEO_TYPE_VGAC || console_blanked)
 		return -EINVAL;
 
 	for (i=j=0; i<16; i++) {
 		outb_p (table[i], dac_reg) ;
-		outb_p (vc_cons[fg_console].d->vc_palette[j++]>>2, dac_val) ;
-		outb_p (vc_cons[fg_console].d->vc_palette[j++]>>2, dac_val) ;
-		outb_p (vc_cons[fg_console].d->vc_palette[j++]>>2, dac_val) ;
+		outb_p (c->vc_palette[j++]>>2, dac_val) ;
+		outb_p (c->vc_palette[j++]>>2, dac_val) ;
+		outb_p (c->vc_palette[j++]>>2, dac_val) ;
 	}
 	return 0;
+#else
+	return -EINVAL;
+#endif
 }
 
-static int vgacon_scrolldelta(struct vc_data *conp, int lines)
+static int vgacon_scrolldelta(struct vc_data *c, int lines)
 {
-    /* TODO */
-    return -ENOSYS;
+	/* FIXME: Better scrollback strategy, maybe move it to generic code
+	 *	  and leave only vga_set_mem_top here.
+	 */
+	if (!lines)			/* Turn scrollback off */
+		c->vc_visible_origin = c->vc_origin;
+	else {
+		int p = c->vc_visible_origin - vga_vram_base;
+		p += lines * c->vc_size_row;
+		if (p < 0)
+			p = 0;
+		c->vc_visible_origin = p + vga_vram_base;
+		if (c->vc_visible_origin > c->vc_origin)
+			c->vc_visible_origin = c->vc_origin;
+	}
+	vga_set_mem_top(c);
+	return 1;
 }
 
-__initfunc(static int vgacon_show_logo( void ))
+static int vgacon_set_origin(struct vc_data *c)
 {
-    int height = 0;
-    char *p;
+	c->vc_origin = c->vc_visible_origin = vga_vram_base;
+	vga_set_mem_top(c);
+	return 1;
+}
 
-    printk(linux_serial_image);
-    for (p = linux_serial_image; *p; p++)
-	if (*p == '\n')
-	    height++;
-    return height;
+static void vgacon_save_screen(struct vc_data *c)
+{
+	static int vga_bootup_console = 0;
+
+	if (!vga_bootup_console) {
+		/* This is a gross hack, but here is the only place we can
+		 * set bootup console parameters without messing up generic
+		 * console initialization routines.
+		 */
+		vga_bootup_console = 1;
+		c->vc_x = ORIG_X;
+		c->vc_y = ORIG_Y;
+	}
+	scr_memcpyw_from((u16 *) c->vc_screenbuf, (u16 *) c->vc_origin, c->vc_screenbuf_size);
+}
+
+static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, int lines)
+{
+	unsigned long oldo;
+	
+	if (t || b != c->vc_rows)
+		return 0;
+
+	if (c->vc_origin != c->vc_visible_origin)
+		vgacon_scrolldelta(c, 0);
+
+	/* FIXME: Handle scrolling down or by more lines? */
+	if (!vga_hardscroll_enabled || dir != SM_UP || lines != 1)
+		return 0;
+
+	oldo = c->vc_origin;
+	if (c->vc_scr_end + c->vc_size_row >= vga_vram_end) {
+		scr_memcpyw((u16 *)vga_vram_base,
+			    (u16 *)(oldo + c->vc_size_row),
+			    c->vc_screenbuf_size - c->vc_size_row);
+		c->vc_origin = vga_vram_base;
+	} else
+		c->vc_origin += c->vc_size_row;
+	c->vc_visible_origin = c->vc_origin;
+	c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
+	scr_memsetw((u16 *)(c->vc_scr_end - c->vc_size_row), c->vc_video_erase_char, c->vc_size_row);
+	vga_set_mem_top(c);
+	c->vc_pos = (c->vc_pos - oldo) + c->vc_origin;
+	return 1;
 }
 
 
@@ -572,9 +677,29 @@
  *  The console `switch' structure for the VGA based console
  */
 
+static int vgacon_dummy(struct vc_data *c)
+{
+	return 0;
+}
+
+#define DUMMY (void *) vgacon_dummy
+
 struct consw vga_con = {
-    vgacon_startup, vgacon_init, vgacon_deinit, vgacon_clear, vgacon_putc,
-    vgacon_putcs, vgacon_cursor, vgacon_scroll, vgacon_bmove, vgacon_switch,
-    vgacon_blank, vgacon_get_font, vgacon_set_font, vgacon_set_palette,
-    vgacon_scrolldelta
+	vgacon_startup,
+	vgacon_init,
+	DUMMY,				/* con_deinit */
+	DUMMY,				/* con_clear */
+	DUMMY,				/* con_putc */
+	DUMMY,				/* con_putcs */
+	vgacon_cursor,
+	vgacon_scroll,			/* con_scroll */
+	DUMMY,				/* con_bmove */
+	vgacon_switch,
+	vgacon_blank,
+	vgacon_get_font,
+	vgacon_set_font,
+	vgacon_set_palette,
+	vgacon_scrolldelta,
+	vgacon_set_origin,
+	vgacon_save_screen
 };
diff -u --recursive --new-file v2.1.108/linux/drivers/video/vgafb.c linux/drivers/video/vgafb.c
--- v2.1.108/linux/drivers/video/vgafb.c	Wed Jun 24 22:54:08 1998
+++ linux/drivers/video/vgafb.c	Fri Jul 10 15:18:32 1998
@@ -38,6 +38,7 @@
 #include <linux/init.h>
 #include <linux/selection.h>
 #include <linux/console.h>
+#include <linux/vt_kern.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -103,6 +104,40 @@
 #endif /* !__powerpc__ */	
 }
 
+/*
+ * By replacing the four outb_p with two back to back outw, we can reduce
+ * the window of opportunity to see text mislocated to the RHS of the
+ * console during heavy scrolling activity. However there is the remote
+ * possibility that some pre-dinosaur hardware won't like the back to back
+ * I/O. Since the Xservers get away with it, we should be able to as well.
+ */
+static inline void write_vga(unsigned char reg, unsigned int val)
+{
+#ifndef SLOW_VGA
+    unsigned int v1, v2;
+
+    v1 = reg + (val & 0xff00);
+    v2 = reg + 1 + ((val << 8) & 0xff00);
+    outw(v1, vga_video_port_reg);
+    outw(v2, vga_video_port_reg);
+#else
+    outb_p(reg, vga_video_port_reg);
+    outb_p(val >> 8, vga_video_port_val);
+    outb_p(reg+1, vga_video_port_reg);
+    outb_p(val & 0xff, vga_video_port_val);
+#endif
+}
+
+static inline void vga_set_origin(unsigned short location)
+{
+	write_vga(12, location >> 1);
+}
+
+static inline void vga_set_cursor(int location)
+{
+    write_vga(14, location >> 1);
+}
+
 static void vga_set_split(unsigned short linenum)
 {
 	unsigned long flags;
@@ -136,34 +171,19 @@
 	restore_flags(flags);
 }
 
-
-/*
- * By replacing the four outb_p with two back to back outw, we can reduce
- * the window of opportunity to see text mislocated to the RHS of the
- * console during heavy scrolling activity. However there is the remote
- * possibility that some pre-dinosaur hardware won't like the back to back
- * I/O. Since the Xservers get away with it, we should be able to as well.
- */
-static inline void write_vga(unsigned char reg, unsigned int val)
+static inline void vga_set_palreg(u_int regno, u_int red, 
+				  u_int green, u_int blue)
 {
-#ifndef SLOW_VGA
-    unsigned int v1, v2;
-
-    v1 = reg + (val & 0xff00);
-    v2 = reg + 1 + ((val << 8) & 0xff00);
-    outw(v1, vga_video_port_reg);
-    outw(v2, vga_video_port_reg);
-#else
-    outb_p(reg, vga_video_port_reg);
-    outb_p(val >> 8, vga_video_port_val);
-    outb_p(reg+1, vga_video_port_reg);
-    outb_p(val & 0xff, vga_video_port_val);
-#endif
-}
+	unsigned long flags;
 
-static inline void vga_set_origin(unsigned short offset)
-{
-	write_vga(12, offset);
+	save_flags(flags); cli();
+	
+	outb_p(regno, dac_reg);
+	outb_p(red,   dac_val);
+	outb_p(green, dac_val);
+	outb_p(blue,  dac_val);
+	
+	restore_flags(flags);
 }
 
 
@@ -171,8 +191,8 @@
      *  Interface used by the world
      */
 
-static int vgafb_open(struct fb_info *info);
-static int vgafb_release(struct fb_info *info);
+static int vgafb_open(struct fb_info *info, int user);
+static int vgafb_release(struct fb_info *info, int user);
 static int vgafb_get_fix(struct fb_fix_screeninfo *fix, int con,
 			 struct fb_info *info);
 static int vgafb_get_var(struct fb_var_screeninfo *var, int con,
@@ -193,7 +213,7 @@
      *  Interface to the low level console driver
      */
 
-unsigned long vgafb_init(unsigned long mem_start);
+void vgafb_init(void);
 void vgafb_setup(char *options, int *ints);
 static int vgafbcon_switch(int con, struct fb_info *info);
 static int vgafbcon_updatevar(int con, struct fb_info *info);
@@ -228,7 +248,7 @@
      *  Open/Release the frame buffer device
      */
 
-static int vgafb_open(struct fb_info *info)
+static int vgafb_open(struct fb_info *info, int user)
 
 {
     /*
@@ -239,7 +259,7 @@
     return(0);
 }
 
-static int vgafb_release(struct fb_info *info)
+static int vgafb_release(struct fb_info *info, int user)
 {
     MOD_DEC_USE_COUNT;
     return(0);
@@ -297,8 +317,9 @@
     if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
 	oldbpp = display->var.bits_per_pixel;
 	display->var = *var;
-	vga_set_origin(var->yoffset/video_font_height*fb_fix.line_length/2);
+	vga_set_origin(var->yoffset/video_font_height*fb_fix.line_length);
     }
+
     if (oldbpp != var->bits_per_pixel) {
 	if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 	    return err;
@@ -320,7 +341,7 @@
     if (var->xoffset || var->yoffset+var->yres > var->yres_virtual)
 	return -EINVAL;
 
-    vga_set_origin(var->yoffset/video_font_height*fb_fix.line_length/2);
+    vga_set_origin(var->yoffset/video_font_height*fb_fix.line_length);
     return 0;
 }
 
@@ -379,21 +400,16 @@
      *  Move hardware vga cursor
      */
 
-static void vga_write_cursor(int location)
-{
-    write_vga(14, location >> 1);
-}
-
 static void fbcon_vgafb_cursor(struct display *p, int mode, int x, int y)
 {
     switch (mode) {
 	case CM_ERASE:
-	   vga_write_cursor(vga_video_mem_term - vga_video_mem_base - 1);
+	   vga_set_cursor(vga_video_mem_term - vga_video_mem_base - 1);
 	   break;
 
 	case CM_MOVE:
 	case CM_DRAW:
-	   vga_write_cursor(y*p->next_line + (x << 1));
+	   vga_set_cursor(y*p->next_line + (x << 1));
 	   break;
     }
 }
@@ -403,14 +419,13 @@
      *  Initialisation
      */
 
-__initfunc(unsigned long vgafb_init(unsigned long mem_start))
+__initfunc(void vgafb_init(void))
 {
-    int err;
     u16 saved;
     u16 *p;
 
     if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB)
-	return mem_start;
+	return;
 
     vga_video_num_lines = ORIG_VIDEO_LINES;
     vga_video_num_columns = ORIG_VIDEO_COLS;
@@ -480,10 +495,8 @@
 		 * default values */
 
 		for (i = 0; i < 16; i++) {
-		    outb_p(color_table[i], dac_reg);
-		    outb_p(default_red[i], dac_val);
-		    outb_p(default_grn[i], dac_val);
-		    outb_p(default_blu[i], dac_val);
+		    vga_set_palreg(color_table[i], default_red[i],
+		    		   default_grn[i], default_blu[i]);
 		}
 	    }
 	} else {
@@ -503,12 +516,12 @@
     vga_writew(0xAA55, p);
     if (vga_readw(p) != 0xAA55) {
 	vga_writew(saved, p);
-	return mem_start;
+	return;
     }
     vga_writew(0x55AA, p);
     if (vga_readw(p) != 0x55AA) {
 	vga_writew(saved, p);
-	return mem_start;
+	return;
     }
     vga_writew(saved, p);
 
@@ -525,8 +538,8 @@
 
     fb_fix.smem_start = (char *) vga_video_mem_base;
     fb_fix.smem_len = vga_video_mem_term - vga_video_mem_base;
-    fb_fix.type = FB_TYPE_VGA_TEXT;
-    fb_fix.type_aux = 0;
+    fb_fix.type = FB_TYPE_TEXT;
+    fb_fix.type_aux = vga_can_do_color ? FB_AUX_TEXT_CGA : FB_AUX_TEXT_MDA;
     fb_fix.visual = FB_VISUAL_PSEUDOCOLOR;
     fb_fix.xpanstep = 0;
     fb_fix.ypanstep = video_font_height;
@@ -606,16 +619,13 @@
     fb_info.updatevar = &vgafbcon_updatevar;
     fb_info.blank = &vgafbcon_blank;
 
-    err = register_framebuffer(&fb_info);
-    if (err < 0)
-	return mem_start;
-
     vgafb_set_var(&fb_var, -1, &fb_info);
 
+    if (register_framebuffer(&fb_info) < 0)
+	return;
+
     printk("fb%d: VGA frame buffer device, using %dK of video memory\n",
 	   GET_FB_IDX(fb_info.node), fb_fix.smem_len>>10);
-
-    return mem_start;
 }
 
 __initfunc(void vgafb_setup(char *options, int *ints))
@@ -635,7 +645,7 @@
 		/* hardware scrolling */
 
 		vga_set_origin(var->yoffset / video_font_height *
-			fb_fix.line_length / 2);
+			fb_fix.line_length);
 
 		vga_set_split(var->yres - ((var->vmode & FB_VMODE_YWRAP) ?
 			var->yoffset+1 : 0));
@@ -666,18 +676,14 @@
 {
     int i;
 
-    outb_p(0, dac_reg);
     if (blank)
 	for (i = 0; i < 16; i++) {
-	    outb_p(0, dac_val);
-	    outb_p(0, dac_val);
-	    outb_p(0, dac_val);
+	    vga_set_palreg(i, 0, 0, 0);
 	}
     else
 	for (i = 0; i < 16; i++) {
-	    outb_p(palette[i].blue, dac_val);
-	    outb_p(palette[i].green, dac_val);
-	    outb_p(palette[i].blue, dac_val);
+	    vga_set_palreg(i, palette[i].red, palette[i].green,
+			      palette[i].blue);
 	}
 }
 
@@ -714,10 +720,7 @@
     palette[regno].green = green;
     palette[regno].blue = blue;
 
-    outb_p(regno, dac_reg);
-    outb_p(red, dac_val);
-    outb_p(green, dac_val);
-    outb_p(blue, dac_val);
+    vga_set_palreg(regno, red, green, blue);
 
     return 0;
 }
@@ -744,3 +747,17 @@
     fbcon_vga_setup, fbcon_vga_bmove, fbcon_vga_clear, fbcon_vga_putc,
     fbcon_vga_putcs, fbcon_vga_revc, fbcon_vgafb_cursor
 };
+
+
+#ifdef MODULE
+int init_module(void)
+{
+    vgafb_init();
+    return 0;
+}
+
+void cleanup_module(void)
+{
+    unregister_framebuffer(&fb_info);
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.108/linux/drivers/video/virgefb.c linux/drivers/video/virgefb.c
--- v2.1.108/linux/drivers/video/virgefb.c	Wed Jun 24 22:54:08 1998
+++ linux/drivers/video/virgefb.c	Fri Jul 10 15:18:32 1998
@@ -238,8 +238,8 @@
 
 void virgefb_setup(char *options, int *ints);
 
-static int virgefb_open(struct fb_info *info);
-static int virgefb_release(struct fb_info *info);
+static int virgefb_open(struct fb_info *info, int user);
+static int virgefb_release(struct fb_info *info, int user);
 static int virgefb_get_fix(struct fb_fix_screeninfo *fix, int con, struct
 fb_info *info);
 static int virgefb_get_var(struct fb_var_screeninfo *var, int con, struct
@@ -260,7 +260,7 @@
  *    Interface to the low level console driver
  */
 
-unsigned long virgefb_init(unsigned long mem_start);
+void virgefb_init(void);
 static int Cyberfb_switch(int con, struct fb_info *info);
 static int Cyberfb_updatevar(int con, struct fb_info *info);
 static void Cyberfb_blank(int blank, struct fb_info *info);
@@ -765,7 +765,7 @@
  *  Open/Release the frame buffer device
  */
 
-static int virgefb_open(struct fb_info *info)
+static int virgefb_open(struct fb_info *info, int user)
 {
 	/*
 	 * Nothing, only a usage count for the moment
@@ -775,7 +775,7 @@
 	return(0);
 }
 
-static int virgefb_release(struct fb_info *info)
+static int virgefb_release(struct fb_info *info, int user)
 {
 	MOD_DEC_USE_COUNT;
 	return(0);
@@ -1010,15 +1010,14 @@
  *    Initialization
  */
 
-__initfunc(unsigned long virgefb_init(unsigned long mem_start))
+__initfunc(void virgefb_init(void))
 {
-	int err;
 	struct virgefb_par par;
 	unsigned long board_addr;
 	const struct ConfigDev *cd;
 
 	if (!(CyberKey = zorro_find(ZORRO_PROD_PHASE5_CYBERVISION64_3D, 0, 0)))
-		return mem_start;
+		return;
 
 	cd = zorro_get_board (CyberKey);
 	zorro_config_board (CyberKey, 0);
@@ -1033,22 +1032,17 @@
 
 		CyberMem = ZTWO_VADDR(board_addr);
 		printk("CV3D detected running in Z2 mode ... not yet supported!\n");
-		return mem_start;
+		return;
 	}
 	else
 	{
-		CyberVGARegs = kernel_map(board_addr +0x0c000000,
-					       0x00010000,
-					       KERNELMAP_NOCACHE_SER,
-					       &mem_start);
+		CyberVGARegs = kernel_map(board_addr +0x0c000000, 0x00010000,
+					       KERNELMAP_NOCACHE_SER, NULL);
 		CyberRegs = (char *)kernel_map(board_addr +0x05000000,
 					       0x00010000,
-					       KERNELMAP_NOCACHE_SER,
-					       &mem_start);
-		CyberMem = kernel_map(board_addr + 0x04800000,
-				      0x00400000,
-				      KERNELMAP_NOCACHE_SER,
-				      &mem_start);
+					       KERNELMAP_NOCACHE_SER, NULL);
+		CyberMem = kernel_map(board_addr + 0x04800000, 0x00400000,
+				      KERNELMAP_NOCACHE_SER, NULL);
 		printk("CV3D detected running in Z3 mode\n");
 	}
 
@@ -1063,10 +1057,6 @@
 	fb_info.updatevar = &Cyberfb_updatevar;
 	fb_info.blank = &Cyberfb_blank;
 
-	err = register_framebuffer(&fb_info);
-	if (err < 0)
-		return mem_start;
-
 	fbhw->init();
 	fbhw->decode_var(&virgefb_default, &par);
 	fbhw->encode_var(&virgefb_default, &par);
@@ -1076,13 +1066,14 @@
 	virgefb_set_disp(-1, &fb_info);
 	do_install_cmap(0, &fb_info);
 
+	if (register_framebuffer(&fb_info) < 0)
+		return;
+
 	printk("fb%d: %s frame buffer device, using %ldK of video memory\n",
 	       GET_FB_IDX(fb_info.node), fb_info.modename, CyberSize>>10);
 
 	/* TODO: This driver cannot be unloaded yet */
 	MOD_INC_USE_COUNT;
-
-	return mem_start;
 }
 
 
@@ -1180,7 +1171,8 @@
 #ifdef MODULE
 int init_module(void)
 {
-	return(virgefb_init(NULL));
+	virgefb_init();
+	return 0;
 }
 
 void cleanup_module(void)
diff -u --recursive --new-file v2.1.108/linux/fs/coda/cache.c linux/fs/coda/cache.c
--- v2.1.108/linux/fs/coda/cache.c	Sun Jun  7 11:16:36 1998
+++ linux/fs/coda/cache.c	Fri Jul 10 15:33:37 1998
@@ -37,8 +37,10 @@
 {
 	struct coda_sb_info *sbi = coda_sbp(sb);
 	ENTRY;
-	if ( !sbi ||  !el) {
-		printk("coda_ccinsert: NULL sbi or el!\n");
+	/* third test verifies cc was initialized before adding it 
+	   to the sblist. Probably superfluous */
+	if ( !sbi || !el || !list_empty(&el->cc_cclist) ) {
+		printk("coda_ccinsert: NULL sbi or el->cc_cclist not empty!\n");
 		return ;
 	}
 
@@ -49,8 +51,8 @@
 static void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cii)
 {
 	ENTRY;
-	if ( !cii ||  !el) {
-		printk("coda_cninsert: NULL cii or el!\n");
+	if ( !cii ||  !el || ! list_empty(&el->cc_cnlist)) {
+		printk("coda_cninsert: NULL cii or el->cc_cnlist not empty!\n");
 		return ;
 	}
 	list_add(&el->cc_cnlist, &cii->c_cnhead);
@@ -60,20 +62,20 @@
 static void coda_ccremove(struct coda_cache *el)
 {
 	ENTRY;
-        if (el->cc_cclist.next && el->cc_cclist.prev)
+        if ( ! list_empty(&el->cc_cclist) )
 	        list_del(&el->cc_cclist);
 	else
-		printk("coda_cnremove: trying to remove 0 entry!");
+		printk("coda_cnremove: loose cc entry!");
 }
 
 /* remove a cache entry from the inode's list */
 static void coda_cnremove(struct coda_cache *el)
 {
 	ENTRY;
-	if (el->cc_cnlist.next && el->cc_cnlist.prev)
+	if ( ! list_empty(&el->cc_cnlist) )
 		list_del(&el->cc_cnlist);
 	else
-		printk("coda_cnremove: trying to remove 0 entry!");
+		printk("coda_cnremove: loose cn entry!");
 }
 
 /* create a new cache entry and enlist it */
@@ -83,12 +85,17 @@
 	struct super_block *sb = inode->i_sb;
 	struct coda_cache *cc = NULL;
 	ENTRY;
+
 	CODA_ALLOC(cc, struct coda_cache *, sizeof(*cc));
 
 	if ( !cc ) {
 		printk("Out of memory in coda_cache_enter!\n");
 		return;
 	}
+
+	INIT_LIST_HEAD(&cc->cc_cclist);
+	INIT_LIST_HEAD(&cc->cc_cnlist);
+
 	coda_load_creds(&cc->cc_cred);
 	cc->cc_mask = mask;
 	coda_cninsert(cc, cii);
@@ -227,15 +234,58 @@
 }
 
 
-/*   DCACHE & ZAPPING related stuff */
-
-/* the following routines set flags in the inodes. They are 
-   detected by:
-   - a dentry method: coda_dentry_revalidate (for lookups)
-     if the flag is C_PURGE
+/* Purging dentries and children */
+/* The following routines drop dentries which are not
+   in use and flag dentries which are in use to be 
+   zapped later.
+
+   The flags are detected by:
+   - coda_dentry_revalidate (for lookups) if the flag is C_PURGE
+   - coda_dentry_delete: to remove dentry from the cache when d_count
+     falls to zero
    - an inode method coda_revalidate (for attributes) if the 
-     flag is C_ATTR
+     flag is C_VATTR
 */
+
+/* 
+   Some of this is pretty scary: what can disappear underneath us?
+   - shrink_dcache_parent calls on purge_one_dentry which is safe:
+     it only purges children.
+   - dput is evil since it  may recurse up the dentry tree
+ */
+
+void coda_purge_dentries(struct inode *inode)
+{
+	struct list_head *tmp, *head = &inode->i_dentry;
+
+	if (!inode)
+		return ;
+
+	/* better safe than sorry: dput could kill us */
+	iget(inode->i_sb, inode->i_ino);
+	/* catch the dentries later if some are still busy */
+	coda_flag_inode(inode, C_PURGE);
+
+restart:
+	tmp = head;
+	while ((tmp = tmp->next) != head) {
+		struct dentry *dentry = list_entry(tmp, struct dentry, d_alias);
+		if (!dentry->d_count) {
+			CDEBUG(D_DOWNCALL, 
+			       "coda_free_dentries: freeing %s/%s, i_count=%d\n",
+			       dentry->d_parent->d_name.name, dentry->d_name.name, 
+			       inode->i_count);
+			dget(dentry);
+			d_drop(dentry);
+			dput(dentry);
+			goto restart;
+		}
+			
+	}
+	iput(inode);
+}
+
+/* this won't do any harm: just flag all children */
 static void coda_flag_children(struct dentry *parent, int flag)
 {
 	struct list_head *child;
@@ -244,37 +294,44 @@
 	child = parent->d_subdirs.next;
 	while ( child != &parent->d_subdirs ) {
 		de = list_entry(child, struct dentry, d_child);
-		coda_flag_inode(de->d_inode, flag);
-		CDEBUG(D_CACHE, "%d for %*s/%*s\n", flag, 
+		child = child->next;
+		/* don't know what to do with negative dentries */
+		if ( ! de->d_inode ) 
+			continue;
+		CDEBUG(D_DOWNCALL, "%d for %*s/%*s\n", flag, 
 		       de->d_name.len, de->d_name.name, 
 		       de->d_parent->d_name.len, de->d_parent->d_name.name);
-		child = child->next;
-		if ( !de->d_inode )
-			d_drop(de); 
+		coda_flag_inode(de->d_inode, flag);
 	}
 	return; 
 }
 
-
-void coda_flag_alias_children(struct inode *inode, int flag)
+void coda_purge_children(struct inode *inode)
 {
 	struct list_head *alias;
 	struct dentry *alias_de;
 
 	if ( !inode ) 
 		return; 
+
+	if (list_empty(&inode->i_dentry))
+	        return; 
+
+	/* I believe that shrink_dcache_parent will not
+	   remove dentries from the alias list. If it 
+	   does we are toast. 
+	*/
 	alias = inode->i_dentry.next; 
 	while ( alias != &inode->i_dentry ) {
 		alias_de = list_entry(alias, struct dentry, d_alias);
-		if ( !alias_de ) {
-			printk("Null alias list for inode %ld\n", inode->i_ino);
-			return;
-		}
-		coda_flag_children(alias_de, flag);
-		alias= alias->next;
+		coda_flag_children(alias_de, C_PURGE);
+		shrink_dcache_parent(alias_de);
+		alias = alias->next;
 	}
+
 }
 
+/* this will not zap the inode away */
 void coda_flag_inode(struct inode *inode, int flag)
 {
 	struct coda_inode_info *cii;
@@ -283,11 +340,8 @@
 		CDEBUG(D_CACHE, " no inode!\n");
 		return;
 	}
+
 	cii = ITOC(inode);
 	cii->c_flags |= flag;
 }		
-
-
-
-
 
diff -u --recursive --new-file v2.1.108/linux/fs/coda/cnode.c linux/fs/coda/cnode.c
--- v2.1.108/linux/fs/coda/cnode.c	Sun Jun  7 11:16:36 1998
+++ linux/fs/coda/cnode.c	Fri Jul 10 15:33:37 1998
@@ -30,6 +30,16 @@
                 inode->i_op = &coda_dir_inode_operations;
         else if (S_ISLNK(inode->i_mode))
                 inode->i_op = &coda_symlink_inode_operations;
+        else if (S_ISCHR(inode->i_mode)) {
+                inode->i_op = &chrdev_inode_operations;
+                inode->i_rdev = to_kdev_t(attr->va_rdev);
+        } else if (S_ISBLK(inode->i_mode)) {
+                inode->i_op = &blkdev_inode_operations;
+                inode->i_rdev = to_kdev_t(attr->va_rdev);
+        } else if (S_ISFIFO(inode->i_mode))
+                init_fifo(inode);
+	else if (S_ISSOCK(inode->i_mode))
+		inode->i_op = NULL;
         else {
                 printk ("coda_read_inode: what's this? i_mode = %o\n", 
 			inode->i_mode);
@@ -59,7 +69,7 @@
 
 	error = venus_getattr(sb, fid, &attr);
 	if ( error ) {
-	    printk("coda_cnode_make: coda_getvattr returned %d for %s.\n", 
+	    CDEBUG(D_CNODE, "coda_cnode_make: coda_getvattr returned %d for %s.\n", 
 		   error, coda_f2s(fid));
 	    *inode = NULL;
 	    return error;
@@ -92,16 +102,16 @@
 	if ( coda_f2i(fid) != ino ) {
 	        if ( !coda_fid_is_weird(fid) ) 
 		        printk("Coda: unknown weird fid: ino %ld, fid %s."
-			       "Tell Peter.", ino, coda_f2s(&cnp->c_fid));
+			       "Tell Peter.\n", ino, coda_f2s(&cnp->c_fid));
 		list_add(&cnp->c_volrootlist, &sbi->sbi_volroothead);
-		CDEBUG(D_CNODE, "Added %ld ,%s to volroothead\n",
+		CDEBUG(D_UPCALL, "Added %ld ,%s to volroothead\n",
 		       ino, coda_f2s(&cnp->c_fid));
 	}
 
         coda_fill_inode(*inode, &attr);
-	CDEBUG(D_CNODE, "Done linking: ino %ld,  at 0x%x with cnp 0x%x,"
-	       "cnp->c_vnode 0x%x\n", (*inode)->i_ino, (int) (*inode), 
-	       (int) cnp, (int)cnp->c_vnode);
+	CDEBUG(D_DOWNCALL, "Done making inode: ino %ld,  count %d with %s\n",
+	        (*inode)->i_ino, (*inode)->i_count, 
+	       coda_f2s(&cnp->c_fid));
 
         EXIT;
         return 0;
@@ -129,7 +139,6 @@
 	struct coda_inode_info *cnp;
 	ENTRY;
 
-	CDEBUG(D_INODE, "%s\n", coda_f2s(fid));
 
 	if ( !sb ) {
 		printk("coda_fid_to_inode: no sb!\n");
@@ -140,6 +149,7 @@
 		printk("coda_fid_to_inode: no fid!\n");
 		return NULL;
 	}
+	CDEBUG(D_INODE, "%s\n", coda_f2s(fid));
 
 
 	if ( coda_fid_is_weird(fid) ) {
@@ -151,9 +161,14 @@
 		while ( (le = le->next) != lh ) {
 			cii = list_entry(le, struct coda_inode_info, 
 					 c_volrootlist);
+			CDEBUG(D_DOWNCALL, "iterating, now doing %s, ino %ld\n", 
+			       coda_f2s(&cii->c_fid), cii->c_vnode->i_ino);
 			if ( coda_fideq(&cii->c_fid, fid) ) {
 				inode = cii->c_vnode;
 				CDEBUG(D_INODE, "volume root, found %ld\n", cii->c_vnode->i_ino);
+				if ( cii->c_magic != CODA_CNODE_MAGIC )
+					printk("%s: Bad magic in inode, tell Peter.\n", 
+					       __FUNCTION__);
 				return cii->c_vnode;
 			}
 			
@@ -184,8 +199,8 @@
 	   mount, but the fid will be wrong. 
 	*/
 	if ( !coda_fideq(fid, &(cnp->c_fid)) ) {
-		printk("coda_fid2inode: bad cnode (ino %ld, fid %s)"
-		       "Tell Peter.\n", nr, coda_f2s(fid));
+		/* printk("coda_fid2inode: bad cnode (ino %ld, fid %s)"
+		   "Tell Peter.\n", nr, coda_f2s(fid)); */
 		iput(inode);
 		return NULL;
 	}
diff -u --recursive --new-file v2.1.108/linux/fs/coda/coda_linux.c linux/fs/coda/coda_linux.c
--- v2.1.108/linux/fs/coda/coda_linux.c	Sun Jun  7 11:16:36 1998
+++ linux/fs/coda/coda_linux.c	Fri Jul 10 15:33:37 1998
@@ -123,20 +123,27 @@
 unsigned short coda_flags_to_cflags(unsigned short flags)
 {
 	unsigned short coda_flags = 0;
-
-	if ( (flags & 0xf) == O_RDONLY )
+	
+	if ( (flags & O_ACCMODE) == O_RDONLY ){ 
+		CDEBUG(D_FILE, "--> C_O_READ added\n");
 		coda_flags |= C_O_READ;
+	}
 
-	if ( flags &  O_RDWR )
-		coda_flags |= C_O_READ;
+	if ( (flags & O_ACCMODE) ==  O_RDWR ) { 
+		CDEBUG(D_FILE, "--> C_O_READ | C_O_WRITE added\n");
+		coda_flags |= C_O_READ | C_O_WRITE;
+	}
 
-	if ( flags & (O_WRONLY | O_RDWR) )
+	if ( (flags & O_ACCMODE) == O_WRONLY ){ 
+		CDEBUG(D_FILE, "--> C_O_WRITE added\n");
 		coda_flags |= C_O_WRITE;
+	}
 
 	if ( flags & O_TRUNC )  { 
 		CDEBUG(D_FILE, "--> C_O_TRUNC added\n");
 		coda_flags |= C_O_TRUNC;
 	}
+
 	if ( flags & O_EXCL ) {
 		coda_flags |= C_O_EXCL;
 		CDEBUG(D_FILE, "--> C_O_EXCL added\n");
@@ -181,12 +188,10 @@
 	        inode->i_nlink = attr->va_nlink;
 	if (attr->va_size != -1)
 	        inode->i_size = attr->va_size;
-	/*  XXX This needs further study */
-	/*
-        inode->i_blksize = attr->va_blocksize;
-	inode->i_blocks = attr->va_size/attr->va_blocksize 
-	  + (attr->va_size % attr->va_blocksize ? 1 : 0); 
-	  */
+	if (attr->va_blocksize != -1)
+		inode->i_blksize = attr->va_blocksize;
+	if (attr->va_size != -1)
+		inode->i_blocks = (attr->va_size + 511) >> 9;
 	if (attr->va_atime.tv_sec != -1) 
 	        inode->i_atime = attr->va_atime.tv_sec;
 	if (attr->va_mtime.tv_sec != -1)
diff -u --recursive --new-file v2.1.108/linux/fs/coda/dir.c linux/fs/coda/dir.c
--- v2.1.108/linux/fs/coda/dir.c	Wed Jun 24 22:54:08 1998
+++ linux/fs/coda/dir.c	Fri Jul 10 15:33:37 1998
@@ -27,6 +27,7 @@
 
 /* dir inode-ops */
 static int coda_create(struct inode *dir, struct dentry *new, int mode);
+static int coda_mknod(struct inode *dir, struct dentry *new, int mode, int rdev);
 static int coda_lookup(struct inode *dir, struct dentry *target);
 static int coda_link(struct dentry *old_dentry, struct inode *dir_inode, 
 		     struct dentry *entry);
@@ -42,20 +43,24 @@
 static int coda_readdir(struct file *file, void *dirent, filldir_t filldir);
 
 /* dentry ops */
-int coda_dentry_revalidate(struct dentry *de);
-
+static int coda_dentry_revalidate(struct dentry *de);
+static void coda_dentry_delete(struct dentry *);
 /* support routines */
 static int coda_venus_readdir(struct file *filp, void *dirent, 
 			      filldir_t filldir);
 int coda_fsync(struct file *, struct dentry *dentry);
 static int coda_refresh_inode(struct dentry *dentry);
 
+int coda_crossvol_rename = 0;
+int coda_hasmknod = 0;
+
+
 struct dentry_operations coda_dentry_operations =
 {
 	coda_dentry_revalidate, /* revalidate */
 	NULL, /* hash */
-	NULL,
-	NULL,
+	NULL, /* compare */
+	coda_dentry_delete /* delete */
 };
 
 struct inode_operations coda_dir_inode_operations =
@@ -68,7 +73,7 @@
 	coda_symlink,	        /* symlink */
 	coda_mkdir,	        /* mkdir */
 	coda_rmdir,   	        /* rmdir */
-	NULL,		        /* mknod */
+	coda_mknod,	        /* mknod */
 	coda_rename,	        /* rename */
 	NULL,	                /* readlink */
 	NULL,	                /* follow_link */
@@ -100,7 +105,7 @@
 
 
 /* inode operations for directories */
-/* access routines: lookup, readlink, permission */
+/* acces routines: lookup, readlink, permission */
 static int coda_lookup(struct inode *dir, struct dentry *entry)
 {
         struct coda_inode_info *dircnp;
@@ -118,7 +123,7 @@
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("coda_lookup: inode is NULL or not a directory\n");
-		return -ENOENT;
+		return -ENOTDIR;
 	}
 
 	dircnp = ITOC(dir);
@@ -154,7 +159,7 @@
 		}
 	    	error = coda_cnode_make(&res_inode, &resfid, dir->i_sb);
 		if (error)
-			return -error;
+			return error;
 	} else if (error != -ENOENT) {
 	        CDEBUG(D_INODE, "error for %s(%*s)%d\n",
 		       coda_f2s(&dircnp->c_fid), length, name, error);
@@ -215,7 +220,7 @@
 
 
 
-/* creation routines: create, mkdir, link, symlink */
+/* creation routines: create, mknod, mkdir, link, symlink */
 
 static int coda_create(struct inode *dir, struct dentry *de, int mode)
 {
@@ -227,6 +232,7 @@
 	struct ViceFid newfid;
 	struct coda_vattr attrs;
 
+	ENTRY;
 	coda_vfs_stat.create++;
 
 	CDEBUG(D_INODE, "name: %s, length %d, mode %o\n",name, length, mode);
@@ -249,7 +255,7 @@
         }
 
 	error = venus_create(dir->i_sb, &(dircnp->c_fid), name, length, 
-				0, mode, &newfid, &attrs);
+				0, mode, 0, &newfid, &attrs);
 
         if ( error ) {
 		CDEBUG(D_INODE, "create: %s, result %d\n",
@@ -271,6 +277,63 @@
         return 0;
 }			     
 
+static int coda_mknod(struct inode *dir, struct dentry *de, int mode, int rdev)
+{
+        int error=0;
+        struct coda_inode_info *dircnp;
+	const char *name=de->d_name.name;
+	int length=de->d_name.len;
+	struct inode *result = NULL;
+	struct ViceFid newfid;
+	struct coda_vattr attrs;
+
+	if ( coda_hasmknod == 0 )
+		return -EIO;
+
+	coda_vfs_stat.create++;
+
+	CDEBUG(D_INODE, "name: %s, length %d, mode %o, rdev %x\n",name, length, mode, rdev);
+
+        if (!dir || !S_ISDIR(dir->i_mode)) {
+                printk("coda_mknod: inode is null or not a directory\n");
+                return -ENOENT;
+        }
+
+	if (coda_isroot(dir) && coda_iscontrol(name, length))
+		return -EPERM;
+
+	dircnp = ITOC(dir);
+        CHECK_CNODE(dircnp);
+
+        if ( length > CFS_MAXNAMLEN ) {
+		printk("name too long: mknod, %s(%s)\n", 
+		       coda_f2s(&dircnp->c_fid), name);
+		return -ENAMETOOLONG;
+        }
+
+	error = venus_create(dir->i_sb, &(dircnp->c_fid), name, length, 
+				0, mode, rdev, &newfid, &attrs);
+
+        if ( error ) {
+		CDEBUG(D_INODE, "mknod: %s, result %d\n",
+		       coda_f2s(&newfid), error); 
+		d_drop(de);
+		return error;
+	}
+
+	error = coda_cnode_make(&result, &newfid, dir->i_sb);
+	if ( error ) {
+		d_drop(de);
+		result = NULL;
+		return error;
+	}
+
+	/* invalidate the directory cnode's attributes */
+	dircnp->c_flags |= C_VATTR;
+	d_instantiate(de, result);
+        return 0;
+}			     
+
 static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
 {
         struct coda_inode_info *dircnp;
@@ -281,7 +344,7 @@
 	int error;
 	struct ViceFid newfid;
 
-
+	ENTRY;
 	coda_vfs_stat.mkdir++;
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
@@ -345,7 +408,6 @@
 		return -EPERM;
 
         dir_cnp = ITOC(dir_inode);
-        CHECK_CNODE(dir_cnp);
         cnp = ITOC(inode);
         CHECK_CNODE(cnp);
 
@@ -362,8 +424,9 @@
 
 	if (  ! error ) { 
 		dir_cnp->c_flags |= C_VATTR;
-		inode->i_nlink++;
+		++inode->i_count;
 		d_instantiate(de, inode);
+		inode->i_nlink++;
 	} else {
 		d_drop(de);
 	}
@@ -417,7 +480,6 @@
 }
 
 /* destruction routines: unlink, rmdir */
-
 int coda_unlink(struct inode *dir, struct dentry *de)
 {
         struct coda_inode_info *dircnp;
@@ -445,8 +507,8 @@
 
         /* cache management */
 	dircnp->c_flags |= C_VATTR;
-	
 	de->d_inode->i_nlink--;
+
 	d_delete(de);
 
         return 0;
@@ -459,6 +521,7 @@
 	int len = de->d_name.len;
         int error, rehash = 0;
 
+	ENTRY;
 	coda_vfs_stat.rmdir++;
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
@@ -516,54 +579,86 @@
 	struct inode *new_inode = new_dentry->d_inode;
         struct coda_inode_info *new_cnp, *old_cnp;
         int error, rehash = 0, update = 1;
-ENTRY;
+
+	ENTRY;
 	coda_vfs_stat.rename++;
 
+        if ( (old_length > CFS_MAXNAMLEN) || new_length > CFS_MAXNAMLEN ) {
+                return -ENAMETOOLONG;
+        }
+
         old_cnp = ITOC(old_dir);
-        CHECK_CNODE(old_cnp);
         new_cnp = ITOC(new_dir);
-        CHECK_CNODE(new_cnp);
 
-        CDEBUG(D_INODE, "old: %s, (%d length, %d strlen), new: %s (%d length, %d strlen).\n", old_name, old_length, strlen(old_name), new_name, new_length, strlen(new_name));
+        CDEBUG(D_INODE, "old: %s, (%d length, %d strlen), new: %s"
+	       "(%d length, %d strlen).old:d_count: %d, new:d_count: %d\n", 
+	       old_name, old_length, strlen(old_name), new_name, new_length, 
+	       strlen(new_name),old_dentry->d_count, new_dentry->d_count);
 
-        if ( (old_length > CFS_MAXNAMLEN) || new_length > CFS_MAXNAMLEN ) {
-                return -ENAMETOOLONG;
+	if (new_inode == old_inode)
+		return 0;
+
+	if (new_dir == old_dir)
+		goto do_rename;
+	/* make sure target is not in use */
+	if (new_inode && S_ISDIR(new_inode->i_mode)) { 
+		/*
+                 * Prune any children before testing for busy.
+                 */
+                if (new_dentry->d_count > 1)
+                        shrink_dcache_parent(new_dentry);
+
+                if (new_dentry->d_count > 1)
+                        return -EBUSY;
         }
 
-        /* the old file should go from the namecache */
-/*         cfsnc_zapfile(old_cnp, (const char *)old_name, old_length); */
-/*         cfsnc_zapfile(new_cnp, (const char *)new_name, new_length); */
-
-        /* cross directory moves */
-	if (new_dir != old_dir  &&
-	    S_ISDIR(old_inode->i_mode) && 
-	    old_dentry->d_count > 1)
-	        shrink_dcache_parent(old_dentry);
-		
-	/* We must prevent any new references to the
-	 * target while the rename is in progress, so
-	 * we unhash the dentry.  */
+	if ( coda_crossvol_rename == 0 && 
+	     old_cnp->c_fid.Volume != new_cnp->c_fid.Volume )
+		return -EXDEV;
+
+	/* if the volumeid are the same we can reuse the inode,
+	   otherwise we need a new inode, since the new file 
+	   will have a new inode number. */
+
+	/* if moving a directory, clean the dcache */
+	if (S_ISDIR(old_inode->i_mode) && old_dentry->d_count > 1) 
+		shrink_dcache_parent(old_dentry);
+
+#if 0
+	if (old_dentry->d_count > 1) {
+		return -EBUSY;
+	}
+#endif
+
+	if (new_dentry->d_count > 1) {
+		return -EBUSY;
+	}
+	d_drop(old_dentry);
+	update = 0;
+
+ do_rename:
 	if (!list_empty(&new_dentry->d_hash)) {
-	        d_drop(new_dentry);
-		rehash = 1;
+		d_drop(new_dentry);
+		rehash = update;
 	}
+	if ( new_inode ) 
+		d_delete(new_dentry);
 
         error = venus_rename(old_dir->i_sb, &(old_cnp->c_fid), 
 			     &(new_cnp->c_fid), old_length, new_length, 
 			     (const char *) old_name, (const char *)new_name);
 
-	if (rehash) {
-		d_add(new_dentry, new_inode);
-	}
-
         if ( error ) {
                 CDEBUG(D_INODE, "returned error %d\n", error);
                 return error;
         }
 	/* Update the dcache if needed */
+	if (rehash) {
+		d_add(new_dentry, NULL);
+	}
 	if (update)
 	        d_move(old_dentry, new_dentry);
-         
+	
         CDEBUG(D_INODE, "result %d\n", error); 
 
 	EXIT;
@@ -594,7 +689,7 @@
         
         if ( !cnp->c_ovp ) {
                 CDEBUG(D_FILE, "open inode pointer = NULL.\n");
-                return -ENODEV;
+                return -EIO;
         }
 
 	coda_prepare_openfile(inode, file, cnp->c_ovp, &open_file,
@@ -620,17 +715,16 @@
         struct coda_inode_info *cnp;
         int error = 0;
         struct inode *cont_inode = NULL;
-        unsigned short flags = f->f_flags;
+        unsigned short flags = f->f_flags & (~O_EXCL);
 	unsigned short coda_flags = coda_flags_to_cflags(flags);
 
         ENTRY;
 	coda_vfs_stat.open++;
         
-        CDEBUG(D_SPECIAL, "OPEN inode number: %ld, flags %o.\n", 
-	       f->f_dentry->d_inode->i_ino, flags);
+        CDEBUG(D_SPECIAL, "OPEN inode number: %ld, count %d, flags %o.\n", 
+	       f->f_dentry->d_inode->i_ino, f->f_dentry->d_count, flags);
 
         cnp = ITOC(i);
-        CHECK_CNODE(cnp);
 
 	error = venus_open(i->i_sb, &(cnp->c_fid), coda_flags, &ino, &dev); 
 	if (error) {
@@ -641,26 +735,22 @@
 
         /* coda_upcall returns ino number of cached object, get inode */
         CDEBUG(D_FILE, "cache file dev %d, ino %ld\n", dev, ino);
+	error = coda_inode_grab(dev, ino, &cont_inode);
+	
+	if ( error || !cont_inode ){
+		printk("coda_open: coda_inode_grab error %d.", error);
+		if (cont_inode) 
+			iput(cont_inode);
+		return error;
+	}
 
-        if ( ! cnp->c_ovp ) {
-                error = coda_inode_grab(dev, ino, &cont_inode);
-                
-                if ( error ){
-                        printk("coda_open: coda_inode_grab error %d.", error);
-                        if (cont_inode) iput(cont_inode);
-			return error;
-                }
-                CDEBUG(D_FILE, "GRAB: coda_inode_grab: ino %ld, ops at %x\n", 
-		       cont_inode->i_ino, (int)cont_inode->i_op);
-                cnp->c_ovp = cont_inode; 
-        } 
+        if (  cnp->c_ovp ) {
+		iput(cnp->c_ovp);
+		cnp->c_ovp = NULL;
+	}
+	cnp->c_ovp = cont_inode; 
         cnp->c_ocount++;
 
-        /* if opened for writing flush cache entry.  */
-/*         if ( flags & (O_WRONLY | O_RDWR) ) { */
-/* 	        cfsnc_zapfid(&(cnp->c_fid)); */
-/* 	}  */
-
         CDEBUG(D_FILE, "result %d, coda i->i_count is %d for ino %ld\n", 
 	       error, i->i_count, i->i_ino);
         CDEBUG(D_FILE, "cache ino: %ld, count %d, ops %x\n", 
@@ -674,7 +764,7 @@
 {
         struct coda_inode_info *cnp;
         int error;
-        unsigned short flags = f->f_flags;
+        unsigned short flags = (f->f_flags) & (~O_EXCL);
 	unsigned short cflags = coda_flags_to_cflags(flags);
 
         ENTRY;
@@ -805,7 +895,8 @@
         return error;
 }
 
-int coda_dentry_revalidate(struct dentry *de)
+/* called when a cache lookup succeeds */
+static int coda_dentry_revalidate(struct dentry *de)
 {
 	int valid = 1;
 	struct inode *inode = de->d_inode;
@@ -816,12 +907,32 @@
 		if (is_bad_inode(inode))
 			return 0;
 		cii = ITOC(de->d_inode);
-		if (cii->c_flags & (C_PURGE | C_VATTR)) 
+		if (cii->c_flags & C_PURGE) 
 			valid = 0;
 	}
 	return valid ||  coda_isroot(de->d_inode);
 }
 
+/*
+ * This is the callback from dput() when d_count is going to 0.
+ * We use this to unhash dentries with bad inodes.
+ */
+static void coda_dentry_delete(struct dentry * dentry)
+{
+	int flags;
+
+	if (!dentry->d_inode) 
+		return ;
+
+	flags =  (ITOC(dentry->d_inode)->c_flags) & C_PURGE;
+	if (is_bad_inode(dentry->d_inode) || flags) {
+		CDEBUG(D_DOWNCALL, "bad inode, unhashing %s/%s, %ld\n", 
+		       dentry->d_parent->d_name.name, dentry->d_name.name,
+		       dentry->d_inode->i_ino);
+		d_drop(dentry);
+	}
+}
+
 
 static int coda_refresh_inode(struct dentry *dentry)
 {
@@ -831,15 +942,16 @@
 	ino_t old_ino;
 	struct inode *inode = dentry->d_inode;
 	struct coda_inode_info *cii = ITOC(inode);
-	
+
 	ENTRY;
+	
 	error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr);
 	if ( error ) { 
 		make_bad_inode(inode);
 		return -EIO;
 	}
 
-	/* this baby may be lost if:
+	/* this inode may be lost if:
             - it's type changed
             - it's ino changed 
 	*/
@@ -854,7 +966,7 @@
 		return -EIO;
 	}
 	
-	cii->c_flags &= ~(C_VATTR | C_PURGE);
+	cii->c_flags &= ~C_VATTR;
 	return 0;
 }
 
@@ -876,7 +988,7 @@
 	       dentry->d_name.len, dentry->d_name.name,
 	       dentry->d_parent->d_name.len, dentry->d_parent->d_name.name);
 
-	if ( cii->c_flags & (C_VATTR | C_PURGE )) {
+	if (cii->c_flags & (C_VATTR | C_PURGE)) {
 		error = coda_refresh_inode(dentry);
 	}
 
diff -u --recursive --new-file v2.1.108/linux/fs/coda/inode.c linux/fs/coda/inode.c
--- v2.1.108/linux/fs/coda/inode.c	Sun Jun  7 11:16:36 1998
+++ linux/fs/coda/inode.c	Fri Jul 10 15:33:37 1998
@@ -48,7 +48,7 @@
 		       int bufsiz);
 
 /* helper functions */
-static inline struct vcomm *coda_psinode2vcomm(struct inode *inode);
+static struct vcomm *coda_psinode2vcomm(struct inode *inode);
 static int coda_get_psdev(void *, struct inode **);
 static struct coda_sb_info *coda_psinode2sbi(struct inode *inode);
 
@@ -211,7 +211,7 @@
 	}
 
 
-	if ( coda_fid_is_volroot(&cii->c_fid) )
+	if ( ! list_empty(&cii->c_volrootlist) )
 		list_del(&cii->c_volrootlist);
 
         open_inode = cii->c_ovp;
@@ -223,7 +223,7 @@
         }
 	
 	coda_cache_clear_inode(inode);
-
+	CDEBUG(D_DOWNCALL, "clearing inode: %ld, %x\n", inode->i_ino, cii->c_flags);
 	inode->u.generic_ip = NULL;
         clear_inode(inode);
 	EXIT;
@@ -291,7 +291,7 @@
 /* MODULE stuff is in psdev.c */
 
 /*  helpers */
-static inline struct vcomm *coda_psinode2vcomm(struct inode *inode) 
+static struct vcomm *coda_psinode2vcomm(struct inode *inode) 
 {
         
 	unsigned int minor = MINOR(inode->i_rdev);
diff -u --recursive --new-file v2.1.108/linux/fs/coda/pioctl.c linux/fs/coda/pioctl.c
--- v2.1.108/linux/fs/coda/pioctl.c	Tue Mar 10 10:03:33 1998
+++ linux/fs/coda/pioctl.c	Fri Jul 10 15:33:37 1998
@@ -123,7 +123,7 @@
 	        target_de = lnamei(data.path);
 	}
 		
-	if ( PTR_ERR(target_de) == -ENOENT ) {
+	if ( IS_ERR(target_de) ) {
                 CDEBUG(D_PIOCTL, "error: lookup fails.\n");
 		return PTR_ERR(target_de);
         } else {
@@ -142,12 +142,12 @@
 
 	/* now proceed to make the upcall */
         cnp = ITOC(target_inode);
-        CHECK_CNODE(cnp);
 
 	error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data);
 
         CDEBUG(D_PIOCTL, "ioctl on inode %ld\n", target_inode->i_ino);
-
+	CDEBUG(D_DOWNCALL, "dput on ino: %ld, icount %d, dcount %d\n", target_inode->i_ino, 
+	       target_inode->i_count, target_de->d_count);
         if ( target_de ) 
 	        dput(target_de);
         return error;
diff -u --recursive --new-file v2.1.108/linux/fs/coda/psdev.c linux/fs/coda/psdev.c
--- v2.1.108/linux/fs/coda/psdev.c	Wed Jun 24 22:54:08 1998
+++ linux/fs/coda/psdev.c	Fri Jul 10 15:33:37 1998
@@ -64,7 +64,7 @@
 
 /* statistics */
 struct coda_upcallstats coda_callstats;
-int           coda_hard = 0;  /* introduces a timeout on upcalls */
+int           coda_hard = 0;  /* allows signals during upcalls */
 unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */
 extern struct coda_sb_info coda_super_info[MAX_CODADEVS];
 struct vcomm psdev_vcomm[MAX_CODADEVS];
@@ -401,7 +401,15 @@
 
 #ifdef CONFIG_PROC_FS
 
-extern struct proc_dir_entry proc_sys_root;
+struct proc_dir_entry proc_sys_root = {
+        PROC_SYS, 3, "sys",                     /* inode, name */
+        S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,   /* mode, nlink, uid, gid */
+        0, &proc_dir_inode_operations,          /* size, ops */
+        NULL, NULL,                             /* get_info, fill_inode */
+        NULL,                                   /* next */
+        NULL, NULL                              /* parent, subdir */
+};
+
 
 struct proc_dir_entry proc_sys_coda = {
         0, 4, "coda",
@@ -412,18 +420,9 @@
 	NULL, NULL
 };
 
-struct proc_dir_entry proc_fs = {
-        PROC_FS, 2, "fs",
-        S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
-        0, &proc_dir_inode_operations,
-	NULL, NULL,
-	NULL,
-	NULL, NULL
-};
-
 /*
- * target directory structure:
-   /proc/fs/
+ target directory structure:
+   /proc/fs  (see linux/fs/proc/root.c)
    /proc/fs/coda
    /proc/fs/coda/{vfs_stats,
 
@@ -497,6 +496,23 @@
 
 #endif
 
+__initfunc(int init_coda(void)) 
+{
+	int status;
+	printk(KERN_INFO "Coda Kernel/Venus communications, v4.6.0, braam@cs.cmu.edu\n");
+	
+	status = init_coda_psdev();
+	if ( status ) {
+		printk("Problem (%d) in init_coda_psdev\n", status);
+		return status;
+	}
+	
+	status = init_coda_fs();
+	if (status) {
+		printk("coda: failed in init_coda_fs!\n");
+	}
+	return status;
+}
 
 int init_coda_psdev(void)
 {
@@ -515,13 +531,14 @@
 	reset_coda_cache_inv_stats();
 
 #ifdef CONFIG_PROC_FS
-	proc_register(&proc_root,&proc_fs);
-	proc_register(&proc_fs,&proc_fs_coda);
+	proc_register(&proc_root_fs,&proc_fs_coda);
 	proc_register(&proc_fs_coda,&proc_coda_vfs);
 	proc_register(&proc_fs_coda,&proc_coda_upcall);
 	proc_register(&proc_fs_coda,&proc_coda_permission);
 	proc_register(&proc_fs_coda,&proc_coda_cache_inv);
+#endif
 
+#ifdef CONFIG_SYSCTL
 	proc_register(&proc_sys_root,&proc_sys_coda);
 	proc_register(&proc_sys_coda,&proc_coda_vfs_control);
 	proc_register(&proc_sys_coda,&proc_coda_upcall_control);
@@ -542,20 +559,20 @@
 
 int init_module(void)
 {
-  int status;
-  printk(KERN_INFO "Coda Kernel/User communications module 2.0\n");
+	int status;
+	printk(KERN_INFO "Coda Kernel/Venus communications (module), v4.6.0, braam@cs.cmu.edu\n");
+
+	status = init_coda_psdev();
+	if ( status ) {
+		printk("Problem (%d) in init_coda_psdev\n", status);
+		return status;
+	}
 
-  status = init_coda_psdev();
-  if ( status ) {
-	  printk("Problem (%d) in init_coda_psdev\n", status);
-	  return status;
-  }
-
-  status = init_coda_fs();
-  if (status) {
-	  printk("coda: failed in init_coda_fs!\n");
-  }
-  return status;
+	status = init_coda_fs();
+	if (status) {
+		printk("coda: failed in init_coda_fs!\n");
+	}
+	return status;
 }
 
 
@@ -578,13 +595,14 @@
         proc_unregister(&proc_sys_coda, proc_coda_upcall_control.low_ino);
 	proc_unregister(&proc_sys_coda, proc_coda_vfs_control.low_ino);
 	proc_unregister(&proc_sys_root, proc_sys_coda.low_ino);
+#endif
 
+#ifdef CONFIG_SYSCTL
         proc_unregister(&proc_fs_coda, proc_coda_cache_inv.low_ino);
         proc_unregister(&proc_fs_coda, proc_coda_permission.low_ino);
         proc_unregister(&proc_fs_coda, proc_coda_upcall.low_ino);
         proc_unregister(&proc_fs_coda, proc_coda_vfs.low_ino);
-	proc_unregister(&proc_fs, proc_fs_coda.low_ino);
-	proc_unregister(&proc_root, proc_fs.low_ino);
+	proc_unregister(&proc_root_fs, proc_fs_coda.low_ino);
 #endif 
 }
 
diff -u --recursive --new-file v2.1.108/linux/fs/coda/upcall.c linux/fs/coda/upcall.c
--- v2.1.108/linux/fs/coda/upcall.c	Sun Jun  7 11:16:36 1998
+++ linux/fs/coda/upcall.c	Fri Jul 10 15:33:37 1998
@@ -46,7 +46,8 @@
 #define UPARG(op)\
 do {\
   	CODA_ALLOC(inp, union inputArgs *, insize);\
-	outp = (union outputArgs *) (inp);\
+        if ( !inp ) { return -ENOMEM; }\
+        outp = (union outputArgs *) (inp);\
         inp->ih.opcode = (op);\
 	inp->ih.pid = current->pid;\
 	inp->ih.pgid = current->pgrp;\
@@ -90,7 +91,7 @@
 
 	if (inp)  CODA_FREE(inp, insize);
         EXIT;
-	return -error;
+	return error;
 }
 
 int venus_getattr(struct super_block *sb, struct ViceFid *fid, 
@@ -98,19 +99,22 @@
 {
         union inputArgs *inp;
         union outputArgs *outp;
-	        int insize, outsize, error;
-ENTRY;
+        int insize, outsize, error;
+	ENTRY;
+
         insize = SIZE(getattr); 
 	UPARG(CFS_GETATTR);
         inp->cfs_getattr.VFid = *fid;
+
         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
 	
 	if ( !error )
 	        *attr = outp->cfs_getattr.attr;
 
-        if (inp) CODA_FREE(inp, insize);
+        if (inp) 
+		CODA_FREE(inp, insize);
         EXIT;
-        return -error;
+        return error;
 }
 
 int  venus_setattr(struct super_block *sb, struct ViceFid *fid, 
@@ -130,7 +134,7 @@
 
         CDEBUG(D_SUPER, " result %d\n", error); 
         if ( inp ) CODA_FREE(inp, insize);
-        return -error;
+        return error;
 }
 
 int venus_lookup(struct super_block *sb, struct ViceFid *fid, 
@@ -160,7 +164,7 @@
 	}
 	if (inp) CODA_FREE(inp, insize);
 		
-	return -error;
+	return error;
 }
 
 
@@ -180,7 +184,7 @@
 
         if (inp) 
 		CODA_FREE(inp, insize);
-        return -error;
+        return error;
 }
 
 int venus_open(struct super_block *sb, struct ViceFid *fid,
@@ -209,7 +213,7 @@
         if (inp) 
                 CODA_FREE(inp, insize);
 
-	return -error;
+	return error;
 }	
 
 int venus_mkdir(struct super_block *sb, struct ViceFid *dirfid, 
@@ -239,7 +243,7 @@
 
 	if (inp) 
 	        CODA_FREE(inp, insize);
-	return -error;        
+	return error;        
 }
 
 
@@ -279,11 +283,11 @@
         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
 
 	if (inp) CODA_FREE(inp, insize);
-	return -error;
+	return error;
 }
 
 int venus_create(struct super_block *sb, struct ViceFid *dirfid, 
-		    const char *name, int length, int excl, int mode, 
+		    const char *name, int length, int excl, int mode, int rdev,
 		    struct ViceFid *newfid, struct coda_vattr *attrs) 
 {
         union inputArgs *inp;
@@ -297,6 +301,7 @@
 
         inp->cfs_create.VFid = *dirfid;
         inp->cfs_create.attr.va_mode = mode;
+        inp->cfs_create.attr.va_rdev = rdev;
 	inp->cfs_create.excl = excl;
         inp->cfs_create.mode = mode;
         inp->cfs_create.name = offset;
@@ -312,7 +317,7 @@
 
 	if (inp) 
 	        CODA_FREE(inp, insize);
-	return -error;        
+	return error;        
 }
 
 int venus_rmdir(struct super_block *sb, struct ViceFid *dirfid, 
@@ -335,7 +340,7 @@
         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
 	if ( inp ) 
 	        CODA_FREE(inp, insize);
-	return -error;
+	return error;
 }
 
 int venus_remove(struct super_block *sb, struct ViceFid *dirfid, 
@@ -357,7 +362,7 @@
         error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
 	if ( inp ) 
 	        CODA_FREE(inp, insize);
-	return -error;
+	return error;
 }
 
 int venus_readlink(struct super_block *sb, struct ViceFid *fid, 
@@ -389,7 +394,7 @@
         if (inp) CODA_FREE(inp, insize);
         CDEBUG(D_INODE, " result %d\n",error);
         EXIT;
-        return -error;
+        return error;
 }
 
 
@@ -420,7 +425,7 @@
 	        CODA_FREE(inp, insize);
         CDEBUG(D_INODE, " result %d\n",error);
         EXIT;
-        return -error;
+        return error;
 }
 
 int venus_symlink(struct super_block *sb, struct ViceFid *fid,
@@ -458,7 +463,7 @@
 	        CODA_FREE(inp, insize);
         CDEBUG(D_INODE, " result %d\n",error);
         EXIT;
-        return -error;
+        return error;
 }
 
 int venus_fsync(struct super_block *sb, struct ViceFid *fid)
@@ -476,7 +481,7 @@
 
 	if ( inp ) 
 		CODA_FREE(inp, insize);
-	return -error;
+	return error;
 }
 
 int venus_access(struct super_block *sb, struct ViceFid *fid, int mask)
@@ -495,7 +500,7 @@
 
 	if (inp) CODA_FREE(inp, insize);
         EXIT;
-	return -error;
+	return error;
 }
 
 
@@ -575,7 +580,7 @@
  exit:
         if (inp) 
 	        CODA_FREE(inp, insize);
-	return -error;
+	return error;
 }
 
 /*
@@ -583,12 +588,6 @@
  * 
  */
 
-/* 
- * coda_upcall will return a POSITIVE error in the case of 
- * failed communication with Venus _or_ will peek at Venus
- * reply and return Venus' error, also POSITIVE. 
- * 
- */
 static inline unsigned long coda_waitfor_upcall(struct vmsg *vmp)
 {
 	struct wait_queue	wait = { current, NULL };
@@ -608,18 +607,18 @@
 		if ( vmp->vm_flags & VM_WRITE )
 			break;
 
-		if ( ! signal_pending(current) )
-			schedule();
-		/* signal is present: after timeout always return */
-		if ( jiffies > vmp->vm_posttime + coda_timeout * HZ )
-			break; 
-				
-		/* if this process really wants to die, let it go */
-		if ( sigismember(&current->signal, SIGKILL) ||
-		     sigismember(&current->signal, SIGINT) )
-			break;
-		else 
-			schedule();
+		if ( !coda_hard && signal_pending(current) ) {
+			/* if this process really wants to die, let it go */
+			if ( sigismember(&(current->signal), SIGKILL) ||
+			     sigismember(&(current->signal), SIGINT) )
+				break;
+			/* signal is present: after timeout always return 
+			   really smart idea, probably useless ... */
+			if ( jiffies > vmp->vm_posttime + coda_timeout * HZ )
+				break; 
+		}
+		schedule();
+
 	}
 	remove_wait_queue(&vmp->vm_sleep, &wait);
 	current->state = TASK_RUNNING;
@@ -630,6 +629,16 @@
 }
 
 
+/* 
+ * coda_upcall will return an error in the case of 
+ * failed communication with Venus _or_ will peek at Venus
+ * reply and return Venus' error.
+ *
+ * As venus has 2 types of errors, normal errors (positive) and internal
+ * errors (negative), normal errors are negated, while internal errors
+ * are all mapped to -EINTR, while showing a nice warning message. (jh)
+ * 
+ */
 static int coda_upcall(struct coda_sb_info *sbi, 
 		int inSize, int *outSize, 
 		union inputArgs *buffer) 
@@ -643,13 +652,13 @@
 ENTRY;
 
 	if (sbi->sbi_vcomm == NULL) {
-                return ENODEV;
+                return -ENODEV;
 	}
 	vcommp = sbi->sbi_vcomm;
 
 
 	if (!vcomm_open(vcommp))
-                return(ENODEV);
+                return -ENXIO;
 
 	/* Format the request message. */
 	CODA_ALLOC(vmp,struct vmsg *,sizeof(struct vmsg));
@@ -693,9 +702,14 @@
 	if (vcomm_open(vcommp)) {      /* i.e. Venus is still alive */
 	    /* Op went through, interrupt or not... */
 	    if (vmp->vm_flags & VM_WRITE) {
-		error = 0;
 		out = (union outputArgs *)vmp->vm_data;
-		error = out->oh.result;
+		/* here we map positive Venus errors to kernel errors */
+		if ( out->oh.result < 0 ) {
+			printk("Tell Peter: Venus returns negative error %ld, for oc %ld!\n",
+			       out->oh.result, out->oh.opcode);
+			out->oh.result = EINTR;
+		}
+		error = -out->oh.result;
 		CDEBUG(D_UPCALL, 
 		       "upcall: (u,o,r) (%ld, %ld, %ld) out at %p\n", 
 		       out->oh.unique, out->oh.opcode, out->oh.result, out);
@@ -708,48 +722,49 @@
 		       "Interrupted before read:(op,un) (%d.%d), flags = %x\n",
 		       vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
 		coda_q_remove(&(vmp->vm_chain));
-		error = ERESTARTNOHAND;
+		/* perhaps the best way to convince the app to
+		   give up? */
+		error = -EINTR;
 		goto exit;
 	    } 
 	    if ( (vmp->vm_flags & VM_READ) && signal_pending(current) ) {
-		/* interrupted after Venus did its read, send signal */
-		union inputArgs *dog;
-		struct vmsg *svmp;
-		
-		CDEBUG(D_UPCALL, 
-		       "Sending Venus a signal: op = %d.%d, flags = %x\n",
-		       vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
-		
-		coda_q_remove(&(vmp->vm_chain));
-		error = ERESTARTNOHAND;
-		
-		CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
-		CODA_ALLOC((svmp->vm_data), char *, sizeof(struct cfs_in_hdr));
-
-		dog = (union inputArgs *)svmp->vm_data;
-		dog->ih.opcode = CFS_SIGNAL;
-		dog->ih.unique = vmp->vm_unique;
-		
-		svmp->vm_flags = 0;
-		svmp->vm_opcode = dog->ih.opcode;
-		svmp->vm_unique = dog->ih.unique;
-		svmp->vm_inSize = sizeof(struct cfs_in_hdr);
-		svmp->vm_outSize = sizeof(struct cfs_in_hdr);
-		CDEBUG(D_UPCALL, 
-		       "coda_upcall: enqueing signal msg (%d, %d)\n",
-		       svmp->vm_opcode, svmp->vm_unique);
-		
-		/* insert at head of queue! */
-		coda_q_insert(&(svmp->vm_chain), vcommp->vc_pending.forw);
-		wake_up_interruptible(&vcommp->vc_waitq);
+		    /* interrupted after Venus did its read, send signal */
+		    union inputArgs *dog;
+		    struct vmsg *svmp;
+		    
+		    CDEBUG(D_UPCALL, 
+			   "Sending Venus a signal: op = %d.%d, flags = %x\n",
+			   vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
+		    
+		    coda_q_remove(&(vmp->vm_chain));
+		    error = -EINTR;
+		    CODA_ALLOC(svmp, struct vmsg *, sizeof (struct vmsg));
+		    CODA_ALLOC((svmp->vm_data), char *, sizeof(struct cfs_in_hdr));
+		    
+		    dog = (union inputArgs *)svmp->vm_data;
+		    dog->ih.opcode = CFS_SIGNAL;
+		    dog->ih.unique = vmp->vm_unique;
+		    
+		    svmp->vm_flags = 0;
+		    svmp->vm_opcode = dog->ih.opcode;
+		    svmp->vm_unique = dog->ih.unique;
+		    svmp->vm_inSize = sizeof(struct cfs_in_hdr);
+		    svmp->vm_outSize = sizeof(struct cfs_in_hdr);
+		    CDEBUG(D_UPCALL, 
+			   "coda_upcall: enqueing signal msg (%d, %d)\n",
+			   svmp->vm_opcode, svmp->vm_unique);
+		    
+		    /* insert at head of queue! */
+		    coda_q_insert(&(svmp->vm_chain), vcommp->vc_pending.forw);
+		    wake_up_interruptible(&vcommp->vc_waitq);
 	    } else {
 		    printk("Coda: Strange interruption..\n");
-		    error = EINTR;
+		    error = -EINTR;
 	    }
 	} else {	/* If venus died i.e. !VC_OPEN(vcommp) */
 	        printk("coda_upcall: Venus dead on (op,un) (%d.%d) flags %d\n",
 		       vmp->vm_opcode, vmp->vm_unique, vmp->vm_flags);
-		error = ENODEV;
+		error = -ENODEV;
 	}
 
  exit:
@@ -759,7 +774,13 @@
 	return error;
 }
 
-
+/*  
+    The statements below are part of the Coda opportunistic
+    programming -- taken from the Mach/BSD kernel code for Coda. 
+    You don't get correct semantics by stating what needs to be
+    done without guaranteeing the invariants needed for it to happen.
+    When will be have time to find out what exactly is going on?  (pjb)
+*/
 
 
 /* 
@@ -790,7 +811,7 @@
 int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb)
 {
 
-    /* Handle invalidation requests. */
+	/* Handle invalidation requests. */
           if ( !sb ) { 
 	          printk("coda_downcall: opcode %d, no sb!\n", opcode);
 		  return 0; 
@@ -821,15 +842,19 @@
 	  case CFS_ZAPDIR : {
 	          struct inode *inode;
 		  ViceFid *fid = &out->cfs_zapdir.CodaFid;
-		  CDEBUG(D_DOWNCALL, "zapdir: fid = %s\n", coda_f2s(fid));
+		  CDEBUG(D_DOWNCALL, "zapdir: fid = %s...\n", coda_f2s(fid));
 		  clstats(CFS_ZAPDIR);
 
 		  inode = coda_fid_to_inode(fid, sb);
-		  if ( inode ) {
+		  if (inode) {
+			  CDEBUG(D_DOWNCALL, "zapdir: inode = %ld children flagged\n", 
+				 inode->i_ino);
+			  coda_purge_children(inode);
+			  CDEBUG(D_DOWNCALL, "zapdir: inode = %ld cache cleared\n", inode->i_ino);
 	                  coda_flag_inode(inode, C_VATTR);
-			  coda_cache_clear_inode(inode);
-			  coda_flag_alias_children(inode, C_PURGE);
-		  }
+		  } else 
+			  CDEBUG(D_DOWNCALL, "zapdir: no inode\n");
+		  
 		  return(0);
 	  }
 
@@ -840,9 +865,10 @@
 		  CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", coda_f2s(fid));
 		  inode = coda_fid_to_inode(fid, sb);
 		  if ( inode ) {
+			  CDEBUG(D_DOWNCALL, "zapfile: inode = %ld\n", inode->i_ino);
 	                  coda_flag_inode(inode, C_VATTR);
-			  coda_cache_clear_inode(inode);
-		  }
+		  } else 
+			  CDEBUG(D_DOWNCALL, "zapfile: no inode\n");
 		  return 0;
 	  }
 
@@ -853,19 +879,26 @@
 		  clstats(CFS_PURGEFID);
 		  inode = coda_fid_to_inode(fid, sb);
 		  if ( inode ) { 
-                          coda_flag_inode(inode, C_PURGE);
-			  coda_cache_clear_inode(inode);
-		  }
+			  CDEBUG(D_DOWNCALL, "purgefid: inode = %ld\n", inode->i_ino);
+			  coda_purge_children(inode);
+			  coda_purge_dentries(inode);
+		  }else 
+			  CDEBUG(D_DOWNCALL, "purgefid: no inode\n");
 		  return 0;
 	  }
 
 	  case CFS_REPLACE : {
-	          printk("CFS_REPLACCE\n");
+	          struct inode *inode;
+		  ViceFid *fid = &out->cfs_replace.OldFid;
 		  clstats(CFS_REPLACE);
 		  CDEBUG(D_DOWNCALL, "CFS_REPLACE\n");
-		  coda_cache_clear_all(sb);
-		  shrink_dcache_sb(sb);
-		  return (0);
+		  inode = coda_fid_to_inode(fid, sb);
+		  if ( inode ) { 
+			  CDEBUG(D_DOWNCALL, "replacefid: inode = %ld\n", inode->i_ino);
+                          coda_purge_dentries(inode);
+		  }else 
+			  CDEBUG(D_DOWNCALL, "purgefid: no inode\n");
+		  return 0;
 	  }			   
 	  }
 	  return 0;
diff -u --recursive --new-file v2.1.108/linux/fs/namei.c linux/fs/namei.c
--- v2.1.108/linux/fs/namei.c	Wed Jun 24 22:54:09 1998
+++ linux/fs/namei.c	Wed Jul 15 09:52:27 1998
@@ -86,50 +86,16 @@
  * semantics.  See the comments in "open_namei" and "do_link" below.
  */
 
-char * getname_quicklist = NULL;
-int getname_quickcount = 0;
-spinlock_t getname_quicklock = SPIN_LOCK_UNLOCKED;
-
-/* Tuning: increase locality by reusing same pages again...
- * if getname_quicklist becomes too long on low memory machines, either a limit
- * should be added or after a number of cycles some pages should
- * be released again ...
- */
 static inline char * get_page(void)
 {
 	char * res;
-	spin_lock(&getname_quicklock);
-	res = getname_quicklist;
-	if (res) {
-#ifdef DEBUG
-		char * tmp = res;
-		int i;
-		for(i=0; i<getname_quickcount; i++)
-			tmp = *(char**)tmp;
-		if (tmp)
-			printk("bad quicklist %x\n", (int)tmp);
-#endif
-		getname_quicklist = *(char**)res;
-		getname_quickcount--;
-	}
-	spin_unlock(&getname_quicklock);
-	if (!res)
-		res = (char*)__get_free_page(GFP_KERNEL);
+	res = (char*)__get_free_page(GFP_KERNEL);
 	return res;
 }
 
 inline void putname(char * name)
 {
-	if (name) {
-		spin_lock(&getname_quicklock);
-		*(char**)name = getname_quicklist;
-		getname_quicklist = name;
-		getname_quickcount++;
-		spin_unlock(&getname_quicklock);
-	}
-	/* if a getname_quicklist limit is necessary to introduce, call
-	 * free_page((unsigned long) name);
-	 */
+	free_page((unsigned long) name); 
 }
 
 /* In order to reduce some races, while at the same time doing additional
diff -u --recursive --new-file v2.1.108/linux/fs/ncpfs/Makefile linux/fs/ncpfs/Makefile
--- v2.1.108/linux/fs/ncpfs/Makefile	Tue Mar 17 22:18:15 1998
+++ linux/fs/ncpfs/Makefile	Wed Jul  8 10:33:42 1998
@@ -1,11 +1,11 @@
 #
-# Makefile for the linux ncp-filesystem routines.
+# Makefile for the linux ncp filesystem routines.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
 # removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
+# unless it's something special (not a .c file).
 #
-# Note 2! The CFLAGS definitions are now in the main makefile...
+# Note 2! The CFLAGS definitions are now in the main makefile.
 
 O_TARGET := ncpfs.o
 O_OBJS   := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o \
diff -u --recursive --new-file v2.1.108/linux/fs/proc/procfs_syms.c linux/fs/proc/procfs_syms.c
--- v2.1.108/linux/fs/proc/procfs_syms.c	Wed Jun 24 22:54:10 1998
+++ linux/fs/proc/procfs_syms.c	Fri Jul 10 15:33:38 1998
@@ -19,6 +19,7 @@
 EXPORT_SYMBOL(create_proc_entry);
 EXPORT_SYMBOL(remove_proc_entry);
 EXPORT_SYMBOL(proc_root);
+EXPORT_SYMBOL(proc_root_fs);
 EXPORT_SYMBOL(proc_get_inode);
 EXPORT_SYMBOL(in_group_p);
 EXPORT_SYMBOL(proc_dir_inode_operations);
diff -u --recursive --new-file v2.1.108/linux/fs/proc/root.c linux/fs/proc/root.c
--- v2.1.108/linux/fs/proc/root.c	Wed May 20 19:10:41 1998
+++ linux/fs/proc/root.c	Fri Jul 10 15:33:37 1998
@@ -566,6 +566,14 @@
 	S_IFREG | S_IRUGO, 1, 0, 0,
 	0, &proc_array_inode_operations
 };
+struct proc_dir_entry proc_root_fs = {
+        PROC_FS, 2, "fs",
+        S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+        0, &proc_dir_inode_operations,
+	NULL, NULL,
+	NULL,
+	NULL, NULL
+};
 static struct proc_dir_entry proc_root_dma = {
 	PROC_DMA, 3, "dma",
 	S_IFREG | S_IRUGO, 1, 0, 0,
@@ -660,6 +668,7 @@
 	proc_register(&proc_root, &proc_root_devices);
 	proc_register(&proc_root, &proc_root_interrupts);
 	proc_register(&proc_root, &proc_root_filesystems);
+	proc_register(&proc_root, &proc_root_fs);
 	proc_register(&proc_root, &proc_root_dma);
 	proc_register(&proc_root, &proc_root_ioports);
 	proc_register(&proc_root, &proc_root_cmdline);
diff -u --recursive --new-file v2.1.108/linux/fs/vfat/namei.c linux/fs/vfat/namei.c
--- v2.1.108/linux/fs/vfat/namei.c	Mon Feb 23 18:12:11 1998
+++ linux/fs/vfat/namei.c	Wed Jul 15 09:52:27 1998
@@ -1105,6 +1105,9 @@
 	fil.f_pos = 0;
 	vf.name = qname->name;
 	vf.len = qname->len;
+	while (vf.len && vf.name[vf.len-1] == '.') {
+		vf.len--;
+	}
 	vf.new_filename = new_filename;
 	vf.found = 0;
 	vf.posix = MSDOS_SB(sb)->options.posixfs;
@@ -1135,7 +1138,7 @@
 		goto cleanup;
 	}
 	
-	res = vfat_build_slots(dir, qname->name, qname->len, ds, 
+	res = vfat_build_slots(dir, qname->name, vf.len, ds, 
 			       &slots, &is_long);
 	if (res < 0) goto cleanup;
 
diff -u --recursive --new-file v2.1.108/linux/include/asm-alpha/linux_logo.h linux/include/asm-alpha/linux_logo.h
--- v2.1.108/linux/include/asm-alpha/linux_logo.h	Wed Jun 24 22:54:10 1998
+++ linux/include/asm-alpha/linux_logo.h	Fri Jul 10 15:18:32 1998
@@ -1,4 +1,4 @@
-/* $Id: linux_logo.h,v 1.2 1998/05/04 14:20:47 jj Exp $
+/* $Id: linux_logo.h,v 1.3 1998/06/29 19:36:17 geert Exp $
  * include/asm-alpha/linux_logo.h: This is a linux logo
  *                                 to be displayed on boot.
  *
@@ -28,27 +28,8 @@
 #ifdef INCLUDE_LINUX_LOGO_DATA
 
 #define INCLUDE_LINUX_LOGO16
-#include <linux/linux_logo.h>
-
-/* Painted by Johnny Stenback <jst@uwasa.fi> */
 
-unsigned char *linux_serial_image __initdata = "\n"
-"         .u$e.\n"
-"       .$$$$$:S\n"
-"       $\"*$/\"*$$\n"
-"       $.`$ . ^F\n"
-"       4k+#+T.$F\n"
-"       4P+++\"$\"$\n"
-"       :R\"+  t$$B\n"
-"    ___#       $$$\n"
-"    |  |       R$$k\n"
-"   dd. | Linux  $!$\n"
-"   ddd |   AXP  $9$F\n"
-" '!!!!!$       !!#!`\n"
-"  !!!!!*     .!!!!!`\n"
-"'!!!!!!!W..e$$!!!!!!`    %s\n"
-" \"~^^~         ^~~^\n"
-"\n";
+#include <linux/linux_logo.h>
 
 #else
 
@@ -62,8 +43,5 @@
 extern unsigned char linux_logo16_green[];
 extern unsigned char linux_logo16_blue[];
 extern unsigned char linux_logo16[];
-extern unsigned char *linux_serial_image;
-
-extern int (*console_show_logo)(void);
 
 #endif
diff -u --recursive --new-file v2.1.108/linux/include/asm-alpha/vga.h linux/include/asm-alpha/vga.h
--- v2.1.108/linux/include/asm-alpha/vga.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-alpha/vga.h	Fri Jul 10 15:18:32 1998
@@ -0,0 +1,56 @@
+/*
+ *	Access to VGA videoram
+ *
+ *	(c) 1998 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _LINUX_ASM_VGA_H_
+#define _LINUX_ASM_VGA_H_
+
+#include <asm/io.h>
+
+#define VT_BUF_HAVE_RW
+#define VT_BUF_HAVE_MEMSETW
+#define VT_BUF_HAVE_MEMCPYF
+
+extern inline void scr_writew(u16 val, u16 *addr)
+{
+	if ((long) addr < 0)
+		*addr = val;
+	else
+		writew(val, (unsigned long) addr);
+}
+
+extern inline u16 scr_readw(u16 *addr)
+{
+	if ((long) addr < 0)
+		return *addr;
+	else
+		return readw((unsigned long) addr);
+}
+
+extern inline void scr_memsetw(u16 *s, u16 c, unsigned int count)
+{
+	if ((long)s < 0)
+		memsetw(s, c, count);
+	else
+		memsetw_io(s, c, count);
+}
+
+extern inline void scr_memcpyw_from(u16 *d, u16 *s, unsigned int count)
+{
+	memcpy_fromio(d, s, count);
+}
+
+extern inline void scr_memcpyw_to(u16 *d, u16 *s, unsigned int count)
+{
+	memcpy_toio(d, s, count);
+}
+
+
+#define vga_readb readb
+#define vga_writeb writeb
+
+#define VGA_MAP_MEM(x) (x)
+
+#endif
diff -u --recursive --new-file v2.1.108/linux/include/asm-arm/linux_logo.h linux/include/asm-arm/linux_logo.h
--- v2.1.108/linux/include/asm-arm/linux_logo.h	Fri May  8 23:14:54 1998
+++ linux/include/asm-arm/linux_logo.h	Fri Jul 10 15:18:32 1998
@@ -25,25 +25,3 @@
 unsigned char linux_logo16[] __initdata = { };
 unsigned char linux_logo_bw[] __initdata = { };
 
-/* Painted by Johnny Stenback <jst@uwasa.fi>
- * Modified by Russell King for ARM
- */
-unsigned char *linux_serial_image __initdata = "\n"
-"         .u$e.\n"
-"       .$$$$$:S\n"
-"       $\"*$/\"*$$\n"
-"       $.`$ . ^F\n"
-"       4k+#+T.$F\n"
-"       4P+++\"$\"$\n"
-"       :R\"+  t$$B\n"
-"    ___#       $$$\n"
-"    |  |       R$$k\n"
-"   dd. | Linux  $!$\n"
-"   ddd |   ARM  $9$F\n"
-" '!!!!!$       !!#!`\n"
-"  !!!!!*     .!!!!!`\n"
-"'!!!!!!!W..e$$!!!!!!`    %s\n"
-" \"~^^~         ^~~^\n"
-"\n";
-
-extern int (*console_show_logo)(void);
diff -u --recursive --new-file v2.1.108/linux/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h
--- v2.1.108/linux/include/asm-i386/bugs.h	Wed Jun 24 22:54:10 1998
+++ linux/include/asm-i386/bugs.h	Thu Jul 16 15:11:06 1998
@@ -2,6 +2,11 @@
  *  include/asm-i386/bugs.h
  *
  *  Copyright (C) 1994  Linus Torvalds
+ *
+ *  Cyrix stuff, June 1998 by:
+ *	- Rafael R. Reilova (moved everything from head.S),
+ *	- Channing Corn (tests & fixes),
+ *	- Andrew D. Balsa (code cleanup).
  */
 
 /*
@@ -41,7 +46,7 @@
 	timer_table[COPRO_TIMER].expires = jiffies+100;
 	timer_active |= 1<<COPRO_TIMER;
 	printk(KERN_ERR "387 failed: trying to reset\n");
-	send_sig(SIGFPE, last_task_used_math, 1);
+	send_sig(SIGFPE, current, 1);
 	outb_p(0,0xf1);
 	outb_p(0,0xf0);
 }
@@ -156,7 +161,7 @@
  *	misexecution of code under Linux. Owners of such processors should
  *	contact AMD for precise details and a CPU swap.
  *
- *	See	http://www.chorus.com/~poulot/k6bug.html
+ *	See	http://www.mygale.com/~poulot/k6bug.html
  *		http://www.amd.com/K6/k6docs/revgd.html
  *
  *	The following test is erm.. interesting. AMD neglected to up
@@ -202,7 +207,7 @@
 			printk("system stability may be impaired when more than 32 MB are used.\n");
 		else 
 			printk("probably OK (after B9730xxxx).\n");
-		printk(KERN_INFO "Please see http://www.chorus.com/poulot/k6bug.html\n");
+		printk(KERN_INFO "Please see http://www.mygale.com/~poulot/k6bug.html\n");
 	}
 }
 
@@ -226,13 +231,99 @@
 	}
 }
 
+/*
+ * perform the Cyrix 5/2 test (!0 means it's a Cyrix)
+ */
+
+static inline int test_cyrix_52div(void)
+{
+	int test;
+
+	__asm__ __volatile__("xor %%eax,%%eax\n\t"
+	     "sahf\n\t"
+	     "movb $5,%%al\n\t"
+	     "movb $2,%%bl\n\t"
+	     "div %%bl\n\t"
+	     "lahf\n\t"
+	     "andl $0x200,%%eax": "=a" (test) : : "bx", "cc");
+
+	return test;
+}
+
+/*
+ * Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected
+ * by the fact that they preserve the flags across the division of 5/2.
+ * PII and PPro exhibit this behavior too, but they have cpuid available.
+ */
+
+__initfunc(static void check_cyrix_cpu(void))
+{
+	if (boot_cpu_data.cpuid_level == -1 && boot_cpu_data.x86 == 4
+	    && test_cyrix_52div()) {
+
+		/* default to an unknown Cx486, (we will diferentiate later) */
+		/* NOTE:  using 0xff since 0x00 is a valid DIR0 value */
+		strcpy(boot_cpu_data.x86_vendor_id, "CyrixInstead");
+		boot_cpu_data.x86_model = 0xff;
+		boot_cpu_data.x86_mask = 0;
+	}
+}
+
+/*
+ * Fix two problems with the Cyrix 686 and 686L:
+ *   -- the cpuid is disabled on power up, enable it, use it.
+ *   -- the SLOP bit needs resetting on some motherboards due to old BIOS,
+ *      so that the udelay loop calibration works well.  Recalibrate.
+ */
+
+extern void calibrate_delay(void) __init;
+
+__initfunc(static void check_cx686_cpuid_slop(void))
+{
+	if (boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX &&
+	    (boot_cpu_data.x86_model & 0xf0) == 0x30) {  /* 686(L) */
+		int dummy;
+		unsigned char ccr3, ccr5;
+
+		cli();
+		ccr3 = getCx86(CX86_CCR3);
+		setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);      /* enable MAPEN  */
+		setCx86(CX86_CCR4, getCx86(CX86_CCR4) | 0x80); /* enable cpuid  */
+		ccr5 = getCx86(CX86_CCR5);
+		if (ccr5 & 2)   /* reset SLOP if needed, old BIOS do this wrong */
+			setCx86(CX86_CCR5, ccr5 & 0xfd);
+		setCx86(CX86_CCR3, ccr3);                      /* disable MAPEN */
+		sti();
+
+		boot_cpu_data.cpuid_level = 1;  /* should cover all 6x86(L) */
+		boot_cpu_data.x86 = 5;
+
+		/* we know we have level 1 available on the 6x86(L) */
+		cpuid(1, &dummy, &dummy, &dummy,
+		      &boot_cpu_data.x86_capability);
+		/*
+		 * DON'T use the x86_mask and x86_model from cpuid, these are
+		 * not as accurate (or the same) as those from the DIR regs.
+		 * already in place after cyrix_model() in setup.c
+		 */
+
+		if (ccr5 & 2) { /* possible wrong calibration done */
+			printk(KERN_INFO "Recalibrating delay loop with SLOP bit reset\n");
+			calibrate_delay();
+			boot_cpu_data.loops_per_sec = loops_per_sec;
+		}
+	}
+}
+
 __initfunc(static void check_bugs(void))
 {
+	check_cyrix_cpu();
 	identify_cpu(&boot_cpu_data);
 #ifndef __SMP__
 	printk("CPU: ");
 	print_cpu_info(&boot_cpu_data);
 #endif
+	check_cx686_cpuid_slop();
 	check_tlb();
 	check_fpu();
 	check_hlt();
diff -u --recursive --new-file v2.1.108/linux/include/asm-i386/linux_logo.h linux/include/asm-i386/linux_logo.h
--- v2.1.108/linux/include/asm-i386/linux_logo.h	Wed Jun 24 22:54:10 1998
+++ linux/include/asm-i386/linux_logo.h	Thu Jul 16 15:11:40 1998
@@ -1,4 +1,4 @@
-/* $Id: linux_logo.h,v 1.3 1998/06/07 21:49:54 geert Exp $
+/* $Id: linux_logo.h,v 1.6 1998/07/07 13:34:56 jj Exp $
  * include/asm-i386/linux_logo.h: This is a linux logo
  *                                to be displayed on boot.
  *
@@ -28,49 +28,8 @@
 #ifdef INCLUDE_LINUX_LOGO_DATA
 
 #define INCLUDE_LINUX_LOGO16
-#include <linux/linux_logo.h>
-
-/* Painted by Johnny Stenback <jst@uwasa.fi> */
 
-unsigned char *linux_serial_image __initdata = "\n"
-"         .u$e.\n"
-"       .$$$$$:S\n"
-"       $\"*$/\"*$$\n"
-"       $.`$ . ^F\n"
-"       4k+#+T.$F\n"
-"       4P+++\"$\"$\n"
-"       :R\"+  t$$B\n"
-"    ___#       $$$\n"
-"    |  |       R$$k\n"
-"   dd. | Linux  $!$\n"
-"   ddd |  ia32  $9$F\n"
-" '!!!!!$       !!#!`\n"
-"  !!!!!*     .!!!!!`\n"
-"'!!!!!!!W..e$$!!!!!!`\n"
-" \"~^^~         ^~~^\n"
-"\n";
-
-/* The following created by Andrew Apted, May 1998 */
-
-unsigned char *linux_mda_image __initdata = "\n"
-"LINUX/IA32..........................\n"
-"::::::::::::::        ::::::::::::::\n"
-"::::::::::::             :::::::::::\n"
-"::::::::::::  ##   ##    :::::::::::\n"
-":::::::::::: # xxxx ##   :::::::::::\n"
-":::::::::::: xxxxxxxxx    ::::::::::\n"
-":::::::::::: ##xxx####     :::::::::\n"
-"::::::::::: ###########     ::::::::\n"
-":::::::::  #############      ::::::\n"
-"::::::::  ###############      :::::\n"
-"::::::   #################      ::::\n"
-"::::::  ##################      ::::\n"
-":::::xxx##################      ::::\n"
-":::xxxxxx  #############xxx   xxx:::\n"
-"xxxxxxxxxxx   ##########xxxxxxxxxx::\n"
-"xxxxxxxxxxxx ########## xxxxxxxxxxxx\n"
-"xxxxxxxxxxxxx#######    xxxxxxxxxxx:\n"
-":::::xxxxxxxx:::::::::::xxxxxx::::::\n\n";
+#include <linux/linux_logo.h>
 
 #else
 
@@ -84,9 +43,5 @@
 extern unsigned char linux_logo16_green[];
 extern unsigned char linux_logo16_blue[];
 extern unsigned char linux_logo16[];
-extern unsigned char *linux_serial_image;
-extern unsigned char *linux_mda_image;
-
-extern int (*console_show_logo)(void);
 
 #endif
diff -u --recursive --new-file v2.1.108/linux/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h
--- v2.1.108/linux/include/asm-i386/pgtable.h	Wed Jun 24 22:54:10 1998
+++ linux/include/asm-i386/pgtable.h	Thu Jul 16 15:11:06 1998
@@ -225,6 +225,9 @@
 #define _PAGE_4M	0x080	/* 4 MB page, Pentium+.. */
 #define _PAGE_GLOBAL	0x100	/* Global TLB entry PPro+ */
 
+#define _PAGE_READABLE  (_PAGE_PRESENT)             
+#define _PAGE_WRITABLE  (_PAGE_PRESENT | _PAGE_RW)
+
 #define _PAGE_TABLE	(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY)
 #define _KERNPG_TABLE	(_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
 #define _PAGE_CHG_MASK	(PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
@@ -330,21 +333,25 @@
  * Undefined behaviour if not..
  */
 extern inline int pte_read(pte_t pte)		{ return pte_val(pte) & _PAGE_USER; }
-extern inline int pte_write(pte_t pte)		{ return pte_val(pte) & _PAGE_RW; }
 extern inline int pte_exec(pte_t pte)		{ return pte_val(pte) & _PAGE_USER; }
 extern inline int pte_dirty(pte_t pte)		{ return pte_val(pte) & _PAGE_DIRTY; }
 extern inline int pte_young(pte_t pte)		{ return pte_val(pte) & _PAGE_ACCESSED; }
 
-extern inline pte_t pte_wrprotect(pte_t pte)	{ pte_val(pte) &= ~_PAGE_RW; return pte; }
 extern inline pte_t pte_rdprotect(pte_t pte)	{ pte_val(pte) &= ~_PAGE_USER; return pte; }
 extern inline pte_t pte_exprotect(pte_t pte)	{ pte_val(pte) &= ~_PAGE_USER; return pte; }
 extern inline pte_t pte_mkclean(pte_t pte)	{ pte_val(pte) &= ~_PAGE_DIRTY; return pte; }
 extern inline pte_t pte_mkold(pte_t pte)	{ pte_val(pte) &= ~_PAGE_ACCESSED; return pte; }
-extern inline pte_t pte_mkwrite(pte_t pte)	{ pte_val(pte) |= _PAGE_RW; return pte; }
 extern inline pte_t pte_mkread(pte_t pte)	{ pte_val(pte) |= _PAGE_USER; return pte; }
 extern inline pte_t pte_mkexec(pte_t pte)	{ pte_val(pte) |= _PAGE_USER; return pte; }
 extern inline pte_t pte_mkdirty(pte_t pte)	{ pte_val(pte) |= _PAGE_DIRTY; return pte; }
 extern inline pte_t pte_mkyoung(pte_t pte)	{ pte_val(pte) |= _PAGE_ACCESSED; return pte; }
+
+/*
+ * These are harder, as writability is two bits, not one..
+ */
+extern inline int pte_write(pte_t pte)		{ return (pte_val(pte) & _PAGE_WRITABLE) == _PAGE_WRITABLE; }
+extern inline pte_t pte_wrprotect(pte_t pte)	{ pte_val(pte) &= ~((pte_val(pte) & _PAGE_PRESENT) << 1); return pte; }
+extern inline pte_t pte_mkwrite(pte_t pte)	{ pte_val(pte) |= _PAGE_RW; return pte; }
 
 /*
  * Conversion functions: convert a page and protection to a page entry,
diff -u --recursive --new-file v2.1.108/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- v2.1.108/linux/include/asm-i386/processor.h	Tue Jun 23 10:01:26 1998
+++ linux/include/asm-i386/processor.h	Wed Jul 15 15:16:13 1998
@@ -47,6 +47,43 @@
 #define X86_VENDOR_CENTAUR 5
 #define X86_VENDOR_UNKNOWN 0xff
 
+/*
+ * capabilities of CPUs
+ */
+
+#define X86_FEATURE_FPU		0x00000001	/* onboard FPU */
+#define X86_FEATURE_VME		0x00000002	/* Virtual Mode Extensions */
+#define X86_FEATURE_DE		0x00000004	/* Debugging Extensions */
+#define X86_FEATURE_PSE		0x00000008	/* Page Size Extensions */
+#define X86_FEATURE_TSC		0x00000010	/* Time Stamp Counter */
+#define X86_FEATURE_MSR		0x00000020	/* Model-Specific Registers, RDMSR, WRMSR */
+#define X86_FEATURE_PAE		0x00000040	/* Physical Address Extensions */
+#define X86_FEATURE_MCE		0x00000080	/* Machine Check Exceptions */
+#define X86_FEATURE_CX8		0x00000100	/* CMPXCHG8 instruction */
+#define X86_FEATURE_APIC	0x00000200	/* onboard APIC */
+#define X86_FEATURE_10		0x00000400
+#define X86_FEATURE_SEP		0x00000800	/* Fast System Call */ 
+#define X86_FEATURE_MTRR	0x00001000	/* Memory Type Range Registers */
+#define X86_FEATURE_PGE		0x00002000	/* Page Global Enable */
+#define X86_FEATURE_MCA		0x00004000	/* Machine Check Architecture */
+#define X86_FEATURE_CMOV	0x00008000	/* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */
+#define X86_FEATURE_PAT	0x00010000	/* Page Attribute Table */
+#define X86_FEATURE_PSE36	0x00020000	/* 36-bit PSEs */
+#define X86_FEATURE_18		0x00040000
+#define X86_FEATURE_19		0x00080000
+#define X86_FEATURE_20		0x00100000
+#define X86_FEATURE_21		0x00200000
+#define X86_FEATURE_22		0x00400000
+#define X86_FEATURE_MMX		0x00800000	/* multimedia extensions */
+#define X86_FEATURE_FXSR	0x01000000	/* FXSAVE and FXRSTOR instructions (fast save and restore of FPU context), and CR4.OSFXSR (OS uses these instructions) available */
+#define X86_FEATURE_25		0x02000000
+#define X86_FEATURE_26		0x04000000
+#define X86_FEATURE_27		0x08000000
+#define X86_FEATURE_28		0x10000000
+#define X86_FEATURE_29		0x20000000
+#define X86_FEATURE_30		0x40000000
+#define X86_FEATURE_AMD3D	0x80000000
+
 extern struct cpuinfo_x86 boot_cpu_data;
 
 #ifdef __SMP__
@@ -63,6 +100,51 @@
 extern void print_cpu_info(struct cpuinfo_x86 *);
 
 /*
+ *	Generic CPUID function
+ */
+extern inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx)
+{
+	__asm__("cpuid"
+		: "=a" (*eax),
+		  "=b" (*ebx),
+		  "=c" (*ecx),
+		  "=d" (*edx)
+		: "a" (op)
+		: "cc");
+}
+
+/*
+ *      Cyrix CPU configuration register indexes
+ */
+#define CX86_CCR2 0xc2
+#define CX86_CCR3 0xc3
+#define CX86_CCR4 0xe8
+#define CX86_CCR5 0xe9
+#define CX86_DIR0 0xfe
+#define CX86_DIR1 0xff
+
+/*
+ *      Cyrix CPU indexed register access macros
+ */
+
+extern inline unsigned char getCx86(unsigned char reg)
+{
+	unsigned char data;
+
+	__asm__ __volatile__("movb %1,%%al\n\t"
+		      "outb %%al,$0x22\n\t"
+		      "inb $0x23,%%al" : "=a" (data) : "q" (reg));
+	return data;
+}
+
+extern inline void setCx86(unsigned char reg, unsigned char data)
+{
+	__asm__ __volatile__("outb %%al,$0x22\n\t"
+	     "movb %1,%%al\n\t"
+	     "outb %%al,$0x23" : : "a" (reg), "q" (data));
+}
+
+/*
  * Bus types (default is ISA, but people can check others with these..)
  */
 extern int EISA_bus;
@@ -74,9 +156,6 @@
 extern unsigned int machine_submodel_id;
 extern unsigned int BIOS_revision;
 
-/* Lazy FPU handling on uni-processor */
-extern struct task_struct *last_task_used_math;
-
 /*
  * User space process size: 3GB (default).
  */
@@ -166,33 +245,34 @@
 #define INIT_MMAP \
 { &init_mm, 0, 0, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, NULL, &init_mm.mmap }
 
-#define INIT_TSS  { \
-	0,0, \
-	sizeof(init_stack) + (long) &init_stack, \
-	__KERNEL_DS, 0, \
-	0,0,0,0,0,0, \
-	(long) &swapper_pg_dir - PAGE_OFFSET, \
-	0,0,0,0,0,0,0,0,0,0, \
-	__USER_DS,0,__USER_DS,0,__USER_DS,0, \
-	__USER_DS,0,__USER_DS,0,__USER_DS,0, \
-	_LDT(0),0, \
-	0, 0x8000, \
-	{~0, }, /* ioperm */ \
-	_TSS(0), 0, 0, 0, (mm_segment_t) { 0 } /* obsolete */ , \
-	{ { 0, }, },  /* 387 state */ \
-	NULL, 0, 0, 0, 0, 0 /* vm86_info */, \
+#define INIT_TSS  {						\
+	0,0, /* back_link, __blh */				\
+	sizeof(init_stack) + (long) &init_stack, /* esp0 */	\
+	__KERNEL_DS, 0, /* ss0 */				\
+	0,0,0,0,0,0, /* stack1, stack2 */			\
+	(long) &swapper_pg_dir - PAGE_OFFSET, /* cr3 */		\
+	0,0, /* eip,eflags */					\
+	0,0,0,0, /* eax,ecx,edx,ebx */				\
+	0,0,0,0, /* esp,ebp,esi,edi */				\
+	0,0,0,0,0,0, /* es,cs,ss */				\
+	0,0,0,0,0,0, /* ds,fs,gs */				\
+	_LDT(0),0, /* ldt */					\
+	0, 0x8000, /* tace, bitmap */				\
+	{~0, }, /* ioperm */					\
+	_TSS(0), 0, 0, 0, (mm_segment_t) { 0 }, /* obsolete */	\
+	{ { 0, }, },  /* 387 state */				\
+	NULL, 0, 0, 0, 0, 0, /* vm86_info */			\
 }
 
-#define start_thread(regs, new_eip, new_esp) do {\
-	unsigned long seg = __USER_DS; \
-	__asm__("movl %w0,%%fs ; movl %w0,%%gs":"=r" (seg) :"0" (seg)); \
-	set_fs(USER_DS); \
-	regs->xds = seg; \
-	regs->xes = seg; \
-	regs->xss = seg; \
-	regs->xcs = __USER_CS; \
-	regs->eip = new_eip; \
-	regs->esp = new_esp; \
+#define start_thread(regs, new_eip, new_esp) do {		\
+	__asm__("movl %w0,%%fs ; movl %w0,%%gs": :"r" (0));	\
+	set_fs(USER_DS);					\
+	regs->xds = __USER_DS;					\
+	regs->xes = __USER_DS;					\
+	regs->xss = __USER_DS;					\
+	regs->xcs = __USER_CS;					\
+	regs->eip = new_eip;					\
+	regs->esp = new_esp;					\
 } while (0)
 
 /* Forward declaration, a strange C thing */
@@ -213,13 +293,8 @@
 	return ((unsigned long *)t->esp)[3];
 }
 
-/* Allocation and freeing of basic task resources. */
-/*
- * NOTE! The task struct and the stack go together
- */
-#define alloc_task_struct() \
-	((struct task_struct *) __get_free_pages(GFP_KERNEL,1))
-#define free_task_struct(p)	free_pages((unsigned long)(p),1)
+extern struct task_struct * alloc_task_struct(void);
+extern void free_task_struct(struct task_struct *);
 
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
diff -u --recursive --new-file v2.1.108/linux/include/asm-i386/system.h linux/include/asm-i386/system.h
--- v2.1.108/linux/include/asm-i386/system.h	Sat Apr 25 18:13:12 1998
+++ linux/include/asm-i386/system.h	Fri Jul 10 16:19:06 1998
@@ -1,6 +1,7 @@
 #ifndef __ASM_SYSTEM_H
 #define __ASM_SYSTEM_H
 
+#include <linux/kernel.h>
 #include <asm/segment.h>
 
 /*
@@ -35,83 +36,34 @@
 	:"=a" (n) \
 	:"0" (0),"i" (FIRST_TSS_ENTRY<<3))
 
-/* This special macro can be used to load a debugging register */
-
-#define loaddebug(tsk,register) \
-		__asm__("movl %0,%%db" #register  \
-			: /* no output */ \
-			:"r" (tsk->debugreg[register]))
-
+struct task_struct;	/* one of the stranger aspects of C forward declarations.. */
+extern void FASTCALL(__switch_to(struct task_struct *prev, struct task_struct *next));
 
 /*
- *	switch_to(n) should switch tasks to task nr n, first
- * checking that n isn't the current task, in which case it does nothing.
- * This also clears the TS-flag if the task we switched to has used
- * the math co-processor latest.
- *
- * It also reloads the debug regs if necessary..
+ * We do most of the task switching in C, but we need
+ * to do the EIP/ESP switch in assembly..
  */
-
- 
-#ifdef __SMP__
-	/*
-	 *	Keep the lock depth straight. If we switch on an interrupt from
-	 *	kernel->user task we need to lose a depth, and if we switch the
-	 *	other way we need to gain a depth. Same layer switches come out
-	 *	the same.
-	 *
-	 *	We spot a switch in user mode because the kernel counter is the
-	 *	same as the interrupt counter depth. (We never switch during the
-	 *	message/invalidate IPI).
-	 *
-	 *	We fsave/fwait so that an exception goes off at the right time
-	 *	(as a call from the fsave or fwait in effect) rather than to
-	 *	the wrong process.
-	 */
-
-#define switch_to(prev,next) do { \
-	if(prev->flags&PF_USEDFPU) \
-	{ \
-		__asm__ __volatile__("fnsave %0":"=m" (prev->tss.i387.hard)); \
-		__asm__ __volatile__("fwait"); \
-		prev->flags&=~PF_USEDFPU;	 \
-	} \
-__asm__("ljmp %0\n\t" \
-	: /* no output */ \
-	:"m" (*(((char *)&next->tss.tr)-4)), \
-	 "c" (next)); \
-	/* Now maybe reload the debug registers */ \
-	if(prev->debugreg[7]){ \
-		loaddebug(prev,0); \
-		loaddebug(prev,1); \
-		loaddebug(prev,2); \
-		loaddebug(prev,3); \
-		loaddebug(prev,6); \
-		loaddebug(prev,7); \
-	} \
-} while (0)
-
-#else
-#define switch_to(prev,next) do { \
-__asm__("ljmp %0\n\t" \
-	"cmpl %1,"SYMBOL_NAME_STR(last_task_used_math)"\n\t" \
-	"jne 1f\n\t" \
-	"clts\n" \
-	"1:" \
-	: /* no outputs */ \
-	:"m" (*(((char *)&next->tss.tr)-4)), \
-	 "r" (prev), "r" (next)); \
-	/* Now maybe reload the debug registers */ \
-	if(prev->debugreg[7]){ \
-		loaddebug(prev,0); \
-		loaddebug(prev,1); \
-		loaddebug(prev,2); \
-		loaddebug(prev,3); \
-		loaddebug(prev,6); \
-		loaddebug(prev,7); \
-	} \
+#define switch_to(prev,next) do {					\
+	unsigned long eax, edx, ecx;					\
+	asm volatile("pushl %%ebx\n\t"					\
+		     "pushl %%esi\n\t"					\
+		     "pushl %%edi\n\t"					\
+		     "pushl %%ebp\n\t"					\
+		     "movl %%esp,%0\n\t"	/* save ESP */		\
+		     "movl %5,%%esp\n\t"	/* restore ESP */	\
+		     "movl $1f,%1\n\t"		/* save EIP */		\
+		     "pushl %6\n\t"		/* restore EIP */	\
+		     "jmp __switch_to\n"				\
+		     "1:\t"						\
+		     "popl %%ebp\n\t"					\
+		     "popl %%edi\n\t"					\
+		     "popl %%esi\n\t"					\
+		     "popl %%ebx"					\
+		     :"=m" (prev->tss.esp),"=m" (prev->tss.eip),	\
+		      "=a" (eax), "=d" (edx), "=c" (ecx)		\
+		     :"m" (next->tss.esp),"m" (next->tss.eip),		\
+		      "a" (prev), "d" (next)); 				\
 } while (0)
-#endif
 
 #define _set_base(addr,base) \
 __asm__("movw %%dx,%0\n\t" \
diff -u --recursive --new-file v2.1.108/linux/include/asm-i386/vga.h linux/include/asm-i386/vga.h
--- v2.1.108/linux/include/asm-i386/vga.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-i386/vga.h	Fri Jul 10 15:18:32 1998
@@ -0,0 +1,20 @@
+/*
+ *	Access to VGA videoram
+ *
+ *	(c) 1998 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _LINUX_ASM_VGA_H_
+#define _LINUX_ASM_VGA_H_
+
+/*
+ *	On the PC, we can just recalculate addresses and then
+ *	access the videoram directly without any black magic.
+ */
+
+#define VGA_MAP_MEM(x) (unsigned long)phys_to_virt(x)
+
+#define vga_readb(x) (*(x))
+#define vga_writeb(x,y) (*(y) = (x))
+
+#endif
diff -u --recursive --new-file v2.1.108/linux/include/asm-m68k/linux_logo.h linux/include/asm-m68k/linux_logo.h
--- v2.1.108/linux/include/asm-m68k/linux_logo.h	Fri May  8 23:14:54 1998
+++ linux/include/asm-m68k/linux_logo.h	Fri Jul 10 15:18:32 1998
@@ -30,26 +30,6 @@
 #define INCLUDE_LINUX_LOGO16
 #include <linux/linux_logo.h>
 
-/* Painted by Johnny Stenback <jst@uwasa.fi> */
-
-unsigned char *linux_serial_image __initdata = "\n"
-"         .u$e.\n"
-"       .$$$$$:S\n"
-"       $\"*$/\"*$$\n"
-"       $.`$ . ^F\n"
-"       4k+#+T.$F\n"
-"       4P+++\"$\"$\n"
-"       :R\"+  t$$B\n"
-"    ___#       $$$\n"
-"    |  |       R$$k\n"
-"   dd. | Linux  $!$\n"
-"   ddd |  m68k  $9$F\n"
-" '!!!!!$       !!#!`\n"
-"  !!!!!*     .!!!!!`\n"
-"'!!!!!!!W..e$$!!!!!!`    %s\n"
-" \"~^^~         ^~~^\n"
-"\n";
-
 #else
 
 /* prototypes only */
@@ -62,8 +42,5 @@
 extern unsigned char linux_logo16_green[];
 extern unsigned char linux_logo16_blue[];
 extern unsigned char linux_logo16[];
-extern unsigned char *linux_serial_image;
-
-extern int (*console_show_logo)(void);
 
 #endif
diff -u --recursive --new-file v2.1.108/linux/include/asm-mips/vga.h linux/include/asm-mips/vga.h
--- v2.1.108/linux/include/asm-mips/vga.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-mips/vga.h	Fri Jul 10 15:18:32 1998
@@ -0,0 +1,35 @@
+/*
+ *	Access to VGA videoram
+ *
+ *	(c) 1998 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _LINUX_ASM_VGA_H_
+#define _LINUX_ASM_VGA_H_
+
+#include <asm/io.h>
+
+#define VT_BUF_HAVE_RW
+
+extern inline void scr_writew(u16 val, u16 *addr)
+{
+	if ((long) addr < 0)
+		*addr = val;
+	else
+		writew(val, (unsigned long) addr);
+}
+
+extern inline u16 scr_readw(u16 *addr)
+{
+	if ((long) addr < 0)
+		return *addr;
+	else
+		return readw((unsigned long) addr);
+}
+
+#define vga_readb readb
+#define vga_writeb writeb
+
+#define VGA_MAP_MEM(x) (x)
+
+#endif
diff -u --recursive --new-file v2.1.108/linux/include/asm-ppc/linux_logo.h linux/include/asm-ppc/linux_logo.h
--- v2.1.108/linux/include/asm-ppc/linux_logo.h	Fri May  8 23:14:56 1998
+++ linux/include/asm-ppc/linux_logo.h	Fri Jul 10 15:18:32 1998
@@ -24,26 +24,6 @@
 #define INCLUDE_LINUX_LOGO16
 #include <linux/linux_logo.h>
 
-/* Painted by Johnny Stenback <jst@uwasa.fi> */
-
-unsigned char *linux_serial_image __initdata = "\n"
-"         .u$e.\n"
-"       .$$$$$:S\n"
-"       $\"*$/\"*$$\n"
-"       $.`$ . ^F\n"
-"       4k+#+T.$F\n"
-"       4P+++\"$\"$\n"
-"       :R\"+  t$$B\n"
-"    ___#       $$$\n"
-"    |  |       R$$k\n"
-"   dd. | Linux  $!$\n"
-"   ddd |  PPC   $9$F\n"
-" '!!!!!$       !!#!`\n"
-"  !!!!!*     .!!!!!`\n"
-"'!!!!!!!W..e$$!!!!!!`    %s\n"
-" \"~^^~         ^~~^\n"
-"\n";
-
 #else
 
 /* prototypes only */
@@ -56,8 +36,5 @@
 extern unsigned char linux_logo16_green[];
 extern unsigned char linux_logo16_blue[];
 extern unsigned char linux_logo16[];
-extern unsigned char *linux_serial_image;
-
-extern int (*console_show_logo)(void);
 
 #endif
diff -u --recursive --new-file v2.1.108/linux/include/asm-ppc/vga.h linux/include/asm-ppc/vga.h
--- v2.1.108/linux/include/asm-ppc/vga.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/vga.h	Fri Jul 10 15:18:32 1998
@@ -0,0 +1,29 @@
+/*
+ *	Access to VGA videoram
+ *
+ *	(c) 1998 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _LINUX_ASM_VGA_H_
+#define _LINUX_ASM_VGA_H_
+
+#include <asm/io.h>
+
+#define VT_BUF_HAVE_RW
+
+extern inline void scr_writew(u16 val, u16 *addr)
+{
+	st_le16(addr, val);
+}
+
+extern inline u16 scr_readw(u16 *addr)
+{
+	return ld_le16(addr);
+}
+
+#define VGA_MAP_MEM(x) (x + _ISA_MEM_BASE)
+
+#define vga_readb(x) (*(x))
+#define vga_writeb(x,y) (*(y) = (x))
+
+#endif
diff -u --recursive --new-file v2.1.108/linux/include/asm-sparc/fbio.h linux/include/asm-sparc/fbio.h
--- v2.1.108/linux/include/asm-sparc/fbio.h	Thu Jul 31 13:09:18 1997
+++ linux/include/asm-sparc/fbio.h	Fri Jul 10 15:18:32 1998
@@ -56,8 +56,13 @@
         unsigned char   *blue;
 };
 
+#ifdef __KERNEL__
+#define FBIOPUTCMAP_SPARC _IOW('F', 3, struct fbcmap)
+#define FBIOGETCMAP_SPARC _IOW('F', 4, struct fbcmap)
+#else
 #define FBIOPUTCMAP _IOW('F', 3, struct fbcmap)
 #define FBIOGETCMAP _IOW('F', 4, struct fbcmap)
+#endif
 
 /* # of device specific values */
 #define FB_ATTR_NDEVSPECIFIC    8
diff -u --recursive --new-file v2.1.108/linux/include/asm-sparc/linux_logo.h linux/include/asm-sparc/linux_logo.h
--- v2.1.108/linux/include/asm-sparc/linux_logo.h	Fri May  8 23:14:56 1998
+++ linux/include/asm-sparc/linux_logo.h	Fri Jul 10 15:18:32 1998
@@ -1,4 +1,4 @@
-/* $Id: linux_logo.h,v 1.2 1998/05/04 14:20:59 jj Exp $
+/* $Id: linux_logo.h,v 1.3 1998/07/08 10:21:23 jj Exp $
  * include/asm-sparc/linux_logo.h: This is a linux logo
  *                                 to be displayed on boot.
  *
@@ -27,22 +27,3 @@
 
 #include <linux/linux_logo.h>
 
-/* Painted by Johnny Stenback <jst@uwasa.fi> */
-
-unsigned char *linux_serial_image __initdata = "\n"
-"         .u$e.\n"
-"       .$$$$$:S\n"
-"       $\"*$/\"*$$\n"
-"       $.`$ . ^F\n"
-"       4k+#+T.$F\n"
-"       4P+++\"$\"$\n"
-"       :R\"+  t$$B\n"
-"    ___#       $$$\n"
-"    |  |       R$$k\n"
-"   dd. | Linux  $!$\n"
-"   ddd |  Sparc $9$F\n"
-" '!!!!!$       !!#!`\n"
-"  !!!!!*     .!!!!!`\n"
-"'!!!!!!!W..e$$!!!!!!`    %s\n"
-" \"~^^~         ^~~^\n"
-"\n";
diff -u --recursive --new-file v2.1.108/linux/include/asm-sparc64/fbio.h linux/include/asm-sparc64/fbio.h
--- v2.1.108/linux/include/asm-sparc64/fbio.h	Thu Sep  4 12:54:49 1997
+++ linux/include/asm-sparc64/fbio.h	Fri Jul 10 15:18:32 1998
@@ -59,8 +59,13 @@
         unsigned char   *blue;
 };
 
+#ifdef __KERNEL__
+#define FBIOPUTCMAP_SPARC _IOW('F', 3, struct fbcmap)
+#define FBIOGETCMAP_SPARC _IOW('F', 4, struct fbcmap)
+#else
 #define FBIOPUTCMAP _IOW('F', 3, struct fbcmap)
 #define FBIOGETCMAP _IOW('F', 4, struct fbcmap)
+#endif
 
 /* # of device specific values */
 #define FB_ATTR_NDEVSPECIFIC    8
diff -u --recursive --new-file v2.1.108/linux/include/asm-sparc64/linux_logo.h linux/include/asm-sparc64/linux_logo.h
--- v2.1.108/linux/include/asm-sparc64/linux_logo.h	Thu Sep  4 12:54:49 1997
+++ linux/include/asm-sparc64/linux_logo.h	Fri Jul 10 15:18:32 1998
@@ -1,4 +1,4 @@
-/* $Id: linux_logo.h,v 1.3 1997/08/25 07:50:35 jj Exp $
+/* $Id: linux_logo.h,v 1.4 1998/07/01 15:43:05 jj Exp $
  * include/asm-sparc64/linux_logo.h: This is a linux logo
  *                                   to be displayed on boot.
  *
@@ -25,6 +25,8 @@
 
 #define LINUX_LOGO_COLORS 215
 
+#ifdef INCLUDE_LINUX_LOGO_DATA
+
 unsigned char linux_logo_red[] __initdata = {
   0x99, 0x95, 0x92, 0x8E, 0x8A, 0x86, 0x02, 0x00,
   0xA5, 0xA9, 0xA2, 0x9E, 0xAD, 0x1B, 0x3B, 0x25,
@@ -1021,22 +1023,22 @@
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 };
 
-/* Painted by Johnny Stenback <jst@uwasa.fi> */
+unsigned char linux_logo16_red[0];
+unsigned char linux_logo16_green[0];
+unsigned char linux_logo16_blue[0];
+unsigned char linux_logo16[0];
+
+#else
+
+/* prototypes only */
+extern unsigned char linux_logo_red[];
+extern unsigned char linux_logo_green[];
+extern unsigned char linux_logo_blue[];
+extern unsigned char linux_logo[];
+extern unsigned char linux_logo_bw[];
+extern unsigned char linux_logo16_red[];
+extern unsigned char linux_logo16_green[];
+extern unsigned char linux_logo16_blue[];
+extern unsigned char linux_logo16[];
 
-unsigned char *linux_serial_image __initdata = "\n"
-"         .u$e.\n"
-"       .$$$$$:S\n"
-"       $\"*$/\"*$$\n"
-"       $.`$ . ^F\n"
-"       4k+#+T.$F\n"
-"       4P+++\"$\"$\n"
-"       :R\"+  t$$B\n"
-"    ___#       $$$\n"
-"    |  |       R$$k\n"
-"   dd. | Linux  $!$\n"
-"   ddd |  Sparc $9$F\n"
-" '!!!!!$       !!#!`\n"
-"  !!!!!*     .!!!!!`\n"
-"'!!!!!!!W..e$$!!!!!!`    %s\n"
-" \"~^^~         ^~~^\n"
-"\n";
+#endif
diff -u --recursive --new-file v2.1.108/linux/include/asm-sparc64/vga.h linux/include/asm-sparc64/vga.h
--- v2.1.108/linux/include/asm-sparc64/vga.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-sparc64/vga.h	Fri Jul 10 15:18:32 1998
@@ -0,0 +1,30 @@
+/*
+ *	Access to VGA videoram
+ *
+ *	(c) 1998 Martin Mares <mj@ucw.cz>
+ */
+
+#ifndef _LINUX_ASM_VGA_H_
+#define _LINUX_ASM_VGA_H_
+
+#define VT_BUF_HAVE_RW
+
+static inline void scr_writew(u16 val, u16 *addr)
+{
+	if ((long) addr < 0)
+		*addr = val;
+	else
+		writew(val, (unsigned long) addr);
+}
+
+static inline u16 scr_readw(u16 *addr)
+{
+	if ((long) addr < 0)
+		return *addr;
+	else
+		return readw((unsigned long) addr);
+}
+
+#define VGA_MAP_MEM(x) (x)
+
+#endif
diff -u --recursive --new-file v2.1.108/linux/include/linux/coda.h linux/include/linux/coda.h
--- v2.1.108/linux/include/linux/coda.h	Wed Jun 24 22:54:11 1998
+++ linux/include/linux/coda.h	Fri Jul 10 15:33:37 1998
@@ -1,3 +1,4 @@
+
 /*
  *
  * Based on cfs.h from Mach, but revamped for increased simplicity.
diff -u --recursive --new-file v2.1.108/linux/include/linux/coda_psdev.h linux/include/linux/coda_psdev.h
--- v2.1.108/linux/include/linux/coda_psdev.h	Thu May  7 22:51:55 1998
+++ linux/include/linux/coda_psdev.h	Fri Jul 10 15:33:37 1998
@@ -74,7 +74,7 @@
 			  const char *name, int length, 
 			  struct ViceFid *newfid, struct coda_vattr *attrs);
 int venus_create(struct super_block *sb, struct ViceFid *dirfid, 
-		    const char *name, int length, int excl, int mode, 
+		    const char *name, int length, int excl, int mode, int rdev,
 		    struct ViceFid *newfid, struct coda_vattr *attrs) ;
 int venus_rmdir(struct super_block *sb, struct ViceFid *dirfid, 
 		    const char *name, int length);
diff -u --recursive --new-file v2.1.108/linux/include/linux/console.h linux/include/linux/console.h
--- v2.1.108/linux/include/linux/console.h	Wed Jun 24 22:54:11 1998
+++ linux/include/linux/console.h	Fri Jul 10 15:18:32 1998
@@ -14,8 +14,6 @@
 #ifndef _LINUX_CONSOLE_H_
 #define _LINUX_CONSOLE_H_ 1
 
-#define NPAR 16
-
 struct vc_data;
 
 /*
@@ -27,14 +25,14 @@
 /* DPC: 1994-04-13 !!! con_putcs is new entry !!! */
 
 struct consw {
-	unsigned long	(*con_startup)(unsigned long, const char **);
-	void	(*con_init)(struct vc_data *);
+	const char *(*con_startup)(void);
+	void	(*con_init)(struct vc_data *, int);
 	void	(*con_deinit)(struct vc_data *);
 	void	(*con_clear)(struct vc_data *, int, int, int, int);
 	void	(*con_putc)(struct vc_data *, int, int, int);
-	void	(*con_putcs)(struct vc_data *, const char *, int, int, int);
+	void	(*con_putcs)(struct vc_data *, const unsigned short *, int, int, int);
 	void	(*con_cursor)(struct vc_data *, int);
-	void	(*con_scroll)(struct vc_data *, int, int, int, int);
+	int	(*con_scroll)(struct vc_data *, int, int, int, int);
 	void	(*con_bmove)(struct vc_data *, int, int, int, int, int, int);
 	int	(*con_switch)(struct vc_data *);
 	int	(*con_blank)(int);
@@ -42,13 +40,17 @@
 	int	(*con_set_font)(struct vc_data *, int, int, char *);
 	int	(*con_set_palette)(struct vc_data *, unsigned char *);
 	int	(*con_scrolldelta)(struct vc_data *, int);
+	int	(*con_set_origin)(struct vc_data *);
+	void	(*con_save_screen)(struct vc_data *);
 };
 
 extern struct consw *conswitchp;
 
+extern struct consw dummy_con;	/* dummy console buffer */
 extern struct consw fb_con;	/* frame buffer based console */
 extern struct consw vga_con;	/* VGA text console */
 extern struct consw compat_con;	/* console wrapper */
+extern struct consw prom_con;	/* SPARC PROM console */
 
 /* flag bits */
 #define CON_INITED  (1)
@@ -63,9 +65,6 @@
 #define CM_DRAW     (1)
 #define CM_ERASE    (2)
 #define CM_MOVE     (3)
-
-struct tty_struct;
-int tioclinux(struct tty_struct *tty, unsigned long arg);
 
 /*
  *	Array of consoles built from command line options (console=)
diff -u --recursive --new-file v2.1.108/linux/include/linux/console_compat.h linux/include/linux/console_compat.h
--- v2.1.108/linux/include/linux/console_compat.h	Wed Jul  1 19:38:56 1998
+++ linux/include/linux/console_compat.h	Fri Jul 10 15:18:32 1998
@@ -8,11 +8,11 @@
  *  for more details.
  */
 
+#include <linux/config.h>
+
 #ifndef _LINUX_CONSOLE_COMPAT_H_
 #define _LINUX_CONSOLE_COMPAT_H_
 
-#include <linux/config.h>
-
 #undef video_num_columns
 #undef video_num_lines
 #undef video_size_row
@@ -23,8 +23,8 @@
 #undef can_do_color
 #undef scr_writew
 #undef scr_readw
-#undef memsetw
-#undef memcpyw
+#undef scr_memsetw
+#undef scr_memcpyw
 #undef set_cursor
 #undef hide_cursor
 #undef set_get_cmap
@@ -47,8 +47,8 @@
 #define can_do_color		compat_can_do_color
 #define scr_writew		compat_scr_writew
 #define scr_readw		compat_scr_readw
-#define memsetw			compat_memsetw
-#define memcpyw			compat_memcpyw
+#define scr_memsetw		compat_memsetw
+#define scr_memcpyw		compat_memcpyw
 #define set_cursor		compat_set_cursor
 #define hide_cursor		compat_hide_cursor
 #define set_get_cmap		compat_set_get_cmap
diff -u --recursive --new-file v2.1.108/linux/include/linux/console_struct.h linux/include/linux/console_struct.h
--- v2.1.108/linux/include/linux/console_struct.h	Wed Jul  1 19:38:56 1998
+++ linux/include/linux/console_struct.h	Mon Jul 13 12:43:04 1998
@@ -14,60 +14,60 @@
 
 struct vc_data {
 	unsigned short	vc_num;			/* Console number */
-	unsigned long	vc_cols;
-	unsigned long	vc_rows;
-	unsigned long	vc_size_row;
+	unsigned int	vc_cols;		/* Console size */
+	unsigned int	vc_rows;
+	unsigned int	vc_size_row;		/* Bytes per row */
 	struct consw	*vc_sw;
-	unsigned long	vc_screenbuf_size;
+	unsigned short	*vc_screenbuf;		/* In-memory character/attribute buffer */
+	unsigned int	vc_screenbuf_size;
 	unsigned short	vc_video_erase_char;	/* Background erase character */
 	unsigned char	vc_attr;		/* Current attributes */
 	unsigned char	vc_def_color;		/* Default colors */
 	unsigned char	vc_color;		/* Foreground & background */
 	unsigned char	vc_s_color;		/* Saved foreground & background */
-	unsigned char	vc_ulcolor;		/* Colour for underline mode */
-	unsigned char	vc_halfcolor;		/* Colour for half intensity mode */
-	unsigned long	vc_origin;		/* Used for EGA/VGA fast scroll	*/
-	unsigned long	vc_scr_end;		/* Used for EGA/VGA fast scroll	*/
-	unsigned long	vc_x,vc_y;
-	unsigned long	vc_top,vc_bottom;
-	unsigned long	vc_state;
-	unsigned long	vc_npar,vc_par[NPAR];
-	unsigned long	vc_pos;
-	unsigned long	vc_video_mem_start;	/* Start of video RAM		*/
-	unsigned long	vc_video_mem_end;	/* End of video RAM (sort of)	*/
-	unsigned long	vc_saved_x;
-	unsigned long	vc_saved_y;
+	unsigned char	vc_ulcolor;		/* Color for underline mode */
+	unsigned char	vc_halfcolor;		/* Color for half intensity mode */
+	unsigned int	vc_x, vc_y;		/* Cursor position */
+	unsigned int	vc_top, vc_bottom;	/* Scrolling region */
+	unsigned int	vc_state;		/* Escape sequence parser state */
+	unsigned int	vc_npar,vc_par[NPAR];	/* Parameters of current escape sequence */
+	unsigned long	vc_scr_top;		/* Top of video memory */
+	unsigned long	vc_origin;		/* Start of real screen */
+	unsigned long	vc_scr_end;		/* End of real screen */
+	unsigned long	vc_visible_origin;	/* Top of visible window */
+	unsigned long	vc_pos;			/* Cursor address */
+	unsigned int	vc_saved_x;
+	unsigned int	vc_saved_y;
 	/* mode flags */
-	unsigned long	vc_charset	: 1;	/* Character set G0 / G1 */
-	unsigned long	vc_s_charset	: 1;	/* Saved character set */
-	unsigned long	vc_disp_ctrl	: 1;	/* Display chars < 32? */
-	unsigned long	vc_toggle_meta	: 1;	/* Toggle high bit? */
-	unsigned long	vc_decscnm	: 1;	/* Screen Mode */
-	unsigned long	vc_decom	: 1;	/* Origin Mode */
-	unsigned long	vc_decawm	: 1;	/* Autowrap Mode */
-	unsigned long	vc_deccm	: 1;	/* Cursor Visible */
-	unsigned long	vc_decim	: 1;	/* Insert Mode */
-	unsigned long	vc_deccolm	: 1;	/* 80/132 Column Mode */
+	unsigned int	vc_charset	: 1;	/* Character set G0 / G1 */
+	unsigned int	vc_s_charset	: 1;	/* Saved character set */
+	unsigned int	vc_disp_ctrl	: 1;	/* Display chars < 32? */
+	unsigned int	vc_toggle_meta	: 1;	/* Toggle high bit? */
+	unsigned int	vc_decscnm	: 1;	/* Screen Mode */
+	unsigned int	vc_decom	: 1;	/* Origin Mode */
+	unsigned int	vc_decawm	: 1;	/* Autowrap Mode */
+	unsigned int	vc_deccm	: 1;	/* Cursor Visible */
+	unsigned int	vc_decim	: 1;	/* Insert Mode */
+	unsigned int	vc_deccolm	: 1;	/* 80/132 Column Mode */
 	/* attribute flags */
-	unsigned long	vc_intensity	: 2;	/* 0=half-bright, 1=normal, 2=bold */
-	unsigned long	vc_underline	: 1;
-	unsigned long	vc_blink	: 1;
-	unsigned long	vc_reverse	: 1;
-	unsigned long	vc_s_intensity	: 2;	/* saved rendition */
-	unsigned long	vc_s_underline	: 1;
-	unsigned long	vc_s_blink	: 1;
-	unsigned long	vc_s_reverse	: 1;
+	unsigned int	vc_intensity	: 2;	/* 0=half-bright, 1=normal, 2=bold */
+	unsigned int	vc_underline	: 1;
+	unsigned int	vc_blink	: 1;
+	unsigned int	vc_reverse	: 1;
+	unsigned int	vc_s_intensity	: 2;	/* saved rendition */
+	unsigned int	vc_s_underline	: 1;
+	unsigned int	vc_s_blink	: 1;
+	unsigned int	vc_s_reverse	: 1;
 	/* misc */
-	unsigned long	vc_ques		: 1;
-	unsigned long	vc_need_wrap	: 1;
-	unsigned long	vc_can_do_color	: 1;
-	unsigned long	vc_has_scrolled : 1;	/* Info for unblank_screen */
-	unsigned long	vc_kmalloced	: 1;	/* kfree_s() needed */
-	unsigned long	vc_report_mouse : 2;
+	unsigned int	vc_ques		: 1;
+	unsigned int	vc_need_wrap	: 1;
+	unsigned int	vc_can_do_color	: 1;
+	unsigned int	vc_report_mouse : 2;
+	unsigned int	vc_kmalloced	: 1;
 	unsigned char	vc_utf		: 1;	/* Unicode UTF-8 encoding */
 	unsigned char	vc_utf_count;
-		 long	vc_utf_char;
-	unsigned long	vc_tab_stop[5];		/* Tab stops. 160 columns. */
+		 int	vc_utf_char;
+	unsigned int	vc_tab_stop[5];		/* Tab stops. 160 columns. */
 	unsigned char   vc_palette[16*3];       /* Colour palette for VGA+ */
 	unsigned short * vc_translate;
 	unsigned char 	vc_G0_charset;
diff -u --recursive --new-file v2.1.108/linux/include/linux/fb.h linux/include/linux/fb.h
--- v2.1.108/linux/include/linux/fb.h	Wed Jun 24 22:54:11 1998
+++ linux/include/linux/fb.h	Fri Jul 10 15:18:32 1998
@@ -30,8 +30,11 @@
 #define FB_TYPE_PACKED_PIXELS		0	/* Packed Pixels	*/
 #define FB_TYPE_PLANES			1	/* Non interleaved planes */
 #define FB_TYPE_INTERLEAVED_PLANES	2	/* Interleaved planes	*/
-#define FB_TYPE_VGA_TEXT		3	/* VGA text/attributes	*/
-#define FB_TYPE_S3_MMIO_TEXT		4	/* S3 MMIO text		*/
+#define FB_TYPE_TEXT			3	/* Text/attributes	*/
+
+#define FB_AUX_TEXT_MDA		0	/* Monochrome text */
+#define FB_AUX_TEXT_CGA		1	/* CGA/EGA/VGA Color text */
+#define FB_AUX_TEXT_S3_MMIO	2	/* S3 MMIO fasttext */
 
 #define FB_VISUAL_MONO01		0	/* Monochr. 1=Black 0=White */
 #define FB_VISUAL_MONO10		1	/* Monochr. 1=White 0=Black */
@@ -51,6 +54,9 @@
 #define FB_ACCEL_ATI_MACH64CT	8	/* ATI Mach 64CT family		*/
 #define FB_ACCEL_ATI_MACH64VT	9	/* ATI Mach 64CT family VT class */
 #define FB_ACCEL_ATI_MACH64GT	10	/* ATI Mach 64CT family GT class */
+#define FB_ACCEL_SUN_CREATOR	11	/* Sun Creator/Creator3D	*/
+#define FB_ACCEL_SUN_CGSIX	12	/* Sun cg6			*/
+#define FB_ACCEL_SUN_LEO	13	/* Sun leo/zx			*/
 
 struct fb_fix_screeninfo {
 	char id[16];			/* identification string eg "TT Builtin" */
@@ -181,7 +187,8 @@
 
 struct fb_info;
 struct fb_info_gen;
-
+struct vm_area_struct;
+struct file;
 
     /*
      *  Frame buffer operations
@@ -189,8 +196,8 @@
 
 struct fb_ops {
     /* open/release and usage marking */
-    int (*fb_open)(struct fb_info *info);
-    int (*fb_release)(struct fb_info *info);
+    int (*fb_open)(struct fb_info *info, int user);
+    int (*fb_release)(struct fb_info *info, int user);
     /* get non settable parameters */
     int (*fb_get_fix)(struct fb_fix_screeninfo *fix, int con,
 		      struct fb_info *info); 
@@ -212,6 +219,8 @@
     /* perform fb specific ioctl */
     int (*fb_ioctl)(struct inode *inode, struct file *file, unsigned int cmd,
 		    unsigned long arg, int con, struct fb_info *info);
+    /* perform fb specific mmap */
+    int (*fb_mmap)(struct fb_info *info, struct file *file, struct vm_area_struct *vma);
 };
 
 
@@ -329,13 +338,9 @@
 };
 
 
-/* prototypes */
-typedef unsigned long fb_init_func(unsigned long);
-
 /* drivers/char/fbmem.c */
 extern int register_framebuffer(struct fb_info *fb_info);
 extern int unregister_framebuffer(const struct fb_info *fb_info);
-extern unsigned long probe_framebuffers(unsigned long kmem_start);
 extern int fbmon_valid_timings(u_int pixclock, u_int htotal, u_int vtotal,
 			       const struct fb_info *fb_info);
 extern int fbmon_dpms(const struct fb_info *fb_info);
diff -u --recursive --new-file v2.1.108/linux/include/linux/if_ppp.h linux/include/linux/if_ppp.h
--- v2.1.108/linux/include/linux/if_ppp.h	Wed Apr  1 20:11:54 1998
+++ linux/include/linux/if_ppp.h	Fri Jul 10 14:02:00 1998
@@ -1,4 +1,4 @@
-/*	$Id: if_ppp.h,v 1.2 1998/01/01 15:05:12 phil Exp $	*/
+/*	$Id: if_ppp.h,v 1.14 1998/07/07 04:27:33 paulus Exp $	*/
 
 /*
  * if_ppp.h - Point-to-Point Protocol definitions.
@@ -21,7 +21,7 @@
  */
 
 /*
- *  ==FILEVERSION 971223==
+ *  ==FILEVERSION 980704==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the above date.
@@ -76,7 +76,7 @@
 #define	SC_MASK		0x0f0000ff	/* bits that user can change */
 
 /* state bits */
-#define SC_XMIT_BUSY	0x10000000	/* ppp_write_wakeup is active */
+#define SC_XMIT_BUSY	0x10000000	/* (used by isdn_ppp?) */
 #define SC_RCV_ODDP	0x08000000	/* have rcvd char with odd parity */
 #define SC_RCV_EVNP	0x04000000	/* have rcvd char with even parity */
 #define SC_RCV_B7_1	0x02000000	/* have rcvd char with bit 7 = 1 */
@@ -89,8 +89,8 @@
  */
 
 struct npioctl {
-    int		protocol;	/* PPP protocol, e.g. PPP_IP */
-    enum NPmode	mode;
+	int		protocol;	/* PPP protocol, e.g. PPP_IP */
+	enum NPmode	mode;
 };
 
 /* Structure describing a CCP configuration option, for PPPIOCSCOMPRESS */
@@ -101,13 +101,13 @@
 };
 
 struct ifpppstatsreq {
-  struct ifreq	   b;
-  struct ppp_stats stats;			/* statistic information */
+	struct ifreq	 b;
+	struct ppp_stats stats;			/* statistic information */
 };
 
 struct ifpppcstatsreq {
-  struct ifreq		b;
-  struct ppp_comp_stats stats;
+	struct ifreq	      b;
+	struct ppp_comp_stats stats;
 };
 
 #define ifr__name       b.ifr_ifrn.ifrn_name
@@ -138,7 +138,7 @@
 #define PPPIOCGIDLE	_IOR('t', 63, struct ppp_idle) /* get idle time */
 
 #define SIOCGPPPSTATS   (SIOCDEVPRIVATE + 0)
-#define SIOCGPPPVER     (SIOCDEVPRIVATE + 1)  /* NEVER change this!! */
+#define SIOCGPPPVER     (SIOCDEVPRIVATE + 1)	/* NEVER change this!! */
 #define SIOCGPPPCSTATS  (SIOCDEVPRIVATE + 2)
 
 #if !defined(ifr_mtu)
diff -u --recursive --new-file v2.1.108/linux/include/linux/if_pppvar.h linux/include/linux/if_pppvar.h
--- v2.1.108/linux/include/linux/if_pppvar.h	Tue Dec 23 10:57:31 1997
+++ linux/include/linux/if_pppvar.h	Fri Jul 10 14:02:00 1998
@@ -42,15 +42,15 @@
  */
 
 /*
- *  ==FILEVERSION 971001==
+ *  ==FILEVERSION 980704==
  *
  *  NOTE TO MAINTAINERS:
- *     If you modify this file at all, please set the above date.
- *     if_pppvar.h 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 if_pppvar.h
- *     file.  Don't change the format of that line otherwise, so the
- *     installation script can recognize it.
+ *   If you modify this file at all, please set the above date.
+ *   if_pppvar.h 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 if_pppvar.h
+ *   file.  Don't change the format of that line otherwise, so the
+ *   installation script can recognize it.
  */
 
 /*
@@ -59,101 +59,74 @@
  */
 
 #define NP_IP	0		/* Internet Protocol */
-#define NUM_NP	1		/* Number of NPs. */
+#define NP_IPX	1		/* IPX protocol */
+#define NP_AT	2		/* Appletalk protocol */
+#define NUM_NP	3		/* Number of NPs. */
 
-/*
- * Buffers for the PPP process have the following structure
- */
-
-#define RBUFSIZE  2048		 /* MUST be a power of 2 and be <= 4095 */
-
-struct ppp_buffer {
-	__s32		size;		/* Size of the buffer area	*/
-        __s32		count;		/* Count of characters in bufr	*/
-        __s32		head;		/* index to head of list	*/
-        __s32		tail;		/* index to tail of list	*/
-        unsigned long	locked;		/* Buffer is being sent		*/
-        __s32		type;		/* Type of the buffer		*/
-					/* =0, device read buffer	*/
-					/* =1, device write buffer	*/
-					/* =2, daemon write buffer	*/
-					/* =3, daemon read buffer	*/
-        __u16		fcs;		/* Frame Check Sequence (CRC)	*/
-        __u16		magic;		/* Extra space if needed	*/
-};
-
-/* Given a pointer to the ppp_buffer then return base address of buffer */
-#define buf_base(buf) ((__u8 *) (&buf[1]))
+#define OBUFSIZE	256	/* # chars of output buffering */
 
 /*
  * Structure describing each ppp unit.
  */
 
 struct ppp {
-	__s32		magic;		/* magic value for structure	*/
+	int		magic;		/* magic value for structure	*/
 	struct ppp	*next;		/* unit with next index		*/
-
-	/* Bitmapped flag fields. */
 	unsigned long	inuse;		/* are we allocated?		*/
-	__u8		escape;		/* 0x20 if prev char was PPP_ESC*/
-	__u8		toss;		/* toss this frame		*/
-
-	__u32		flags;		/* miscellany			*/
+	int		line;		/* network interface unit #	*/
+	__u32		flags;		/* miscellaneous control flags	*/
+	int		mtu;		/* maximum xmit frame size	*/
+	int		mru;		/* maximum receive frame size	*/
+	struct slcompress *slcomp;	/* for TCP header compression	*/
+	struct sk_buff_head xmt_q;	/* frames to send from pppd	*/
+	struct sk_buff_head rcv_q;	/* frames for pppd to read	*/
 
+	/* Information specific to using ppp on async serial lines. */
+	struct tty_struct *tty;		/* ptr to TTY structure	*/
+	struct tty_struct *backup_tty;	/* TTY to use if tty gets closed */
+	__u8		escape;		/* 0x20 if prev char was PPP_ESC */
+	__u8		toss;		/* toss this frame		*/
+	volatile __u8	tty_pushing;	/* internal state flag		*/
 	__u32		xmit_async_map[8]; /* 1 bit means that given control 
 					   character is quoted on output*/
-
 	__u32		recv_async_map; /* 1 bit means that given control 
 					   character is ignored on input*/
-	__s32		mtu;		/* maximum xmit frame size	*/
-	__s32		mru;		/* maximum receive frame size	*/
+	__u32		bytes_sent;	/* Bytes sent on frame	*/
+	__u32		bytes_rcvd;	/* Bytes recvd on frame	*/
 
-	/* Information about the current tty data */
-	__s32		line;		/* PPP channel number	*/
-	struct tty_struct *tty;		/* ptr to TTY structure	*/
-	struct tty_struct *backup_tty;	/* TTY to use if tty gets closed */
-	__s32		bytes_sent;	/* Bytes sent on frame	*/
-	__s32		bytes_rcvd;	/* Bytes recvd on frame	*/
+	/* Async transmission information */
+	struct sk_buff	*tpkt;		/* frame currently being sent	*/
+	int		tpkt_pos;	/* how much of it we've done	*/
+	__u16		tfcs;		/* FCS so far for it		*/
+	unsigned char	*optr;		/* where we're up to in sending */
+	unsigned char	*olim;		/* points past last valid char	*/
+
+	/* Async reception information */
+	struct sk_buff	*rpkt;		/* frame currently being rcvd	*/
+	__u16		rfcs;		/* FCS so far of rpkt		*/
 
-	/* VJ Header compression data */
-	struct slcompress *slcomp;	/* for header compression	*/
-
-	/* Transmission information */
-	struct ppp_buffer *xbuf;	/* Buffer currently being sent	*/
-	struct ppp_buffer *s1buf;	/* Pointer to daemon buffer	*/
-	struct ppp_buffer *s2buf;	/* Pointer to device buffer	*/
+	/* Queues for select() functionality */
+	struct wait_queue *read_wait;	/* queue for reading processes	*/
 
+	/* info for detecting idle channels */
 	unsigned long	last_xmit;	/* time of last transmission	*/
 	unsigned long	last_recv;	/* time last packet received    */
 
-  /* These are pointers to the malloc()ed frame buffers.
-     These buffers are used while processing a packet.	If a packet
-     has to hang around for the user process to read it, it lingers in
-     the user buffers below. */
-
-	struct ppp_buffer *wbuf;	  /* Transmission information	*/
-	struct ppp_buffer *tbuf;	  /* daemon transmission buffer	*/
-	struct ppp_buffer *rbuf;	  /* Receive information	*/
-	struct ppp_buffer *ubuf;	  /* User buffer information	*/
-	struct ppp_buffer *cbuf;	  /* compression buffer		*/
-
-	/* Queues for select() functionality */
-	struct wait_queue *write_wait;	  /* queue for reading processes */
-	struct wait_queue *read_wait;	  /* queue for writing processes */
-
 	/* Statistic information */
-	struct pppstat	stats;		  /* statistic information	*/
+	struct pppstat	stats;		/* statistic information	*/
 
 	/* PPP compression protocol information */
-	__u32	sc_bytessent;		  /* count of octets sent */
-	__u32	sc_bytesrcvd;		  /* count of octets received */
+	struct	compressor *sc_xcomp;	/* transmit compressor */
+	void	*sc_xc_state;		/* transmit compressor state */
+	struct	compressor *sc_rcomp;	/* receive decompressor */
+	void	*sc_rc_state;		/* receive decompressor state */
+
 	enum	NPmode sc_npmode[NUM_NP]; /* what to do with each NP */
-	struct	compressor *sc_xcomp;	  /* transmit compressor */
-	void	*sc_xc_state;		  /* transmit compressor state */
-	struct	compressor *sc_rcomp;	  /* receive decompressor */
-	void	*sc_rc_state;		  /* receive decompressor state */
-	__s32	 sc_xfer;		  /* PID of reserved PPP table */
-	char	name[8];
+	int	 sc_xfer;		/* PID of reserved PPP table */
+	char	name[8];		/* space for unit name */
 	struct device	dev;		/* net device structure */
 	struct enet_statistics estats;	/* more detailed stats */
+
+	/* tty output buffer */
+	unsigned char	obuf[OBUFSIZE];	/* buffer for characters to send */
 };
diff -u --recursive --new-file v2.1.108/linux/include/linux/if_tr.h linux/include/linux/if_tr.h
--- v2.1.108/linux/include/linux/if_tr.h	Sat Nov 29 10:33:21 1997
+++ linux/include/linux/if_tr.h	Fri Jul 10 13:51:41 1998
@@ -59,29 +59,31 @@
 
 /* Token-Ring statistics collection data. */
 struct tr_statistics {
-	int rx_packets;			/* total packets received	*/
-	int tx_packets;			/* total packets transmitted	*/
-	int rx_errors;			/* bad packets received		*/
-	int tx_errors;			/* packet transmit problems	*/
-	int rx_dropped;			/* no space in linux buffers	*/
-	int tx_dropped;			/* no space available in linux	*/
-	int multicast;			/* multicast packets received	*/
-	int transmit_collision;
+	unsigned long rx_packets;       /* total packets received	*/
+	unsigned long tx_packets;	/* total packets transmitted	*/
+	unsigned long rx_bytes;		/* total bytes received   	*/
+	unsigned long tx_bytes;		/* total bytes transmitted	*/
+	unsigned long rx_errors;	/* bad packets received		*/
+	unsigned long tx_errors;	/* packet transmit problems	*/
+	unsigned long rx_dropped;	/* no space in linux buffers	*/
+	unsigned long tx_dropped;	/* no space available in linux	*/
+	unsigned long multicast;	/* multicast packets received	*/
+	unsigned long transmit_collision;
 
 	/* detailed Token-Ring errors. See IBM Token-Ring Network
 	   Architecture for more info */
 
-	int line_errors;
-	int internal_errors;
-	int burst_errors;
-	int A_C_errors;
-	int abort_delimiters;
-	int lost_frames;
-	int recv_congest_count;
-	int frame_copied_errors;
-	int frequency_errors;
-	int token_errors;
-	int dummy1;
+	unsigned long line_errors;
+	unsigned long internal_errors;
+	unsigned long burst_errors;
+	unsigned long A_C_errors;
+	unsigned long abort_delimiters;
+	unsigned long lost_frames;
+	unsigned long recv_congest_count;
+	unsigned long frame_copied_errors;
+	unsigned long frequency_errors;
+	unsigned long token_errors;
+	unsigned long dummy1;
 };
 
 /* source routing stuff */
@@ -93,5 +95,6 @@
 #define TR_RCF_LIMITED_BROADCAST 0xC000 /* single-route broadcast */
 #define TR_RCF_FRAME2K 0x20
 #define TR_RCF_BROADCAST_MASK 0xC000
+#define TR_MAXRIFLEN 18
 
 #endif	/* _LINUX_IF_TR_H */
diff -u --recursive --new-file v2.1.108/linux/include/linux/ipx.h linux/include/linux/ipx.h
--- v2.1.108/linux/include/linux/ipx.h	Sun Jun  7 11:16:39 1998
+++ linux/include/linux/ipx.h	Thu Jul 16 15:11:54 1998
@@ -41,7 +41,7 @@
 #define IPX_FRAME_8022		2
 #define IPX_FRAME_ETHERII	3
 #define IPX_FRAME_8023		4
-#define IPX_FRAME_TR_8022	5
+#define IPX_FRAME_TR_8022       5 /* obsolete */
 	unsigned char ipx_special;
 #define IPX_SPECIAL_NONE	0
 #define IPX_PRIMARY		1
diff -u --recursive --new-file v2.1.108/linux/include/linux/kbd_kern.h linux/include/linux/kbd_kern.h
--- v2.1.108/linux/include/linux/kbd_kern.h	Fri Feb  6 15:33:19 1998
+++ linux/include/linux/kbd_kern.h	Fri Jul 10 15:18:32 1998
@@ -138,4 +138,17 @@
 
 #define U(x) ((x) ^ 0xf000)
 
+/* keyboard.c */
+
+struct console;
+
+int getkeycode(unsigned int scancode);
+int setkeycode(unsigned int scancode, unsigned int keycode);
+void compute_shiftstate(void);
+int keyboard_wait_for_keypress(struct console *);
+
+/* defkeymap.c */
+
+extern unsigned int keymap_count;
+
 #endif
diff -u --recursive --new-file v2.1.108/linux/include/linux/linux_logo.h linux/include/linux/linux_logo.h
--- v2.1.108/linux/include/linux/linux_logo.h	Wed Jun 24 22:54:11 1998
+++ linux/include/linux/linux_logo.h	Fri Jul 10 15:18:32 1998
@@ -1,4 +1,4 @@
-/* $Id: linux_logo.h,v 1.1 1998/05/04 14:21:03 jj Exp $
+/* $Id: linux_logo.h,v 1.3 1998/07/06 15:51:16 jj Exp $
  * include/linux/linux_logo.h: This is a linux logo
  *                             to be displayed on boot.
  *
diff -u --recursive --new-file v2.1.108/linux/include/linux/mm.h linux/include/linux/mm.h
--- v2.1.108/linux/include/linux/mm.h	Wed Jul  1 19:38:57 1998
+++ linux/include/linux/mm.h	Thu Jul 16 15:11:07 1998
@@ -254,21 +254,9 @@
 /* memory.c & swap.c*/
 
 /*
- * This traverses "nr" memory size lists,
- * and returns true if there is enough memory.
- *
- * For example, we want to keep on waking up
- * kswapd every once in a while until the highest
- * memory order has an entry (ie nr == 0), but
- * we want to do it in the background.
- *
- * We want to do it in the foreground only if
- * none of the three highest lists have enough
- * memory. Random number.
+ * Decide if we should try to do some swapout..
  */
-extern int free_memory_available(int nr);
-#define kswapd_continue()	(!free_memory_available(3))
-#define kswapd_wakeup()		(!free_memory_available(0))
+extern int free_memory_available(void);
 
 #define free_page(addr) free_pages((addr),0)
 extern void FASTCALL(free_pages(unsigned long addr, unsigned long order));
@@ -330,8 +318,8 @@
 #define GFP_BUFFER	(__GFP_LOW | __GFP_WAIT)
 #define GFP_ATOMIC	(__GFP_HIGH)
 #define GFP_USER	(__GFP_LOW | __GFP_WAIT | __GFP_IO)
-#define GFP_KERNEL	(__GFP_LOW | __GFP_WAIT | __GFP_IO)
-#define GFP_NFS		(__GFP_MED | __GFP_WAIT | __GFP_IO)
+#define GFP_KERNEL	(__GFP_MED | __GFP_WAIT | __GFP_IO)
+#define GFP_NFS		(__GFP_HIGH | __GFP_WAIT | __GFP_IO)
 
 /* Flag - indicates that the buffer will be suitable for DMA.  Ignored on some
    platforms, used as appropriate on others */
diff -u --recursive --new-file v2.1.108/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.1.108/linux/include/linux/pci.h	Wed Jun 24 22:54:11 1998
+++ linux/include/linux/pci.h	Fri Jul 10 10:48:37 1998
@@ -839,10 +839,15 @@
 #define PCI_DEVICE_ID_COMPEX_RL2000	0x1401
 
 #define PCI_VENDOR_ID_RP               0x11fe
-#define PCI_DEVICE_ID_RP8OCTA          0x0001
+#define PCI_DEVICE_ID_RP32INTF         0x0001
 #define PCI_DEVICE_ID_RP8INTF          0x0002
 #define PCI_DEVICE_ID_RP16INTF         0x0003
-#define PCI_DEVICE_ID_RP32INTF         0x0004
+#define PCI_DEVICE_ID_RP4QUAD	       0x0004
+#define PCI_DEVICE_ID_RP8OCTA          0x0005
+#define PCI_DEVICE_ID_RP8J	       0x0006
+#define PCI_DEVICE_ID_RPP4	       0x000A
+#define PCI_DEVICE_ID_RPP8	       0x000B
+#define PCI_DEVICE_ID_RP8M	       0x000C
 
 #define PCI_VENDOR_ID_CYCLADES		0x120e
 #define PCI_DEVICE_ID_CYCLOM_Y_Lo	0x0100
diff -u --recursive --new-file v2.1.108/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.108/linux/include/linux/proc_fs.h	Tue Jun 23 10:01:29 1998
+++ linux/include/linux/proc_fs.h	Thu Jul 16 15:11:13 1998
@@ -281,6 +281,7 @@
 				off_t offset, int length, int inout);
 
 extern struct proc_dir_entry proc_root;
+extern struct proc_dir_entry proc_root_fs;
 extern struct proc_dir_entry *proc_net;
 extern struct proc_dir_entry *proc_scsi;
 extern struct proc_dir_entry proc_sys;
diff -u --recursive --new-file v2.1.108/linux/include/linux/selection.h linux/include/linux/selection.h
--- v2.1.108/linux/include/linux/selection.h	Wed Jul  1 19:38:57 1998
+++ linux/include/linux/selection.h	Thu Jul 16 15:11:40 1998
@@ -4,6 +4,11 @@
  * Interface between console.c, tty_io.c, vt.c, vc_screen.c and selection.c
  */
 
+#ifndef _LINUX_SELECTION_H_
+#define _LINUX_SELECTION_H_
+
+#include <linux/vt_buffer.h>
+
 extern int sel_cons;
 
 extern void clear_selection(void);
@@ -21,22 +26,11 @@
 
 extern int console_blanked;
 
-extern unsigned long video_font_height;
-extern unsigned long video_scan_lines;
-extern unsigned long default_font_height;
-extern int video_font_is_default;
-
 extern unsigned char color_table[];
 extern int default_red[];
 extern int default_grn[];
 extern int default_blu[];
 
-extern unsigned short __real_origin;
-extern unsigned short __origin;
-extern unsigned char has_wrapped;
-
-extern unsigned short *vc_scrbuf[MAX_NR_CONSOLES];
-
 extern void do_unblank_screen(void);
 extern unsigned short *screen_pos(int currcons, int w_offset, int viewed);
 extern unsigned short screen_word(int currcons, int offset, int viewed);
@@ -44,48 +38,7 @@
 extern void complement_pos(int currcons, int offset);
 extern void invert_screen(int currcons, int offset, int count, int shift);
 
-#define reverse_video_char(a)	(((a) & 0x88) | ((((a) >> 4) | ((a) << 4)) & 0x77))
-#define reverse_video_short(a)	(((a) & 0x88ff) | \
-	(((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4))
-/* this latter line used to have masks 0xf000 and 0x0f00, but selection
-   requires a self-inverse operation; moreover, the old version looks wrong */
-#define reverse_video_short_mono(a)	((a) ^ 0x800)
-#define complement_video_short(a)	((a) ^ (can_do_color ? 0x7700 : 0x800))
-
 extern void getconsxy(int currcons, char *p);
 extern void putconsxy(int currcons, char *p);
 
-
-/* how to access screen memory */
-
-static inline void scr_writew(unsigned short val, unsigned short *addr)
-{
-	/* simply store the value in the "shadow screen" memory */
-	*addr = val;
-}
-
-static inline unsigned short scr_readw(unsigned short * addr)
-{
-	return *addr;
-}
-
-static inline void memsetw(void * s, unsigned short c, unsigned int count)
-{
-	unsigned short * addr = (unsigned short *) s;
-
-	count /= 2;
-	while (count) {
-		count--;
-		scr_writew(c, addr++);
-	}
-}
-
-static inline void memcpyw(unsigned short *to, unsigned short *from,
-			   unsigned int count)
-{
-	count /= 2;
-	while (count) {
-		count--;
-		scr_writew(scr_readw(from++), to++);
-	}
-}
+#endif
diff -u --recursive --new-file v2.1.108/linux/include/linux/soundcard.h linux/include/linux/soundcard.h
--- v2.1.108/linux/include/linux/soundcard.h	Sun Jun  7 11:16:39 1998
+++ linux/include/linux/soundcard.h	Fri Jul 10 14:03:36 1998
@@ -69,7 +69,7 @@
 #define SNDCARD_PSEUDO_MSS	24
 #define SNDCARD_GUSPNP		25
 #define SNDCARD_UART401		26
-/* Soundcard numbers 27 to N are reserved. Don't add more numbers here */
+/* Sound card numbers 27 to N are reserved. Don't add more numbers here. */
 
 /***********************************
  * IOCTL Commands for /dev/sequencer
diff -u --recursive --new-file v2.1.108/linux/include/linux/swap.h linux/include/linux/swap.h
--- v2.1.108/linux/include/linux/swap.h	Tue Jun 23 10:01:29 1998
+++ linux/include/linux/swap.h	Mon Jul 13 12:35:50 1998
@@ -50,7 +50,7 @@
 extern int shm_swap (int, int);
 
 /* linux/mm/vmscan.c */
-extern int try_to_free_page(int);
+extern int try_to_free_pages(unsigned int gfp_mask, int count);
 
 /* linux/mm/page_io.c */
 extern void rw_swap_page(int, unsigned long, char *, int);
@@ -126,21 +126,6 @@
 	if (PageFreeAfter(page))
 		count--;
 	return (count > 1);
-}
-
-/*
- * When we're freeing pages from a user application, we want
- * to cluster swapouts too.	-- Rik.
- * linux/mm/page_alloc.c
- */
-static inline int try_to_free_pages(int gfp_mask, int count)
-{
-	int retval = 0;
-	while (count--) {
-		if (try_to_free_page(gfp_mask))
-			retval = 1;
-	}
-	return retval;
 }
 
 /*
diff -u --recursive --new-file v2.1.108/linux/include/linux/trdevice.h linux/include/linux/trdevice.h
--- v2.1.108/linux/include/linux/trdevice.h	Sat Nov 29 10:51:44 1997
+++ linux/include/linux/trdevice.h	Fri Jul 10 13:51:41 1998
@@ -33,7 +33,6 @@
 				   void *saddr, unsigned len);
 extern int		tr_rebuild_header(struct sk_buff *skb);
 extern unsigned short	tr_type_trans(struct sk_buff *skb, struct device *dev);
-extern void             tr_reformat(struct sk_buff *, unsigned int);
 extern struct device    * init_trdev(struct device *, int);
 
 #endif
diff -u --recursive --new-file v2.1.108/linux/include/linux/vt_buffer.h linux/include/linux/vt_buffer.h
--- v2.1.108/linux/include/linux/vt_buffer.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/vt_buffer.h	Thu Jul 16 15:11:40 1998
@@ -0,0 +1,77 @@
+/*
+ *	include/linux/vt_buffer.h -- Access to VT screen buffer
+ *
+ *	(c) 1998 Martin Mares <mj@ucw.cz>
+ *
+ *	This is a set of macros and functions which are used in the
+ *	console driver and related code to access the screen buffer.
+ *	In most cases the console works with simple in-memory buffer,
+ *	but when handling hardware text mode consoles, we store
+ *	the foreground console directly in video memory.
+ */
+
+#ifndef _LINUX_VT_BUFFER_H_
+#define _LINUX_VT_BUFFER_H_
+
+#include <linux/config.h>
+
+#ifdef CONFIG_VGA_CONSOLE
+#if !defined(CONFIG_FB) && !defined(CONFIG_FB_MODULE)
+#define VT_BUF_VRAM_ONLY
+#endif
+#include <asm/vga.h>
+#endif
+
+#ifndef VT_BUF_HAVE_RW
+#define scr_writew(val, addr) (*(addr) = (val))
+#define scr_readw(addr) (*(addr))
+#define scr_memcpyw(d, s, c) memcpy(d, s, c)
+#define VT_BUF_HAVE_MEMCPYW
+#define scr_memcpyw_from(d, s, c) memcpy(d, s, c)
+#define scr_memcpyw_to(d, s, c) memcpy(d, s, c)
+#define VT_BUF_HAVE_MEMCPYF
+#endif
+
+#ifndef VT_BUF_HAVE_MEMSETW
+extern inline void scr_memsetw(u16 *s, u16 c, unsigned int count)
+{
+	count /= 2;
+	while (count--)
+		scr_writew(c, s++);
+}
+#endif
+
+#ifndef VT_BUF_HAVE_MEMCPYW
+extern inline void scr_memcpyw(u16 *d, u16 *s, unsigned int count)
+{
+	count /= 2;
+	while (count--)
+		scr_writew(scr_readw(s++), d++);
+}
+#endif
+
+#ifndef VT_BUF_HAVE_MEMCPYF
+extern inline void scr_memcpyw_from(u16 *d, u16 *s, unsigned int count)
+{
+	count /= 2;
+	while (count--)
+		*d++ = scr_readw(s++);
+}
+
+extern inline void scr_memcpyw_to(u16 *d, u16 *s, unsigned int count)
+{
+	count /= 2;
+	while (count--)
+		scr_writew(*s++, d++);
+}
+#endif
+
+#define reverse_video_char(a)	(((a) & 0x88) | ((((a) >> 4) | ((a) << 4)) & 0x77))
+#define reverse_video_short(a)	(((a) & 0x88ff) | \
+	(((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4))
+/* this latter line used to have masks 0xf000 and 0x0f00, but selection
+   requires a self-inverse operation; moreover, the old version looks wrong */
+#define reverse_video_short_mono(a)	((a) ^ 0x800)
+#define complement_video_short(a)	((a) ^ (can_do_color ? 0x7700 : 0x800))
+
+#endif
diff -u --recursive --new-file v2.1.108/linux/include/linux/vt_kern.h linux/include/linux/vt_kern.h
--- v2.1.108/linux/include/linux/vt_kern.h	Wed Jun 24 22:54:12 1998
+++ linux/include/linux/vt_kern.h	Mon Jul 13 12:43:04 1998
@@ -6,6 +6,7 @@
  * with information needed by the vt package
  */
 
+#include <linux/config.h>
 #include <linux/vt.h>
 
 /*
@@ -21,9 +22,11 @@
 extern struct vt_struct {
 	int vc_num;				/* The console number */
 	unsigned char	vc_mode;		/* KD_TEXT, ... */
+#if 0	/* FIXME: Does anyone use these? */
 	unsigned char	vc_kbdraw;
 	unsigned char	vc_kbde0;
 	unsigned char   vc_kbdleds;
+#endif
 	struct vt_mode	vt_mode;
 	int		vt_pid;
 	int		vt_newvt;
@@ -31,11 +34,67 @@
 } *vt_cons[MAX_NR_CONSOLES];
 
 void (*kd_mksound)(unsigned int hz, unsigned int ticks);
-int vc_allocate(unsigned int console);
+
+/* console.c */
+
+int vc_allocate(unsigned int console, int init);
 int vc_cons_allocated(unsigned int console);
-int vc_resize(unsigned long lines, unsigned long cols);
-void vc_resize_con(unsigned long lines, unsigned long cols,
-		   unsigned int currcons);
+int vc_resize(unsigned int lines, unsigned int cols,
+	      unsigned int first, unsigned int last);
+#define vc_resize_all(l, c) vc_resize(l, c, 0, MAX_NR_CONSOLES-1)
+#define vc_resize_con(l, c, x) vc_resize(l, c, x, x)
 void vc_disallocate(unsigned int console);
+void poke_blanked_console(void);
+void set_vesa_blanking(unsigned long arg);
+void vesa_blank(void);
+void vesa_powerdown(void);
+void reset_palette(int currcons);
+void set_palette(void);
+void do_blank_screen(int nopowersave);
+int con_set_font(char * fontmap, int w, int h, int chars);
+int con_get_font(char * fontmap, int *w, int *h, int *chars);
+int con_set_cmap(unsigned char *cmap);
+int con_get_cmap(unsigned char *cmap);
+void scrollback(int);
+void scrollfront(int);
+
+struct tty_struct;
+int tioclinux(struct tty_struct *tty, unsigned long arg);
+
+#if defined(CONFIG_PMAC_CONSOLE) || defined(CONFIG_FB_COMPAT_XPMAC)
+#include <asm/vc_ioctl.h>
+extern int console_getmode(struct vc_mode *);
+extern int console_setmode(struct vc_mode *, int);
+extern int console_setcmap(int, unsigned char *,
+			   unsigned char *, unsigned char *);
+extern int console_powermode(int);
+#endif
+
+/* consolemap.c */
+
+struct unimapinit;
+struct unipair;
+
+int con_set_trans_old(unsigned char * table);
+int con_get_trans_old(unsigned char * table);
+int con_set_trans_new(unsigned short * table);
+int con_get_trans_new(unsigned short * table);
+void con_clear_unimap(struct unimapinit *ui);
+int con_set_unimap(ushort ct, struct unipair *list);
+int con_get_unimap(ushort ct, ushort *uct, struct unipair *list);
+void con_set_default_unimap(void);
+
+/* vt.c */
+
+extern unsigned int video_mode_512ch;
+extern unsigned int video_font_height;
+extern unsigned int default_font_height;
+extern unsigned int video_scan_lines;
+
+void complete_change_console(unsigned int new_console);
+int vt_waitactive(int vt);
+void change_console(unsigned int);
+void reset_vc(unsigned int new_console);
+int vt_waitactive(int vt);
 
 #endif /* _VT_KERN_H */
diff -u --recursive --new-file v2.1.108/linux/include/net/p8022tr.h linux/include/net/p8022tr.h
--- v2.1.108/linux/include/net/p8022tr.h	Sat Mar 30 03:20:33 1996
+++ linux/include/net/p8022tr.h	Wed Dec 31 16:00:00 1969
@@ -1,8 +0,0 @@
-#ifndef _NET_P8022TR_H
-#define _NET_P8022TR_H
-
-extern struct datalink_proto *register_8022tr_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *));
-extern void unregister_8022tr_client(unsigned char type);
-
-#endif
-
diff -u --recursive --new-file v2.1.108/linux/include/net/p8022trcall.h linux/include/net/p8022trcall.h
--- v2.1.108/linux/include/net/p8022trcall.h	Sat Mar 30 03:20:33 1996
+++ linux/include/net/p8022trcall.h	Wed Dec 31 16:00:00 1969
@@ -1,3 +0,0 @@
-/* Separate to keep compilation of Space.c simpler */
-extern void p8022tr_proto_init(struct net_proto *);
-
diff -u --recursive --new-file v2.1.108/linux/kernel/fork.c linux/kernel/fork.c
--- v2.1.108/linux/kernel/fork.c	Wed Jun 24 22:54:12 1998
+++ linux/kernel/fork.c	Tue Jul  7 10:37:07 1998
@@ -308,6 +308,10 @@
 
 	if (clone_flags & CLONE_VM) {
 		mmget(current->mm);
+		/*
+		 * Set up the LDT descriptor for the clone task.
+		 */
+		copy_segments(nr, tsk, NULL);
 		SET_PAGE_DIR(tsk, current->mm->pgd);
 		return 0;
 	}
diff -u --recursive --new-file v2.1.108/linux/kernel/kmod.c linux/kernel/kmod.c
--- v2.1.108/linux/kernel/kmod.c	Wed Jun 24 22:54:12 1998
+++ linux/kernel/kmod.c	Tue Jul 14 10:24:37 1998
@@ -21,7 +21,7 @@
 	modprobe_path is set via /proc/sys.
 */
 char modprobe_path[256] = "/sbin/modprobe";
-static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL };
+static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
 
 /*
 	exec_modprobe is spawned from a kernel-mode user process,
diff -u --recursive --new-file v2.1.108/linux/kernel/printk.c linux/kernel/printk.c
--- v2.1.108/linux/kernel/printk.c	Thu May  7 22:51:55 1998
+++ linux/kernel/printk.c	Tue Jul  7 16:11:10 1998
@@ -14,8 +14,6 @@
 
 #include <stdarg.h>
 
-#include <asm/system.h>
-
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -27,6 +25,7 @@
 #include <linux/console.h>
 #include <linux/init.h>
 
+#include <asm/system.h>
 #include <asm/uaccess.h>
 
 #define LOG_BUF_LEN	8192
diff -u --recursive --new-file v2.1.108/linux/kernel/sched.c linux/kernel/sched.c
--- v2.1.108/linux/kernel/sched.c	Wed Jun 24 22:54:12 1998
+++ linux/kernel/sched.c	Fri Jul 10 14:05:14 1998
@@ -1532,7 +1532,7 @@
 		printk(stat_nam[p->state]);
 	else
 		printk(" ");
-#if ((~0UL) == 0xffffffff)
+#if (BITS_PER_LONG == 32)
 	if (p == current)
 		printk(" current  ");
 	else
@@ -1595,7 +1595,7 @@
 {
 	struct task_struct *p;
 
-#if ((~0UL) == 0xffffffff)
+#if (BITS_PER_LONG == 32)
 	printk("\n"
 	       "                         free                        sibling\n");
 	printk("  task             PC    stack   pid father child younger older\n");
diff -u --recursive --new-file v2.1.108/linux/mm/filemap.c linux/mm/filemap.c
--- v2.1.108/linux/mm/filemap.c	Wed Jul  1 19:38:57 1998
+++ linux/mm/filemap.c	Wed Jul 15 16:35:31 1998
@@ -125,8 +125,8 @@
 	struct buffer_head *tmp, *bh;
 	int count_max, count_min;
 
-	count_max = (limit<<1) >> (priority>>1);
-	count_min = (limit<<1) >> (priority);
+	count_max = (limit<<2) >> (priority>>1);
+	count_min = (limit<<2) >> (priority);
 
 	page = mem_map + clock;
 	do {
@@ -851,7 +851,7 @@
 	return written;
 }
 
-asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, size_t count)
+asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
 {
 	ssize_t retval;
 	struct file * in_file, * out_file;
@@ -900,16 +900,27 @@
 	retval = 0;
 	if (count) {
 		read_descriptor_t desc;
+		loff_t pos = 0, *ppos;
+
+		retval = -EFAULT;
+		ppos = &in_file->f_pos;
+		if (offset) {
+			if (get_user(pos, offset))
+				goto fput_out;
+			ppos = &pos;
+		}
 
 		desc.written = 0;
 		desc.count = count;
 		desc.buf = (char *) out_file;
 		desc.error = 0;
-		do_generic_file_read(in_file, &in_file->f_pos, &desc, file_send_actor);
+		do_generic_file_read(in_file, ppos, &desc, file_send_actor);
 
 		retval = desc.written;
 		if (!retval)
 			retval = desc.error;
+		if (offset)
+			put_user(pos, offset);
 	}
 
 
diff -u --recursive --new-file v2.1.108/linux/mm/mmap.c linux/mm/mmap.c
--- v2.1.108/linux/mm/mmap.c	Wed Jul  1 19:38:57 1998
+++ linux/mm/mmap.c	Thu Jul 16 11:07:20 1998
@@ -57,19 +57,19 @@
 	 * simple, it hopefully works in most obvious cases.. Easy to
 	 * fool it, but this should catch most mistakes.
 	 */
-	long freepages;
+	long free;
 	
         /* Sometimes we want to use more memory than we have. */
 	if (sysctl_overcommit_memory)
 	    return 1;
 
-	freepages = buffermem >> PAGE_SHIFT;
-	freepages += page_cache_size;
-	freepages >>= 1;
-	freepages += nr_free_pages;
-	freepages += nr_swap_pages;
-	freepages -= num_physpages >> 4;
-	return freepages > pages;
+	free = buffermem >> PAGE_SHIFT;
+	free += page_cache_size;
+	free >>= 1;
+	free += nr_free_pages;
+	free += nr_swap_pages;
+	free -= num_physpages >> 4;
+	return free > pages;
 }
 
 /* Remove one vm structure from the inode's i_mmap ring. */
diff -u --recursive --new-file v2.1.108/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.1.108/linux/mm/page_alloc.c	Wed Jun 24 22:54:14 1998
+++ linux/mm/page_alloc.c	Thu Jul 16 14:59:47 1998
@@ -103,48 +103,28 @@
 /*
  * This routine is used by the kernel swap daemon to determine
  * whether we have "enough" free pages. It is fairly arbitrary,
- * but this had better return false if any reasonable "get_free_page()"
- * allocation could currently fail..
+ * having a low-water and high-water mark.
  *
- * This will return zero if no list was found, non-zero
- * if there was memory (the bigger, the better).
+ * This returns:
+ *  0 - urgent need for memory
+ *  1 - need some memory, but do it slowly in the background
+ *  2 - no need to even think about it.
  */
-int free_memory_available(int nr)
+int free_memory_available(void)
 {
-	int retval = 0;
-	unsigned long flags;
-	struct free_area_struct * list;
+	static int available = 1;
 
-	/*
-	 * If we have more than about 3% to 5% of all memory free,
-	 * consider it to be good enough for anything.
-	 * It may not be, due to fragmentation, but we
-	 * don't want to keep on forever trying to find
-	 * free unfragmented memory.
-	 * Added low/high water marks to avoid thrashing -- Rik.
-	 */
-	if (nr_free_pages > (nr ? freepages.low : freepages.high))
-		return nr+1;
+	if (nr_free_pages < freepages.low) {
+		available = 0;
+		return 0;
+	}
 
-	list = free_area + NR_MEM_LISTS;
-	spin_lock_irqsave(&page_alloc_lock, flags);
-	/* We fall through the loop if the list contains one
-	 * item. -- thanks to Colin Plumb <colin@nyx.net>
-	 */
-	do {
-		list--;
-		/* Empty list? Bad - we need more memory */
-		if (list->next == memory_head(list))
-			break;
-		/* One item on the list? Look further */
-		if (list->next->next == memory_head(list))
-			continue;
-		/* More than one item? We're ok */
-		retval = nr + 1;
-		break;
-	} while (--nr >= 0);
-	spin_unlock_irqrestore(&page_alloc_lock, flags);
-	return retval;
+	if (nr_free_pages > freepages.high) {
+		available = 1;
+		return 2;
+	}
+
+	return available;
 }
 
 static inline void free_pages_ok(unsigned long map_nr, unsigned long order)
@@ -217,13 +197,11 @@
 	change_bit((index) >> (1+(order)), (area)->map)
 #define CAN_DMA(x) (PageDMA(x))
 #define ADDRESS(x) (PAGE_OFFSET + ((x) << PAGE_SHIFT))
-#define RMQUEUE(order, maxorder, dma) \
+#define RMQUEUE(order, dma) \
 do { struct free_area_struct * area = free_area+order; \
      unsigned long new_order = order; \
 	do { struct page *prev = memory_head(area), *ret = prev->next; \
 		while (memory_head(area) != ret) { \
-			if (new_order >= maxorder && ret->next == prev) \
-				break; \
 			if (!dma || CAN_DMA(ret)) { \
 				unsigned long map_nr = ret->map_nr; \
 				(prev->next = ret->next)->prev = prev; \
@@ -255,39 +233,36 @@
 
 unsigned long __get_free_pages(int gfp_mask, unsigned long order)
 {
-	unsigned long flags, maxorder;
+	unsigned long flags;
 
 	if (order >= NR_MEM_LISTS)
 		goto nopage;
 
-	/*
-	 * "maxorder" is the highest order number that we're allowed
-	 * to empty in order to find a free page..
-	 */
-	maxorder = NR_MEM_LISTS-1;
-	if (gfp_mask & __GFP_HIGH)
-		maxorder = NR_MEM_LISTS;
-
-	if (in_interrupt() && (gfp_mask & __GFP_WAIT)) {
-		static int count = 0;
-		if (++count < 5) {
-			printk("gfp called nonatomically from interrupt %p\n",
-				__builtin_return_address(0));
-			gfp_mask &= ~__GFP_WAIT;
+	if (gfp_mask & __GFP_WAIT) {
+		if (in_interrupt()) {
+			static int count = 0;
+			if (++count < 5) {
+				printk("gfp called nonatomically from interrupt %p\n",
+					__builtin_return_address(0));
+			}
+			goto nopage;
 		}
-	}
 
-	for (;;) {
-		spin_lock_irqsave(&page_alloc_lock, flags);
-		RMQUEUE(order, maxorder, (gfp_mask & GFP_DMA));
-		spin_unlock_irqrestore(&page_alloc_lock, flags);
-		if (!(gfp_mask & __GFP_WAIT))
-			break;
-		if (!try_to_free_pages(gfp_mask, SWAP_CLUSTER_MAX))
-			break;
-		gfp_mask &= ~__GFP_WAIT;	/* go through this only once */
-		maxorder = NR_MEM_LISTS;	/* Allow anything this time */
+		if (freepages.min > nr_free_pages) {
+			int freed;
+			freed = try_to_free_pages(gfp_mask, SWAP_CLUSTER_MAX);
+			/*
+			 * Low priority (user) allocations must not
+			 * succeed if we didn't have enough memory
+			 * and we couldn't get more..
+			 */
+			if (!freed && !(gfp_mask & (__GFP_MED | __GFP_HIGH)))
+				goto nopage;
+		}
 	}
+	spin_lock_irqsave(&page_alloc_lock, flags);
+	RMQUEUE(order, (gfp_mask & GFP_DMA));
+	spin_unlock_irqrestore(&page_alloc_lock, flags);
 nopage:
 	return 0;
 }
@@ -303,6 +278,11 @@
  	unsigned long total = 0;
 
 	printk("Free pages:      %6dkB\n ( ",nr_free_pages<<(PAGE_SHIFT-10));
+	printk("Free: %d (%d %d %d)\n",
+		nr_free_pages,
+		freepages.min,
+		freepages.low,
+		freepages.high);
 	spin_lock_irqsave(&page_alloc_lock, flags);
  	for (order=0 ; order < NR_MEM_LISTS; order++) {
 		struct page * tmp;
diff -u --recursive --new-file v2.1.108/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.1.108/linux/mm/vmscan.c	Wed Jun 24 22:54:14 1998
+++ linux/mm/vmscan.c	Thu Jul 16 15:03:40 1998
@@ -444,7 +444,7 @@
  * to be.  This works out OK, because we now do proper aging on page
  * contents. 
  */
-static inline int do_try_to_free_page(int gfp_mask)
+static int do_try_to_free_page(int gfp_mask)
 {
 	static int state = 0;
 	int i=6;
@@ -457,9 +457,10 @@
 	stop = 3;
 	if (gfp_mask & __GFP_WAIT)
 		stop = 0;
+
 	if (((buffermem >> PAGE_SHIFT) * 100 > buffer_mem.borrow_percent * num_physpages)
 		   || (page_cache_size * 100 > page_cache.borrow_percent * num_physpages))
-		state = 0;
+		shrink_mmap(i, gfp_mask);
 
 	switch (state) {
 		do {
@@ -485,23 +486,6 @@
 }
 
 /*
- * This is REALLY ugly.
- *
- * We need to make the locks finer granularity, but right
- * now we need this so that we can do page allocations
- * without holding the kernel lock etc.
- */
-int try_to_free_page(int gfp_mask)
-{
-	int retval;
-
-	lock_kernel();
-	retval = do_try_to_free_page(gfp_mask);
-	unlock_kernel();
-	return retval;
-}
-
-/*
  * Before we start the kernel thread, print out the 
  * kswapd initialization message (otherwise the init message 
  * may be printed in the middle of another driver's init 
@@ -573,15 +557,16 @@
 		 * woken up more often and the rate will be even
 		 * higher).
 		 */
-		tries = pager_daemon.tries_base >> free_memory_available(3);
+		tries = pager_daemon.tries_base;
+		tries >>= 4*free_memory_available();
 	
 		while (tries--) {
 			int gfp_mask;
 
-			if (++tried > pager_daemon.tries_min && free_memory_available(0))
+			if (free_memory_available() > 1)
 				break;
 			gfp_mask = __GFP_IO;
-			try_to_free_page(gfp_mask);
+			do_try_to_free_page(gfp_mask);
 			/*
 			 * Syncing large chunks is faster than swapping
 			 * synchronously (less head movement). -- Rik.
@@ -597,6 +582,27 @@
 	return 0;
 }
 
+/*
+ * This is REALLY ugly.
+ *
+ * We need to make the locks finer granularity, but right
+ * now we need this so that we can do page allocations
+ * without holding the kernel lock etc.
+ */
+int try_to_free_pages(unsigned int gfp_mask, int count)
+{
+	int retval;
+
+	lock_kernel();
+	retval = 0;
+	do {
+		retval |= do_try_to_free_page(gfp_mask);
+		count--;
+	} while (count > 0);
+	unlock_kernel();
+	return retval;
+}
+
 /* 
  * The swap_tick function gets called on every clock tick.
  */
@@ -616,11 +622,11 @@
 	 * Schedule for wakeup if there isn't lots
 	 * of free memory.
 	 */
-	switch (free_memory_available(3)) {
+	switch (free_memory_available()) {
 	case 0:
 		want = now;
 		/* Fall through */
-	case 1 ... 3:
+	case 1:
 		want_wakeup = 1;
 	default:
 	}
diff -u --recursive --new-file v2.1.108/linux/net/802/Makefile linux/net/802/Makefile
--- v2.1.108/linux/net/802/Makefile	Fri May  8 23:14:57 1998
+++ linux/net/802/Makefile	Fri Jul 10 13:51:41 1998
@@ -23,6 +23,7 @@
 
 ifdef CONFIG_TR
 O_OBJS += tr.o
+	SNAP=y
 endif
 
 ifdef CONFIG_FDDI
@@ -42,7 +43,7 @@
 endif
 
 ifeq ($(SNAP),y)
-OX_OBJS += p8022.o psnap.o p8022tr.o
+OX_OBJS += p8022.o psnap.o
 endif
 
 
diff -u --recursive --new-file v2.1.108/linux/net/802/p8022tr.c linux/net/802/p8022tr.c
--- v2.1.108/linux/net/802/p8022tr.c	Mon Feb 23 18:12:12 1998
+++ linux/net/802/p8022tr.c	Wed Dec 31 16:00:00 1969
@@ -1,143 +0,0 @@
-/*
- * NET3:	Handling for token ring frames that are not IP. IP is hooked
- *		early in the token ring support code.
- *
- *		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/module.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/datalink.h>
-#include <linux/mm.h>
-#include <linux/in.h>
-#include <linux/init.h>
-#include <net/p8022tr.h>
-
-#define SNAP_HEADER_LEN	8
-
-static struct datalink_proto *p8022tr_list = NULL;
-
-/*
- *	We don't handle the loopback SAP stuff, the extended
- *	802.2 command set, multicast SAP identifiers and non UI
- *	frames. We have the absolute minimum needed for IPX,
- *	IP and Appletalk phase 2. See the llc_* routines for support
- *	to handle the fun stuff.
- *
- *	We assume the list will be very short (at the moment its normally
- *	one or two entries).
- */
-
-static struct datalink_proto *find_8022tr_client(unsigned char type)
-{
-	struct datalink_proto	*proto;
-
-	for (proto = p8022tr_list;
-		((proto != NULL) && (*(proto->type) != type));
-		proto = proto->next)
-		;
-
-	return proto;
-}
-
-int p8022tr_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
-{
-	struct datalink_proto	*proto;
-
-	proto = find_8022tr_client(*(skb->h.raw));
-	if (proto != NULL) {
-		skb->h.raw += 3;
-		skb_pull(skb,3);
-		return proto->rcvfunc(skb, dev, pt);
-	}
-
-	skb->sk = NULL;
-	kfree_skb(skb);
-	return 0;
-}
-
-static void p8022tr_datalink_header(struct datalink_proto *dl,
-		struct sk_buff *skb, unsigned char *dest_node)
-{
-	struct device	*dev = skb->dev;
-	unsigned char	*rawp;
-	unsigned char	*olddata;
-	unsigned char	*newdata;
-
-	rawp = skb_push(skb,3);
-	*rawp++ = dl->type[0];
-	*rawp++ = dl->type[0];
-	*rawp = 0x03;	/* UI */
-	dev->hard_header(skb, dev, ETH_P_802_3, dest_node, NULL, skb->len);
-	olddata = skb->data;
-	newdata = skb_pull(skb, SNAP_HEADER_LEN);
-	memmove(newdata, olddata, dev->hard_header_len - SNAP_HEADER_LEN);
-}
-
-static struct packet_type p8022tr_packet_type =
-{
-	0,
-	NULL,		/* All devices */
-	p8022tr_rcv,
-	NULL,
-	NULL,
-};
-
-
-EXPORT_SYMBOL(register_8022tr_client);
-EXPORT_SYMBOL(unregister_8022tr_client);
-
-__initfunc(void p8022tr_proto_init(struct net_proto *pro))
-{
-	p8022tr_packet_type.type=htons(ETH_P_TR_802_2);
-	dev_add_pack(&p8022tr_packet_type);
-}
-
-struct datalink_proto *register_8022tr_client(unsigned char type, 
-	int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *))
-{
-	struct datalink_proto	*proto;
-
-	if (find_8022tr_client(type) != NULL)
-		return NULL;
-
-	proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
-	if (proto != NULL) {
-		proto->type[0] = type;
-		proto->type_len = 1;
-		proto->rcvfunc = rcvfunc;
-		proto->header_length = 3;
-		proto->datalink_header = p8022tr_datalink_header;
-		proto->string_name = "802.2TR";
-		proto->next = p8022tr_list;
-		p8022tr_list = proto;
-	}
-
-	return proto;
-}
-
-void unregister_8022tr_client(unsigned char type)
-{
-	struct datalink_proto *tmp, **clients = &p8022tr_list;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-
-	while ((tmp = *clients) != NULL)
-	{
-		if (tmp->type[0] == type) {
-			*clients = tmp->next;
-			kfree_s(tmp, sizeof(struct datalink_proto));
-			break;
-		} else {
-			clients = &tmp->next;
-		}
-	}
-
-	restore_flags(flags);
-}
diff -u --recursive --new-file v2.1.108/linux/net/802/psnap.c linux/net/802/psnap.c
--- v2.1.108/linux/net/802/psnap.c	Tue Jun 23 10:01:30 1998
+++ linux/net/802/psnap.c	Fri Jul 10 13:51:41 1998
@@ -60,9 +60,11 @@
 		 */
 
 		skb->h.raw += 5;
+		skb->nh.raw += 5;
 		skb_pull(skb,5);
 		if (psnap_packet_type.type == 0)
 			psnap_packet_type.type=htons(ETH_P_SNAP);
+
 		return proto->rcvfunc(skb, dev, &psnap_packet_type);
 	}
 	skb->sk = NULL;
diff -u --recursive --new-file v2.1.108/linux/net/802/tr.c linux/net/802/tr.c
--- v2.1.108/linux/net/802/tr.c	Tue Mar 17 22:18:16 1998
+++ linux/net/802/tr.c	Fri Jul 10 13:51:41 1998
@@ -9,6 +9,9 @@
  * Fixes:       3 Feb 97 Paul Norton <pnorton@cts.com> Minor routing fixes.
  *              Added rif table to /proc/net/tr_rif and rif timeout to
  *              /proc/sys/net/token-ring/rif_timeout.
+ *              22 Jun 98 Paul Norton <p.norton@computer.org> Rearranged
+ *              tr_header and tr_type_trans to handle passing IPX SNAP and
+ *              802.2 through the correct layers. Eliminated tr_reformat.
  *        
  */
 
@@ -84,9 +87,30 @@
 int tr_header(struct sk_buff *skb, struct device *dev, unsigned short type,
               void *daddr, void *saddr, unsigned len) 
 {
+	struct trh_hdr *trh;
+	int hdr_len;
 
-	struct trh_hdr *trh=(struct trh_hdr *)skb_push(skb,dev->hard_header_len);
-	struct trllc *trllc=(struct trllc *)(trh+1);
+	/* 
+	 * Add the 802.2 SNAP header if IP as the IPv4 code calls  
+	 * dev->hard_header directly.
+	 */
+	if (type == ETH_P_IP || type == ETH_P_ARP)
+	{
+		struct trllc *trllc=(struct trllc *)(trh+1);
+
+		hdr_len = sizeof(struct trh_hdr) + sizeof(struct trllc);
+		trh = (struct trh_hdr *)skb_push(skb, hdr_len);
+		trllc = (struct trllc *)(trh+1);
+		trllc->dsap = trllc->ssap = EXTENDED_SAP;
+		trllc->llc = UI_CMD;
+		trllc->protid[0] = trllc->protid[1] = trllc->protid[2] = 0x00;
+		trllc->ethertype = htons(type);
+	}
+	else
+	{
+		hdr_len = sizeof(struct trh_hdr);
+		trh = (struct trh_hdr *)skb_push(skb, hdr_len);	
+	}
 
 	trh->ac=AC;
 	trh->fc=LLC_FRAME;
@@ -94,18 +118,7 @@
 	if(saddr)
 		memcpy(trh->saddr,saddr,dev->addr_len);
 	else
-		memset(trh->saddr,0,dev->addr_len); /* Adapter fills in address */
-
-	/*
-	 *	This is the stuff needed for IP encoding - IP over 802.2
-	 *	with SNAP.
-	 */
-	 
-	trllc->dsap=trllc->ssap=EXTENDED_SAP;
-	trllc->llc=UI_CMD;
-	
-	trllc->protid[0]=trllc->protid[1]=trllc->protid[2]=0x00;
-	trllc->ethertype=htons(type);
+		memcpy(trh->saddr,dev->dev_addr,dev->addr_len);
 
 	/*
 	 *	Build the destination and then source route the frame
@@ -115,10 +128,10 @@
 	{
 		memcpy(trh->daddr,daddr,dev->addr_len);
 		tr_source_route(skb,trh,dev);
-		return(dev->hard_header_len);
+		return(hdr_len);
 	}
-	return -dev->hard_header_len;
 
+	return -hdr_len;
 }
 	
 /*
@@ -161,12 +174,18 @@
 {
 
 	struct trh_hdr *trh=(struct trh_hdr *)skb->data;
-	struct trllc *trllc=(struct trllc *)(skb->data+sizeof(struct trh_hdr));
+	struct trllc *trllc;
+	unsigned riflen=0;
 	
 	skb->mac.raw = skb->data;
 	
-	skb_pull(skb,dev->hard_header_len);
-	
+       	if(trh->saddr[0] & TR_RII)
+		riflen = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
+
+	trllc = (struct trllc *)(skb->data+sizeof(struct trh_hdr)-TR_MAXRIFLEN+riflen);
+
+	skb_pull(skb,sizeof(struct trh_hdr)-TR_MAXRIFLEN+riflen);
+
 	tr_add_rif_info(trh, dev);
 
 	if(*trh->daddr & 1) 
@@ -183,38 +202,20 @@
 			skb->pkt_type=PACKET_OTHERHOST;
 	}
 
- 	return trllc->ethertype;
-}
-
-/*
- *      Reformat the headers to make a "standard" frame. This is done
- *      in-place in the sk_buff. 
- */
-
-void tr_reformat(struct sk_buff *skb, unsigned int hdr_len)
-{
-	struct trllc *llc = (struct trllc *)(skb->data+hdr_len);
-	struct device *dev = skb->dev;
-	unsigned char *olddata = skb->data;
-	int slack;
+	/*
+	 * Strip the SNAP header from ARP packets since we don't 
+	 * pass them through to the 802.2/SNAP layers.
+	 */
 
-	if (llc->dsap == 0xAA && llc->ssap == 0xAA)
+	if (trllc->dsap == EXTENDED_SAP &&
+	    (trllc->ethertype == ntohs(ETH_P_IP) ||
+	     trllc->ethertype == ntohs(ETH_P_ARP)))
 	{
-		slack = sizeof(struct trh_hdr) - hdr_len;
-		skb_push(skb, slack);
-		memmove(skb->data, olddata, hdr_len);
-		memset(skb->data+hdr_len, 0, slack);
+		skb_pull(skb, sizeof(struct trllc));
+		return trllc->ethertype;
 	}
-	else
-	{
-		struct trllc *local_llc;
-		slack = sizeof(struct trh_hdr) - hdr_len + sizeof(struct trllc);
-		skb_push(skb, slack);
-		memmove(skb->data, olddata, hdr_len);
-		memset(skb->data+hdr_len, 0, slack);
-		local_llc = (struct trllc *)(skb->data+dev->hard_header_len);
-		local_llc->ethertype = htons(ETH_P_TR_802_2);
-       	}
+
+	return ntohs(ETH_P_802_2);
 }
 
 /*
@@ -350,22 +351,23 @@
 			return;
 		}
 
+		memcpy(&(entry->addr[0]),&(trh->saddr[0]),TR_ALEN);
+		memcpy(&(entry->iface[0]),dev->name,5);
+		entry->next=rif_table[hash];
+		entry->last_used=jiffies;
+		rif_table[hash]=entry;
+
 		if (rii_p)
 		{
 			entry->rcf = trh->rcf & htons((unsigned short)~TR_RCF_BROADCAST_MASK);
 			memcpy(&(entry->rseg[0]),&(trh->rseg[0]),8*sizeof(unsigned short));
 			entry->local_ring = 0;
+			trh->saddr[0]|=TR_RII; /* put the routing indicator back for tcpdump */
 		}
 		else
 		{
 			entry->local_ring = 1;
 		}
-
-		memcpy(&(entry->addr[0]),&(trh->saddr[0]),TR_ALEN);
-		memcpy(&(entry->iface[0]),dev->name,5);
-		entry->next=rif_table[hash];
-		entry->last_used=jiffies;
-		rif_table[hash]=entry;
 	} 	
 	else	/* Y. Tahara added */
 	{ 
@@ -447,7 +449,7 @@
 	rif_cache entry;
 
 	size=sprintf(buffer,
-		     "if     TR address       TTL   rcf   routing segments\n\n");
+		     "if     TR address       TTL   rcf   routing segments\n");
 	pos+=size;
 	len+=size;
 
diff -u --recursive --new-file v2.1.108/linux/net/core/profile.c linux/net/core/profile.c
--- v2.1.108/linux/net/core/profile.c	Tue Jun 23 10:01:30 1998
+++ linux/net/core/profile.c	Fri Jul 10 14:01:13 1998
@@ -13,6 +13,7 @@
 #include <linux/inet.h>
 #include <net/checksum.h>
 
+#include <asm/processor.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
 
@@ -276,7 +277,7 @@
 
 	printk("Evaluating net profiler cost ...");
 #if CPU == 586 || CPU == 686
-	if (!(boot_cpu_data.x86_capability & 16)) {
+	if (!(boot_cpu_data.x86_capability & X86_FEATURE_TSC)) {
 		printk(KERN_ERR "Sorry, your CPU does not support TSC. Net profiler disabled.\n");
 		return -1;
 	}
diff -u --recursive --new-file v2.1.108/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.1.108/linux/net/ipx/af_ipx.c	Sun Jun  7 11:16:40 1998
+++ linux/net/ipx/af_ipx.c	Fri Jul 10 13:51:41 1998
@@ -49,6 +49,7 @@
  *	Revision 0.38:  Asynchronous socket stuff made current.
  *	Revision 0.39:  SPX interfaces
  *	Revision 0.40:  Tiny SIOCGSTAMP fix (chris@cybernet.co.nz)
+ *      Revision 0.41:  802.2TR removed (p.norton@computer.org)
  *
  *	Protect the module by a MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT
  *	pair. Also, now usage count is managed this way
@@ -89,12 +90,12 @@
 #include <linux/termios.h>	/* For TIOCOUTQ/INQ */
 #include <linux/interrupt.h>
 #include <net/p8022.h>
-#include <net/p8022tr.h>
 #include <net/psnap.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <linux/firewall.h>
 #include <linux/init.h>
+#include <linux/if_arp.h>
 
 #ifdef MODULE
 static void ipx_proto_finito(void);
@@ -107,7 +108,6 @@
 
 /* Global Variables */
 static struct datalink_proto	*p8022_datalink 	= NULL;
-static struct datalink_proto	*p8022tr_datalink 	= NULL;
 static struct datalink_proto	*pEII_datalink 		= NULL;
 static struct datalink_proto	*p8023_datalink 	= NULL;
 static struct datalink_proto	*pSNAP_datalink 	= NULL;
@@ -853,9 +853,6 @@
 		case IPX_FRAME_8022:
 			return (htons(ETH_P_802_2));
 
-		case IPX_FRAME_TR_8022:
-			return (htons(ETH_P_TR_802_2));
-
 		case IPX_FRAME_SNAP:
 			return (htons(ETH_P_SNAP));
 
@@ -883,23 +880,32 @@
 		&& (ipxitf_find_using_net(idef->ipx_network) != NULL))
 		return (-EADDRINUSE);
 
+	dev = dev_get(idef->ipx_device);
+	if(dev == NULL)
+		return (-ENODEV);
+
 	switch(idef->ipx_dlink_type) 
 	{
-		case IPX_FRAME_ETHERII:
-			dlink_type 	= htons(ETH_P_IPX);
-			datalink 	= pEII_datalink;
-			break;
-
 		case IPX_FRAME_TR_8022:
-			dlink_type 	= htons(ETH_P_TR_802_2);
-			datalink 	= p8022tr_datalink;
-			break;
+			printk("IPX frame type 802.2TR is obsolete. Use 802.2 instead.\n");
+			/* fall through */
 
 		case IPX_FRAME_8022:
 			dlink_type 	= htons(ETH_P_802_2);
 			datalink 	= p8022_datalink;
 			break;
 
+		case IPX_FRAME_ETHERII:
+			if (dev->type != ARPHRD_IEEE802)
+			{
+				dlink_type 	= htons(ETH_P_IPX);
+				datalink 	= pEII_datalink;
+				break;
+			}
+			else 
+				printk("IPX frame type EtherII over token-ring is obsolete. Use SNAP instead.\n");
+			/* fall through */
+
 		case IPX_FRAME_SNAP:
 			dlink_type 	= htons(ETH_P_SNAP);
 			datalink 	= pSNAP_datalink;
@@ -915,13 +921,6 @@
 			break;
 	}
 
-	if(datalink == NULL)
-		return (-EPROTONOSUPPORT);
-
-	dev = dev_get(idef->ipx_device);
-	if(dev == NULL)
-		return (-ENODEV);
-
 	if(!(dev->flags & IFF_UP))
 		return (-ENETDOWN);
 
@@ -929,6 +928,9 @@
 	if(dev->addr_len > IPX_NODE_LEN)
 		return (-EINVAL);
 
+	if(datalink == NULL)
+		return (-EPROTONOSUPPORT);
+
 	if((intrfc = ipxitf_find_using_phys(dev, dlink_type)) == NULL)
 	{
 		/* Ok now create */
@@ -1014,10 +1016,6 @@
 			datalink = p8022_datalink;
 			break;
 
-		case ETH_P_TR_802_2:
-			datalink = p8022tr_datalink;
-			break;
-
 		case ETH_P_SNAP:
 			datalink = pSNAP_datalink;
 			break;
@@ -2360,9 +2358,6 @@
 	if((p8022_datalink = register_8022_client(ipx_8022_type,ipx_rcv)) == NULL)
 		printk(KERN_CRIT "IPX: Unable to register with 802.2\n");
 
-	if((p8022tr_datalink = register_8022tr_client(ipx_8022_type,ipx_rcv)) == NULL)
-		printk(KERN_CRIT "IPX: Unable to register with 802.2TR\n");
-
 	if((pSNAP_datalink = register_snap_client(ipx_snap_id,ipx_rcv)) == NULL)
 		printk(KERN_CRIT "IPX: Unable to register with SNAP\n");
 
@@ -2431,9 +2426,6 @@
 
 	unregister_snap_client(ipx_snap_id);
 	pSNAP_datalink 	= NULL;
-
-	unregister_8022tr_client(ipx_8022_type);
-	p8022tr_datalink = NULL;
 
 	unregister_8022_client(ipx_8022_type);
 	p8022_datalink 	= NULL;
diff -u --recursive --new-file v2.1.108/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.108/linux/net/netsyms.c	Tue Jun  9 11:57:31 1998
+++ linux/net/netsyms.c	Fri Jul 10 13:51:41 1998
@@ -375,7 +375,6 @@
 EXPORT_SYMBOL(unregister_trdev);
 EXPORT_SYMBOL(init_trdev);
 EXPORT_SYMBOL(tr_freedev);
-EXPORT_SYMBOL(tr_reformat);
 #endif
                   
 /* Used by at least ipip.c.  */
diff -u --recursive --new-file v2.1.108/linux/net/protocols.c linux/net/protocols.c
--- v2.1.108/linux/net/protocols.c	Sun Nov 30 14:00:40 1997
+++ linux/net/protocols.c	Fri Jul 10 13:51:41 1998
@@ -86,7 +86,6 @@
 
 #ifdef NEED_802
 #include <net/p8022call.h>
-#include <net/p8022trcall.h>
 #endif
 
 /*
@@ -108,7 +107,6 @@
 
 #ifdef NEED_802
   { "802.2",	p8022_proto_init },			/* 802.2 demultiplexor		*/
-  { "802.2TR",	p8022tr_proto_init },			/* 802.2 demultiplexor		*/
   { "SNAP",	snap_proto_init },			/* SNAP demultiplexor		*/
 #endif
 
diff -u --recursive --new-file v2.1.108/linux/net/sched/sch_api.c linux/net/sched/sch_api.c
--- v2.1.108/linux/net/sched/sch_api.c	Fri May  8 23:14:58 1998
+++ linux/net/sched/sch_api.c	Fri Jul 10 14:01:13 1998
@@ -9,6 +9,7 @@
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  */
 
+#include <asm/processor.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -881,7 +882,7 @@
 	unsigned long stop;
 
 #if CPU == 586 || CPU == 686
-	if (!(boot_cpu_data.x86_capability & 16))
+	if (!(boot_cpu_data.x86_capability & X86_FEATURE_TSC)
 		return -1;
 #endif
 
