diff -u --recursive --new-file v2.1.104/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.104/linux/Documentation/Configure.help	Sun Jun  7 11:16:25 1998
+++ linux/Documentation/Configure.help	Sun Jun  7 10:37:41 1998
@@ -7511,15 +7511,21 @@
   Kernel patches and programs to do that are in the pcsndrv package on
   sunsite.unc.edu:/pub/Linux/kernel/patches/console/.
 
+Support for Aztech Sound Galaxy (non-Pnp) cards
+CONFIG_SOUND_SGALAXY
+  This module initialises the older non Plug and Play sound galaxy cards
+  from Aztech. It supports the Waverider Pro 32 - 3D and the Galaxy
+  Washington 16.
+
 ProAudioSpectrum 16 support
-CONFIG_PAS
+CONFIG_SOUND_PAS
   Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio
   16 or Logitech SoundMan 16 sound card.  Don't answer Y if you have
   some other card made by Media Vision or Logitech since they are not
   PAS16 compatible.
 
 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support
-CONFIG_SB
+CONFIG_SOUND_SB
   Answer Y if you have an original SoundBlaster card made by
   Creative Labs or a 100% hardware compatible clone (like the
   Thunderboard or SM Games). If your card was in the list of supported
@@ -7534,36 +7540,28 @@
   Y here and to "Additional lowlevel drivers" and to "SB32/AWE
   support" below.
 
-Are you using the IBM Mwave "emulation" of SB ?
-CONFIG_SB_MWAVE
-  The IBM Mwave can do what's loosely describable as emulation of an
-  8bit SoundBlaster card if you load the right firmware from DOS warm
-  boot and pray and your machine happens to like you. Say Y if you are
-  doing this as the IRQ test normally fails on the Mwave emulation.
-  Please read Documentation/sound/mwave.
-
 Generic OPL2/OPL3 FM synthesizer support
-CONFIG_ADLIB
+CONFIG_SOUND_ADLIB
   Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
   Answering Y is usually a safe and recommended choice, however some
   cards may have software (TSR) FM emulation. Enabling FM support with
   these cards may cause trouble (I don't currently know of any such
   cards, however). If unsure, say Y.
 
-#Loopback MIDI device support
-#CONFIG_VMIDI
+Loopback MIDI device support
+CONFIG_VMIDI
 ###
 ### somebody please fill this in.
 ###
-#
+
 Gravis Ultrasound support
-CONFIG_GUS
+CONFIG_SOUND_GUS
   Say Y here for any type of Gravis Ultrasound card, including
-  the GUS or GUS MAX. Please read Documentation/sound/ultrasound for
-  more information.
+  the GUS or GUS MAX. See also Documentation/sound/ultrasound for
+  more information on configuring this card with modules.
 
 MPU-401 support (NOT for SB16)
-CONFIG_MPU401
+CONFIG_SOUND_MPU401
   Be careful with this question. The MPU401 interface is supported by
   all sound cards. However, some natively supported cards have their
   own driver for MPU401. Enabling this MPU401 option with these cards
@@ -7574,35 +7572,38 @@
   answer Y if you have a true MPU401 MIDI interface card.
 
 6850 UART Midi support
-CONFIG_UART6850
+CONFIG_SOUND_UART6850
   This option enables support for MIDI interfaces based on the 6850
   UART chip. This interface is rarely found on sound cards.  It's safe
   to answer N to this question.
 
 PSS (AD1848, ADSP-2115, ESC614) support
-CONFIG_PSS
+CONFIG_SOUND_PSS
   Answer Y only if you have Orchid SW32, Cardinal DSP16 or some other
   card based on the PSS chipset (AD1848 codec + ADSP-2115 DSP chip +
   Echo ESC614 ASIC CHIP).
 
-#Enable PSS mixer (Beethoven ADSP-16 and other compatible)
-#CONFIG_PSS_MIXER
-###
-### Don't know what this is
-###
-#
+Have DSPxxx.LD firmware file
+CONFIG_PSS_HAVE_BOOT
+  If you want to emulate the Sound Blaster card and you have a DSPxxx.LD
+  file, then answer Y here to include this file.
+
+Full pathname of DSPxxx.LD firmware file
+CONFIG_PSS_BOOT_FILE
+  Enter the full pathname of your DSPxxx.LD file, starting from /.
+
 16 bit sampling option of GUS (_NOT_ GUS MAX)
-CONFIG_GUS16
+CONFIG_SOUND_GUS16
   Answer Y if you have installed the 16 bit sampling daughtercard on
   your GUS.  Answer N if you have a GUS MAX, since saying Y here
   disables GUS MAX support.
 
 GUS MAX support
-CONFIG_GUSMAX
+CONFIG_SOUND_GUSMAX
   Answer Y only if you have a Gravis Ultrasound MAX.
 
 Microsoft Sound System support
-CONFIG_MSS
+CONFIG_SOUND_MSS
   Again think carefully before answering Y to this question.  It's
   safe to answer Y if you have the original Windows Sound System card
   made by Microsoft or Aztech SG 16 Pro (or NX16 Pro).  Also you may
@@ -7628,60 +7629,81 @@
   conflict.
 
 Ensoniq Soundscape support
-CONFIG_SSCAPE
+CONFIG_SOUND_SSCAPE
   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 (Reveal makes also other cards).
 
 MediaTriX AudioTriX Pro support
-CONFIG_TRIX
+CONFIG_SOUND_TRIX
   Answer Y if you have the AudioTriX Pro sound card manufactured
   by MediaTrix.
 
+Have TRXPRO.HEX firmware file
+CONFIG_TRIX_HAVE_BOOT
+  The MediaTrix AudioTrix Pro has an on-board microcontroller which
+  needs to be initialized by downloading the code from the file
+  TRXPRO.HEX in the DOS driver directory. If you don't have the
+  TRXPRO.HEX file handy you may skip this step. However, the SB and
+  MPU-401 modes of AudioTrix Pro will not work without this file!
+
+Full pathname of TRXPRO.HEX firmware file
+CONFIG_TRIX_BOOT_FILE
+  Enter the full pathname of your TRXPRO.HEX file, starting from /.
+
 Support for OPTi MAD16 and/or Mozart based cards
-CONFIG_MAD16
+CONFIG_SOUND_MAD16
   Answer Y if your card has a Mozart (OAK OTI-601) or MAD16 (OPTi
-  82C928 or 82C929 or 82C931) audio interface chip. Please read
-  Documentation/sound/MAD16. Please read Documentation/sound/Opti. For
-  the 82C931, additional information is in drivers/sound/README.C931.
-  These chips are currently quite common so it's possible that many
-  no-name cards have one of them. In addition the MAD16 chip is used
-  in some cards made by known manufacturers such as Turtle Beach
-  (Tropez), Reveal (some models) and Diamond (latest ones).
+  82C928 or 82C929 or 82C931) audio interface chip. For the 82C931,
+  please read drivers/sound/README.C931. These chips are currently
+  quite common so it's possible that many no-name cards have one of
+  them. In addition the MAD16 chip is used in some cards made by known
+  manufacturers such as Turtle Beach (Tropez), Reveal (some models)
+  and Diamond (latest ones). See also Documentation/sound/Opti for
+  more information on setting these cards up as modules.
 
 Support MIDI in older MAD16 based cards (requires SB)
 CONFIG_MAD16_OLDCARD
-  Answer Y (or M) if you have an older card based on the C928 or
-  Mozart chipset and you want to have MIDI support. If you enable this
-  option you also need to enable support for SoundBlaster.
+  Answer Y (or M) if you have an older card based on the C928
+  or Mozart chipset and you want to have  MIDI support. If you
+  enable this option you also need to enable support for SoundBlaster.
 
 Support for Crystal CS4232 based (PnP) cards
-CONFIG_CS4232
+CONFIG_SOUND_CS4232
   Say Y here if you have a card based on the Crystal CS4232 chip set,
-  which use the Plug and Play protocol. Please read
-  Documentation/sound/CS4232 for more info.
+  which use its own Plug and Play protocol. See Documentation/sound/CS4232
+  for more information on configuring this card.
 
 Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers
-CONFIG_MAUI
+CONFIG_SOUND_MAUI
   Say Y here if you have a Turtle Beach Wave Front, Maui, or 
   Tropez sound card.
 
+Have OSWF.MOT firmware file
+CONFIG_MAUI_HAVE_BOOT
+  Turtle Beach Maui and Tropez sound cards have a microcontroller which
+  needs to be initialized prior to use.  OSWF.MOT is a file distributed
+  with the card's DOS/Windows drivers.  Answer Y if you have this file.
+
+Full pathname of OSWF.MOT firmware file
+CONFIG_MAUI_BOOT_FILE
+  Enter the full pathname of your OSWF.MOT file, starting from /.
+
 /dev/dsp and /dev/audio support
-CONFIG_AUDIO
+CONFIG_SOUND_AUDIO
   Answering N disables /dev/dsp and /dev/audio, the A/D and D/A
   converter devices.  Answer N only if you know you will not need
   the option.  They are usually required.  Answer Y.
 
 MIDI interface support
-CONFIG_MIDI
+CONFIG_SOUND_MIDI
   Answering N disables /dev/midixx devices and access to any MIDI
   ports using /dev/sequencer and /dev/music. This option also affects
   any MPU401 and/or General MIDI compatible devices.  Answer Y.
 
 FM synthesizer (YM3812/OPL-3) support
-CONFIG_YM3812
-  Answer Y here, unless you know you will not need the option. For
-  OPL-3 cards, you may want to read Documentation/sound/OPL3.
+CONFIG_SOUND_YM3812
+  Answer Y here, unless you know you will not need the option.
 
 Sun Audio support
 CONFIG_SUN_AUDIO
@@ -7694,6 +7716,11 @@
   appeared, say Y. The answer to this question does not directly
   affect the kernel; saying Y will simply cause this configure script
   to present you with more options. If unsure, say Y.
+
+SB32/AWE support
+CONFIG_AWE32_SYNTH
+  Say Y here if you have a SB32 or SB AWE soundcard. See
+  drivers/sound/lowlevel/README.awe for more info.
 
 ACI mixer (miroPCM12)
 CONFIG_ACI_MIXER
diff -u --recursive --new-file v2.1.104/linux/Documentation/SMP.txt linux/Documentation/SMP.txt
--- v2.1.104/linux/Documentation/SMP.txt	Sun Jun  7 11:16:25 1998
+++ linux/Documentation/SMP.txt	Sun Jun  7 10:37:41 1998
@@ -1,25 +1,20 @@
+SMP on x86/Linux is now an official feature and is not exprtimental.
+Experimental SMP support for other architectures is underway.
+
+Please view linux/Documentation/smp for more information about enabling SMP.
+
 SMP support for Linux with up to 16 processors using the Intel MP
 specification. 
 
-WARNING:
-	This is experimental. Back up your disks first. Experience is that
-it is basically stable in its current (inefficient form).
-
 To fix:
 
-o	Fix sys_idle to exit/enter kernel state and do hlt's.
-o	Fix scheduler decisions to reschedule. Per cpu reschedule ?
-o	Clean up message pass.
-o	Test for B stepping processors.
 o	Clean up processor specific/independent split.
 o	Document it all.	[PARTLY DONE]
 o	Halt other CPU's on reset/panic doesn't always work.
 o	Don't waste page at 4K - don't need it now.(watch the GDT code).
 o	Dump bootup pages once booted somehow.
 o	Clean up warnings/volatiles.
-o	Fix load_TR() for non contiguous processor ids
-o	Iterate over the slave timer requests if one is lost (keep a count per cpu)
-o	Distribute IRQs (locking present just needs the 82489 to be asked
-	nicely).
 o	486 startup code.
 o	How to handle mixed FPU/non FPU processors.
+o	Test IO-APIC on various hardware.
+o	Seperate IO-APIC from SMP.
diff -u --recursive --new-file v2.1.104/linux/Documentation/networking/baycom.txt linux/Documentation/networking/baycom.txt
--- v2.1.104/linux/Documentation/networking/baycom.txt	Sat May  2 14:19:51 1998
+++ linux/Documentation/networking/baycom.txt	Sun Jun  7 11:13:44 1998
@@ -1,9 +1,41 @@
-		    LINUX DRIVER FOR BAYCOM MODEMS
+		    LINUX DRIVERS FOR BAYCOM MODEMS
 
        Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
 
-This document describes the Linux Kernel Driver for simple Baycom style
-amateur radio modems. The driver supports the following modems:
+!!NEW!! (04/98) The drivers for the baycom modems have been split into
+separate drivers as they did not share any code, and the driver
+and device names have changed.
+
+This document describes the Linux Kernel Drivers for simple Baycom style
+amateur radio modems. 
+
+The following drivers are available:
+
+baycom_ser_fdx:
+  This driver supports the SER12 modems either full or half duplex.
+  Its baud rate may be changed via the `baud' module parameter,
+  therefore it supports just about every bit bang modem on a
+  serial port. Its devices are called bcsf0 through bcsf3.
+  This is the recommended driver for SER12 type modems,
+  however if you have a broken UART clone that does not have working
+  delta status bits, you may try baycom_ser_hdx. 
+
+baycom_ser_hdx: 
+  This is an alternative driver for SER12 type modems.
+  It only supports half duplex, and only 1200 baud. Its devices
+  are called bcsh0 through bcsh3. Use this driver only if baycom_ser_fdx
+  does not work with your UART.
+
+baycom_par:
+  This driver supports the par96 and picpar modems.
+  Its devices are called bcp0 through bcp3.
+
+baycom_epp:
+  This driver supports the epp modem.
+  Its devices are called bce0 through bce3.
+  This driver is work-in-progress.
+
+The following modems are supported:
 
 ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
         of a modulator/demodulator chip, usually a TI TCM3105. The computer
@@ -28,6 +60,13 @@
         an additional power supply. Furthermore, it incorporates a carrier
         detect circuitry.
 
+epp:    This is a high speed modem adaptor that connects to an enhanced parallel port.
+        Its target audience is users working over a high speed hub (76.8kbit/s).
+
+eppfpga: This is a redesign of the epp adaptor.
+
+
+
 All of the above modems only support half duplex communications. However,
 the driver supports the KISS (see below) fullduplex command. It then simply
 starts to send as soon as there's a packet to transmit and does not care
@@ -36,11 +75,11 @@
 access protocol.
 
 
-The Interface of the driver
+The Interface of the drivers
 
-Unlike previous drivers, the driver is no longer a character device,
-but it is now a true kernel network interface. Installation is therefore
-simple. Once installed, four interfaces named bc[0-3] are available.
+Unlike previous drivers, these drivers are no longer character devices,
+but they are now true kernel network interfaces. Installation is therefore
+simple. Once installed, four interfaces named bc{sf,sh,p,e}[0-3] are available.
 sethdlc from the ax25 utilities may be used to set driver states etc.
 Users of userland AX.25 stacks may use the net2kiss utility (also available
 in the ax25 utilities package) to convert packets of a network interface
@@ -50,27 +89,27 @@
 
 Configuring the driver
 
-Every time the driver is inserted into the kernel, it has to know which
+Every time a driver is inserted into the kernel, it has to know which
 modems it should access at which ports. This can be done with the setbaycom
 utility. If you are only using one modem, you can also configure the
 driver from the insmod command line (or by means of an option line in
 /etc/conf.modules).
 
 Examples:
-  insmod baycom modem=1 iobase=0x3f8 irq=4 options=1
-  sethdlc -i bc0 -p type ser12 io 0x3f8 irq 4 options 1
+  insmod baycom_ser_fdx mode="ser12*" iobase=0x3f8 irq=4
+  sethdlc -i bcsf0 -p mode "ser12*" io 0x3f8 irq 4
 
 Both lines configure the first port to drive a ser12 modem at the first
-serial port (COM1 under DOS). options=1 instructs the driver to use
+serial port (COM1 under DOS). The * in the mode parameter instructs the driver to use
 the software DCD algorithm (see below).
 
-  insmod baycom modem=2 iobase=0x378 irq=7 options=1
-  sethdlc -i bc0 -p type par96 io 0x378 irq 7 options 1
+  insmod baycom_par mode="picpar" iobase=0x378
+  sethdlc -i bcp0 -p mode "picpar" io 0x378
+
+Both lines configure the first port to drive a picpar modem at the
+first parallel port (LPT1 under DOS). (Note: picpar implies
+hardware DCD, par96 implies software DCD).
 
-Both lines configure the first port to drive a par96 or par97 modem at the
-first parallel port (LPT1 under DOS). options=1 instructs the driver to use
-the software DCD algorithm (see below).
-  
 The channel access parameters can be set with sethdlc -a or kissparms.
 Note that both utilities interpret the values slightly differently.
 
@@ -93,25 +132,26 @@
         feeds the DCD input of the PAR96 modem, the use of the hardware
         DCD circuitry is recommended.
 
-picpar: the picpar modem features builtin DCD hardware, which is highly
+picpar: the picpar modem features a builtin DCD hardware, which is highly
         recommended.
 
 
 
 Compatibility with the rest of the Linux kernel
 
-The serial driver, the line printer (lp) driver and the baycom driver compete
+The serial driver and the baycom serial drivers compete
 for the same hardware resources. Of course only one driver can access a given
 interface at a time. The serial driver grabs all interfaces it can find at
-startup time. Therefore the baycom driver subsequently won't be able to
+startup time. Therefore the baycom drivers subsequently won't be able to
 access a serial port. You might therefore find it necessary to release
 a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where
-# is the number of the interface. The baycom driver does not reserve any
-port at startup, unless one is specified on the 'insmod' command line. Another
-method to solve the problem is to compile all three drivers as modules and
-leave it to kerneld to load the correct driver depending on the application.
-
+# is the number of the interface. The baycom drivers do not reserve any
+ports at startup, unless one is specified on the 'insmod' command line. Another
+method to solve the problem is to compile all drivers as modules and
+leave it to kmod to load the correct driver depending on the application.
 
+The parallel port drivers (baycom_par, baycom_epp) now use the parport subsystem
+to arbitrate the ports between different client drivers.
 
 vy 73s de
 Tom Sailer, sailer@ife.ee.ethz.ch
diff -u --recursive --new-file v2.1.104/linux/Documentation/sound/ESS1868 linux/Documentation/sound/ESS1868
--- v2.1.104/linux/Documentation/sound/ESS1868	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/sound/ESS1868	Sun Jun  7 10:37:41 1998
@@ -0,0 +1,84 @@
+Documentation for the ESS1868F AudioDrive PnP sound card
+
+The ESS1868 Sound card is a PnP ESS1688 compatible 16-bit sound card.
+
+Notes about configuring the sound card:
+
+  *  The ESS1868 does not allow use of a 16-bit DMA, thus DMA 0, 1, 2, and 3
+     may only be used.
+
+  *  isapnptools version 1.14 does work with ESS1868.  Earlier versions might
+     not.
+  
+  *  Sound support MUST be compiled as MODULES, not statically linked
+     into the kernel.
+  
+For configuring the sound card's I/O addresses, IRQ and DMA, here is a
+sample copy of the isapnp.conf directives regarding the ESS1868:
+
+(CONFIGURE ESS1868/-1 (LD 1
+(IO 0 (BASE 0x0220))
+(IO 1 (BASE 0x0388))
+(IO 2 (BASE 0x0330))
+(DMA 0 (CHANNEL 1))
+(INT 0 (IRQ 5 (MODE +E)))
+(ACT Y)
+))
+
+(for a full working isapnp.conf file, remember the
+(ISOLATE)
+(IDENTIFY *)
+at the beginning and the
+(WAITFORKEY)
+at the end.)
+
+In this setup, the main card I/O is 0x0220, FM synthesizer is 0x0388, and
+the MPU-401 MIDI port is located at 0x0330.  IRQ is IRQ 5, DMA is channel 1.
+
+After configuring the sound card via isapnp, to use the card you must load
+the sound modules with the proper I/O information.  Here is my setup:
+
+# ESS1868F AudioDrive initialization
+
+/sbin/insmod sound
+/sbin/insmod uart401
+/sbin/insmod sb io=0x220 irq=5 dma=1 dma16=-1
+/sbin/insmod mpu401 io=0x330
+/sbin/insmod opl3 io=0x388
+/sbin/insmod v_midi
+
+opl3 is the FM synthesizer--I have not tried the SoftOSS wavetable
+synthesizer yet, but I assume it would work as well.  Also, doing:
+/sbin/insmod opl3
+/sbin/insmod adlib_card io=0x388
+works, but I believe the sound quality is a bit distorted when playing MIDI
+files.
+
+When using the above setup, my /proc/sound gives the following:
+
+OSS/Free:3.8s2++-971130
+Load type: Driver loaded as a module
+Kernel: Linux scitus.dyn.ml.org 2.1.104 #1 SMP Sun May 24 11:04:27 EDT 1998 i486
+Config options: 0
+
+Installed drivers: 
+
+Card config: 
+
+Audio devices:
+0: ESS ES1688 AudioDrive (rev 11) (3.1)
+
+Synth devices:
+0: Yamaha OPL-3
+
+Midi devices:
+0: Loopback MIDI Port 1
+1: Loopback MIDI Port 2
+
+Timers:
+0: System clock
+
+Mixers:
+0: Sound Blaster
+
+
diff -u --recursive --new-file v2.1.104/linux/Makefile linux/Makefile
--- v2.1.104/linux/Makefile	Sun Jun  7 11:16:26 1998
+++ linux/Makefile	Sun Jun  7 10:41:28 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 104
+SUBLEVEL = 105
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
@@ -318,6 +318,7 @@
 	if [ -f NLS_MODULES   ]; then inst_mod NLS_MODULES   fs;    fi; \
 	if [ -f CDROM_MODULES ]; then inst_mod CDROM_MODULES cdrom; fi; \
 	if [ -f HAM_MODULES   ]; then inst_mod HAM_MODULES   net;   fi; \
+	if [ -f SOUND_MODULES ]; then inst_mod SOUND_MODULES sound; fi; \
 	\
 	ls *.o > .allmods; \
 	echo $$MODULES | tr ' ' '\n' | sort | comm -23 .allmods - > .misc; \
diff -u --recursive --new-file v2.1.104/linux/Rules.make linux/Rules.make
--- v2.1.104/linux/Rules.make	Sun Jun  7 11:16:26 1998
+++ linux/Rules.make	Sat Jun  6 18:52:04 1998
@@ -53,7 +53,7 @@
 %.o: %.c
 	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -c -o $@ $<
 	@ ( \
-	    echo 'ifeq ($(strip $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@)),$$(strip $$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@)))' ; \
+	    echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@))),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@))))' ; \
 	    echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
 	    echo 'endif' \
 	) > $(dir $@)/.$(notdir $@).flags
@@ -79,7 +79,7 @@
 	$(AR) rcs $@
 endif
 	@ ( \
-	    echo 'ifeq ($(strip $(EXTRA_LDFLAGS) $(ALL_O)),$$(strip $$(EXTRA_LDFLAGS) $$(ALL_O)))' ; \
+	    echo 'ifeq ($(strip $(subst $(comma),:,$(EXTRA_LDFLAGS) $(ALL_O))),$$(strip $$(subst $$(comma),:,$$(EXTRA_LDFLAGS) $$(ALL_O))))' ; \
 	    echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
 	    echo 'endif' \
 	) > $(dir $@)/.$(notdir $@).flags
@@ -93,7 +93,7 @@
 	rm -f $@
 	$(AR) $(EXTRA_ARFLAGS) rcs $@ $(LX_OBJS) $(L_OBJS)
 	@ ( \
-	    echo 'ifeq ($(strip $(EXTRA_ARFLAGS) $(LX_OBJS) $(L_OBJS)),$$(strip $$(EXTRA_ARFLAGS) $$(LX_OBJS) $$(L_OBJS)))' ; \
+	    echo 'ifeq ($(strip $(subst $(comma),:,$(EXTRA_ARFLAGS) $(LX_OBJS) $(L_OBJS))),$$(strip $$(subst $$(comma),:,$$(EXTRA_ARFLAGS) $$(LX_OBJS) $$(L_OBJS))))' ; \
 	    echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
 	    echo 'endif' \
 	) > $(dir $@)/.$(notdir $@).flags
@@ -227,7 +227,7 @@
 $(SYMTAB_OBJS): $(TOPDIR)/include/linux/modversions.h $(SYMTAB_OBJS:.o=.c)
 	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c)
 	@ ( \
-	    echo 'ifeq ($(strip $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB),$$(strip $$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@) -DEXPORT_SYMTAB))' ; \
+	    echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB)),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@) -DEXPORT_SYMTAB)))' ; \
 	    echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
 	    echo 'endif' \
 	) > $(dir $@)/.$(notdir $@).flags
@@ -253,6 +253,9 @@
 #   every file is forced, except those whose flags are positively up-to-date.
 #
 FILES_FLAGS_UP_TO_DATE :=
+
+# For use in expunging commas from flags, which mung our checking.
+comma = ,
 
 FILES_FLAGS_EXIST := $(wildcard .*.flags)
 ifneq ($(FILES_FLAGS_EXIST),)
diff -u --recursive --new-file v2.1.104/linux/arch/alpha/Makefile linux/arch/alpha/Makefile
--- v2.1.104/linux/arch/alpha/Makefile	Fri May  8 23:14:41 1998
+++ linux/arch/alpha/Makefile	Sat Jun  6 18:52:04 1998
@@ -10,17 +10,13 @@
 
 NM := nm -B
 
-#LINKFLAGS = -static -T arch/alpha/vmlinux.lds
-#CFLAGS := $(CFLAGS) -pipe -mno-fp-regs -ffixed-8
-
 ifdef CONFIG_CROSSCOMPILE
 # enable this for linking under OSF/1:
 LINKFLAGS = -non_shared -T 0xfffffc0000310000 -N
 else
  elf=$(shell if $(LD) --help | grep elf64alpha >/dev/null; then echo yes; fi)
  ifeq ($(elf),yes)
-#   LINKFLAGS = -static -Ttext 0xfffffc0000310000 -N
-LINKFLAGS = -static -T arch/alpha/vmlinux.lds
+   LINKFLAGS = -static -T arch/alpha/vmlinux.lds
  else
    LINKFLAGS = -static -T arch/alpha/vmlinux.lds -N
  endif
@@ -28,16 +24,13 @@
 CFLAGS := $(CFLAGS) -pipe
 endif
 
-CFLAGS := $(CFLAGS) -mno-fp-regs -ffixed-8 -Wno-uninitialized
+CFLAGS := $(CFLAGS) -mno-fp-regs -ffixed-8
 
 # determine if we can use the BWX instructions with GAS
-$(shell rm -f ./GAS_VER)
-$(shell $(AS) --version >& ./GAS_VER)
-OLD_GAS := $(shell if cat ./GAS_VER | grep 'version 2.7' > /dev/null; then echo yes; else echo no; fi)
-$(shell rm -f ./GAS_VER)
+OLD_GAS := $(shell if $(AS) --version 2>&1 | grep 'version 2.7' > /dev/null; then echo y; else echo n; fi)
 
-ifneq ($(OLD_GAS),yes)
 # if PYXIS, then enable use of BWIO space
+ifneq ($(OLD_GAS),y)
  ifeq ($(CONFIG_ALPHA_PYXIS),y)
   CFLAGS := $(CFLAGS) -Wa,-m21164a -DBWX_USABLE -DBWIO_ENABLED
  endif
diff -u --recursive --new-file v2.1.104/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.104/linux/arch/i386/defconfig	Thu May 14 19:47:38 1998
+++ linux/arch/i386/defconfig	Sun Jun  7 11:21:35 1998
@@ -189,6 +189,7 @@
 # CONFIG_DEC_ELCP is not set
 # CONFIG_DGRS is not set
 CONFIG_EEXPRESS_PRO100=y
+# CONFIG_NE2K_PCI is not set
 # CONFIG_NET_POCKET is not set
 # CONFIG_FDDI is not set
 # CONFIG_DLCI is not set
diff -u --recursive --new-file v2.1.104/linux/arch/i386/kernel/io_apic.c linux/arch/i386/kernel/io_apic.c
--- v2.1.104/linux/arch/i386/kernel/io_apic.c	Sun Jun  7 11:16:27 1998
+++ linux/arch/i386/kernel/io_apic.c	Sat Jun  6 23:08:44 1998
@@ -616,7 +616,7 @@
 	entry.delivery_mode = dest_ExtINT;
 	entry.dest_mode = 1;				/* logical delivery */
 	entry.mask = 0;					/* unmask IRQ now */
-	entry.dest.logical.logical_dest = 0xff;		/* all CPUs */
+	entry.dest.logical.logical_dest = 0x01;		/* all CPUs */
 
 	entry.vector = IO_APIC_VECTOR(pin);		/* it's ignored */
 
diff -u --recursive --new-file v2.1.104/linux/drivers/Makefile linux/drivers/Makefile
--- v2.1.104/linux/drivers/Makefile	Wed Dec  3 15:17:30 1997
+++ linux/drivers/Makefile	Sun Jun  7 10:37:41 1998
@@ -7,9 +7,9 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-SUB_DIRS     := block char net misc #streams
+SUB_DIRS     := block char net misc sound
 MOD_SUB_DIRS := $(SUB_DIRS) sbus
-ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus sound cdrom isdn pnp macintosh
+ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus cdrom isdn pnp macintosh
 
 ifdef CONFIG_PCI
 SUB_DIRS += pci
@@ -32,14 +32,6 @@
 else
   ifeq ($(CONFIG_SCSI),m)
   MOD_SUB_DIRS += scsi
-  endif
-endif
-
-ifeq ($(CONFIG_SOUND),y)
-SUB_DIRS += sound
-else
-  ifeq ($(CONFIG_SOUND),m)
-  MOD_SUB_DIRS += sound
   endif
 endif
 
diff -u --recursive --new-file v2.1.104/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.104/linux/drivers/char/Config.in	Thu Apr 23 20:21:32 1998
+++ linux/drivers/char/Config.in	Sun Jun  7 10:37:41 1998
@@ -110,20 +110,27 @@
 if [ "$CONFIG_ALPHA_BOOK1" = "y" ]; then
   bool 'Tadpole ANA H8 Support'  CONFIG_H8
 fi
+
 tristate 'Video For Linux' CONFIG_VIDEO_DEV
 if [ "$CONFIG_VIDEO_DEV" != "n" ]; then
+  dep_tristate 'AIMSlab RadioTrack (aka RadioReveal) support' CONFIG_RADIO_RTRACK $CONFIG_VIDEO_DEV
+  if [ "$CONFIG_RADIO_RTRACK" = "y" ]; then
+    hex '  RadioTrack i/o port (0x20f or 0x30f)' CONFIG_RADIO_RTRACK_PORT 20f
+  fi
+  dep_tristate 'Aztech/Packard Bell Radio' CONFIG_RADIO_AZTECH $CONFIG_VIDEO_DEV
+  if [ "$CONFIG_RADIO_AZTECH" = "y" ]; then
+    hex '  Aztech/Packard Bell I/O port (0x350 or 0x358)' CONFIG_RADIO_AZTECH_PORT 350
+  fi
   dep_tristate 'BT848 Video For Linux' CONFIG_VIDEO_BT848 $CONFIG_VIDEO_DEV
   if [ "$CONFIG_PARPORT" != "n" ]; then
     dep_tristate 'Quickcam BW Video For Linux' CONFIG_VIDEO_BWQCAM $CONFIG_VIDEO_DEV
     dep_tristate 'Colour QuickCam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV
   fi
   dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV
-  #dep_tristate 'SAA5249 Teletext processor' CONFIG_VIDEO_SAA5249 $CONFIG_VIDEO_DEV
-  if [ "$CONFIG_VIDEO_SAA5249" != "n" ]; then
-	define_bool CONFIG_BUS_I2C $CONFIG_VIDEO_SAA5249
-  fi
-  if [ "$CONFIG_VIDEO_BT848" != "n" ]; then
-	define_bool CONFIG_BUS_I2C $CONFIG_VIDEO_BT848
+  dep_tristate 'SAA5249 Teletext processor' CONFIG_VIDEO_SAA5249 $CONFIG_VIDEO_DEV
+  dep_tristate 'SF16FMI Radio' CONFIG_RADIO_SF16FMI $CONFIG_VIDEO_DEV
+  if [ "$CONFIG_RADIO_SF16FMI" = "y" ]; then
+    hex '  SF16FMI I/O port (0x284 or 0x384)' CONFIG_RADIO_SF16FMI_PORT 284
   fi
 fi
 tristate '/dev/nvram support' CONFIG_NVRAM
@@ -132,7 +139,7 @@
 if [ "$CONFIG_MISC_RADIO" != "n" ]; then
   bool '  AIMSlab RadioTrack (aka RadioReveal) support' CONFIG_RADIO_RTRACK
   if [ "$CONFIG_RADIO_RTRACK" != "n" ]; then
-    hex '    RadioTrack i/o port (0x20f or 0x30f)' CONFIG_RADIO_RTRACK_PORT 0x20f
+    hex '    RadioTrack i/o port (0x20f or 0x30f)' CONFIG_RADIO_RTRACK_PORT 20f
   fi
 fi
 
diff -u --recursive --new-file v2.1.104/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.1.104/linux/drivers/char/Makefile	Wed Apr  8 19:36:25 1998
+++ linux/drivers/char/Makefile	Sun Jun  7 10:37:41 1998
@@ -303,26 +303,30 @@
 endif
 
 ifeq ($(CONFIG_BUS_I2C),y)
-	LX_OBJS += i2c.o
+	L_I2C=y
 else
   ifeq ($(CONFIG_BUS_I2C),m)
-	MX_OBJS += i2c.o
+	M_I2C=y
   endif
 endif
 
 ifeq ($(CONFIG_VIDEO_BT848),y)
 L_OBJS += bttv.o msp3400.o tuner.o
+L_I2C=y
 else
   ifeq ($(CONFIG_VIDEO_BT848),m)
     M_OBJS += bttv.o msp3400.o tuner.o
+    M_I2C=y
   endif
 endif
 
 ifeq ($(CONFIG_VIDEO_SAA5249),y)
 L_OBJS += saa5249.o
+L_I2C=y
 else
   ifeq ($(CONFIG_VIDEO_SAA5249),m)
     M_OBJS += saa5249.o
+    M_I2C=y
   endif
 endif
 
@@ -350,6 +354,30 @@
   endif
 endif
 
+ifeq ($(CONFIG_RADIO_AZTECH),y)
+L_OBJS += radio-aztech.o
+else
+  ifeq ($(CONFIG_RADIO_AZTECH),m)
+  M_OBJS += radio-aztech.o
+  endif
+endif                                             
+
+ifeq ($(CONFIG_RADIO_SF16FMI),y)
+L_OBJS += radio-sf16fmi.o
+else
+  ifeq ($(CONFIG_RADIO_SF16FMI),m)
+  M_OBJS += radio-sf16fmi.o
+  endif
+endif                                             
+
+ifeq ($(CONFIG_RADIO_RTRACK),y)
+L_OBJS += radio-aimslab.o
+else
+  ifeq ($(CONFIG_RADIO_RTRACK),m)
+  M_OBJS += radio-aimslab.o
+  endif
+endif                                             
+
 ifeq ($(CONFIG_MISC_RADIO),y)
 L_OBJS += radio.o
   ifeq ($(CONFIG_RADIO_RTRACK),y)
@@ -392,6 +420,14 @@
 else
   ifdef MM
   MX_OBJS += misc.o
+  endif
+endif
+
+ifeq ($(L_I2C),y)
+LX_OBJS += i2c.o
+else
+  ifeq ($(M_I2C),y)
+  MX_OBJS += i2c.o
   endif
 endif
 
diff -u --recursive --new-file v2.1.104/linux/drivers/char/bt848.h linux/drivers/char/bt848.h
--- v2.1.104/linux/drivers/char/bt848.h	Wed Apr  8 19:36:25 1998
+++ linux/drivers/char/bt848.h	Sun Jun  7 10:37:41 1998
@@ -40,6 +40,7 @@
 #define BT848_DSTATUS_FIELD    (1<<5)
 #define BT848_DSTATUS_NUML     (1<<4)
 #define BT848_DSTATUS_CSEL     (1<<3)
+#define BT848_DSTATUS_PLOCK    (1<<2)
 #define BT848_DSTATUS_LOF      (1<<1)
 #define BT848_DSTATUS_COF      (1<<0)
 
@@ -55,10 +56,12 @@
 #define BT848_IFORM_XTAUTO     (3<<3)
 #define BT848_IFORM_XTBOTH     (3<<3)
 #define BT848_IFORM_NTSC       1
+#define BT848_IFORM_NTSC_J     2
 #define BT848_IFORM_PAL_BDGHI  3
 #define BT848_IFORM_PAL_M      4
 #define BT848_IFORM_PAL_N      5
 #define BT848_IFORM_SECAM      6
+#define BT848_IFORM_PAL_NC     7
 #define BT848_IFORM_AUTO       0
 #define BT848_IFORM_NORM       7
 
@@ -114,6 +117,12 @@
 #define BT848_SCLOOP_HFILT_QCIF (2<<3)
 #define BT848_SCLOOP_HFILT_ICON (3<<3)
 
+#define BT848_SCLOOP_PEAK       (1<<7)
+#define BT848_SCLOOP_HFILT_MINP (1<<3)
+#define BT848_SCLOOP_HFILT_MEDP (2<<3)
+#define BT848_SCLOOP_HFILT_MAXP (3<<3)
+
+
 #define BT848_OFORM            0x048
 #define BT848_OFORM_RANGE      (1<<7)
 #define BT848_OFORM_CORE0      (0<<5)
@@ -213,6 +222,7 @@
 #define BT848_VBI_PACK_DEL_EXT_FRAME  2
 #define BT848_VBI_PACK_DEL_VBI_PKT_HI 1
 
+
 #define BT848_INT_STAT         0x100
 #define BT848_INT_MASK         0x104
 
@@ -317,5 +327,16 @@
 #define BT848_RISC_WRITE123    (0x09<<28)
 #define BT848_RISC_SKIP123     (0x0a<<28)
 #define BT848_RISC_WRITE1S23   (0x0b<<28)
+
+
+
+/* Bt848A and Bt849 only !! */
+#define BT848_TGLB             0x080
+#define BT848_TGCTRL           0x084
+#define BT848_FCAP             0x0E8
+#define BT848_PLL_F_LO         0x0F0
+#define BT848_PLL_F_HI         0x0F4
+#define BT848_PLL_XCI          0x0F8
+
 
 #endif
diff -u --recursive --new-file v2.1.104/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.1.104/linux/drivers/char/bttv.c	Sun Jun  7 11:16:29 1998
+++ linux/drivers/char/bttv.c	Sun Jun  7 10:37:41 1998
@@ -24,12 +24,15 @@
  
     TODO:  
    
-    * think of some good ioctls for Video4Linux to handle 
-      YUV, planar YUV, ...  grabs and sell them to AC :-)
     * move norm from tuner to channel struct!?
       composite source from a satellite tuner can deliver different norms
       depending on tuned channel
     * mmap VBI data?
+    * use new PCI routines
+    * fix RAW Composite grabbing for NTSC 
+    * allow for different VDELAY in RAW grabbing?
+    * extra modules for tda9850, tda8425, any volunteers???
+    * support 15bpp
 */
 
 #include <linux/module.h>
@@ -193,7 +196,7 @@
 static int fbuffer_alloc(struct bttv *btv)
 {
 	if(!btv->fbuffer)
-		btv->fbuffer=(unsigned char *) rvmalloc(2*0x144000);
+		btv->fbuffer=(unsigned char *) rvmalloc(2*BTTV_MAX_FBUF);
 	else
 		printk(KERN_ERR "bttv: Double alloc of fbuffer!\n");
 	if(!btv->fbuffer)
@@ -393,6 +396,42 @@
  
 /* ----------------------------------------------------------------------- */
 
+
+struct tvcard
+{
+        int inputs;
+        int tuner;
+        int svhs;
+        u32 gpiomask;
+        u32 muxsel[8];
+        u32 audiomux[6]; /* Tuner, Radio, internal, external, mute, stereo */
+};
+
+static struct tvcard tvcards[] = 
+{
+        /* default */
+        { 3, 0, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0}},
+        /* MIRO */
+        { 4, 0, 2,15, { 2, 3, 1, 1}, { 2, 0, 0, 0,10}},
+        /* Hauppauge */
+        { 3, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4}},
+	/* STB */
+        { 3, 0, 2, 7, { 2, 3, 1, 1}, { 4, 0, 2, 3, 1}},
+	/* Intel??? */
+        { 3, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4}},
+	/* Diamond DTV2000 */
+        { 3, 0, 2, 3, { 2, 3, 1, 1}, { 0, 1, 0, 1, 3}},
+	/* AVerMedia TVPhone */
+        { 3, 0, 2,15, { 2, 3, 1, 1}, {12, 0,11,11, 0}},
+        /* Matrix Vision MV-Delta */
+        { 5,-1, 4, 0, { 2, 3, 1, 0, 0}},
+        /* Fly Video II */
+        { 3, 0, 2, 0xc00, { 2, 3, 1, 1}, 
+        {0, 0xc00, 0x800, 0x400, 0xc00, 0}},
+};
+#define TVCARDS (sizeof(tvcards)/sizeof(tvcard))
+
+
 /*
  *	Tuner, Radio, internal, external and mute 
  */
@@ -404,17 +443,14 @@
 	{ 0x00, 0x01, 0x02, 0x03, 0x04}, /* Hauppauge */
 	{ 0x04, 0x00, 0x02, 0x03, 0x01}, /* STB */
 	{ 0x00, 0x01, 0x02, 0x03, 0x04}, /* Intel??? */
-	{ 0x00, 0x01, 0x02, 0x03, 0x04}, /* Diamond DTV2000 */
+	{ 0x00, 0x01, 0x00, 0x01, 0x03}, /* Diamond DTV2000 */
 	{ 0x0c, 0x00, 0x0b, 0x0b, 0x00}, /* AVerMedia TVPhone */
 };
 
 static void audio(struct bttv *btv, int mode)
 {
-        /* enable least significant GPIO output nibble */
-	btwrite(0x0f, BT848_GPIO_OUT_EN);
-
-        /* select direct input */
-	btwrite(0x00, BT848_GPIO_REG_INP);
+	btaor(tvcards[btv->type].gpiomask, ~tvcards[btv->type].gpiomask,
+              BT848_GPIO_OUT_EN);
 
 	switch (mode)
 	{
@@ -437,12 +473,17 @@
 			break;
 	}
         /* if audio mute or not in H-lock, turn audio off */
-	if ((btv->audio&AUDIO_MUTE) || 
-	    (!btv->radio && !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)))
+	if ((btv->audio&AUDIO_MUTE)
+#if 0	
+	 || 
+	    (!btv->radio && !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC))
+#endif	    
+		)
 	        mode=AUDIO_OFF;
         if ((mode == 0) && (btv->radio))
 		mode = 1;
-	btaor(audiomuxs[btv->type][mode] , ~0x0f, BT848_GPIO_DATA);
+	btaor(tvcards[btv->type].audiomux[mode],
+              ~tvcards[btv->type].gpiomask, BT848_GPIO_DATA);
 }
 
 
@@ -469,14 +510,77 @@
 	}
 }
 
-static void bt848_muxsel(struct bttv *btv, uint input)
+
+/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC*/
+
+static int set_pll(struct bttv *btv)
+{
+        int i;
+
+	if (!btv->pll)
+	       	return 0;
+        if ((btread(BT848_IFORM)&BT848_IFORM_XT0))
+        {
+                /* printk ("switching PLL off\n");*/
+                btwrite(0x00,BT848_TGCTRL);
+                btwrite(0x00,BT848_PLL_XCI);
+                btv->pll&=~2;
+                return 0;
+        }
+        
+        /* do not set pll again if already active */
+        if (btv->pll&2)
+                return 1;
+        
+        /* printk ("setting PLL for PAL/SECAM\n");*/
+
+        btwrite(0x00,BT848_TGCTRL);
+        btwrite(0xf9,BT848_PLL_F_LO);
+        btwrite(0xdc,BT848_PLL_F_HI);
+        btwrite(0x8e,BT848_PLL_XCI);
+
+	/* Ugh ugh ugh .. schedule ? */
+        udelay(100000);
+        for (i=0; i<100; i++) 
+        {
+                if ((btread(BT848_DSTATUS)&BT848_DSTATUS_PLOCK))
+                        btwrite(0,BT848_DSTATUS);
+                else
+                {
+                        btwrite(0x08,BT848_TGCTRL);
+                        btv->pll|=2;
+                        return 1;            
+                }
+                udelay(10000);
+        }
+        return -1;
+}
+
+static void bt848_muxsel(struct bttv *btv, unsigned int input)
 {
-	input&=3;
+	btwrite(tvcards[btv->type].gpiomask, BT848_GPIO_OUT_EN);
 
 	/* This seems to get rid of some synchronization problems */
 	btand(~(3<<5), BT848_IFORM);
 	mdelay(10); 
 
+       	input %= tvcards[btv->type].inputs;
+	if (input==tvcards[btv->type].svhs) 
+	{
+		btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
+		btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
+	}
+	else
+	{
+		btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+		btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
+	}
+	btaor((tvcards[btv->type].muxsel[input&7]&3)<<5, ~(3<<5), BT848_IFORM);
+	audio(btv, (input!=tvcards[btv->type].tuner) ? 
+              AUDIO_EXTERN : AUDIO_TUNER);
+	btaor(tvcards[btv->type].muxsel[input]>>4,
+              ~tvcards[btv->type].gpiomask, BT848_GPIO_DATA);
+#if 0
 	if (input==3) 
 	{
 		btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
@@ -491,6 +595,7 @@
 		input=3;
 	btaor(((input+2)&3)<<5, ~(3<<5), BT848_IFORM);
 	audio(btv, input ? AUDIO_EXTERN : AUDIO_TUNER);
+#endif
 }
 
 
@@ -540,8 +645,51 @@
         8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 0 
 };
 
-static int  make_vrisctab(struct bttv *btv, unsigned int *ro, unsigned int *re, 
-	unsigned int *vbuf, unsigned short width, unsigned short height, unsigned short fmt)
+int palette2fmt[] = {
+       0,
+       BT848_COLOR_FMT_Y8,
+       BT848_COLOR_FMT_RGB8,
+       BT848_COLOR_FMT_RGB16,
+       BT848_COLOR_FMT_RGB24,
+       BT848_COLOR_FMT_RGB32,
+       BT848_COLOR_FMT_RGB15,
+};
+
+static int  make_rawrisctab(struct bttv *btv, unsigned int *ro,
+                            unsigned int *re, unsigned int *vbuf)
+{
+        unsigned long line;
+	unsigned long bpl=1024;
+	unsigned long vadr=(unsigned long) vbuf;
+
+	*(ro++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(ro++)=0;
+	*(re++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(re++)=0;
+  
+        /* In PAL 650 blocks of 256 DWORDs are sampled, but only if VDELAY
+           is 2. We'll have to handle this inside the IRQ handler ... */
+
+	for (line=0; line < 640; line++)
+	{
+                *(ro++)=BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL;
+                *(ro++)=kvirt_to_bus(vadr);
+                *(re++)=BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL;
+                *(re++)=kvirt_to_bus(vadr+BTTV_MAX_FBUF/2);
+                vadr+=bpl;
+	}
+	
+	*(ro++)=BT848_RISC_JUMP;
+	*(ro++)=btv->bus_vbi_even;
+	*(re++)=BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16);
+	*(re++)=btv->bus_vbi_odd;
+	
+	return 0;
+}
+
+
+static int  make_vrisctab(struct bttv *btv, unsigned int *ro,
+                          unsigned int *re, 
+                          unsigned int *vbuf, unsigned short width,
+                          unsigned short height, unsigned short fmt)
 {
         unsigned long line;
 	unsigned long bpl;  /* bytes per line */
@@ -551,6 +699,9 @@
 	int inter;
 	unsigned long vadr=(unsigned long) vbuf;
 
+        if (btv->gfmt==BT848_COLOR_FMT_RAW) 
+                return make_rawrisctab(btv, ro, re, vbuf);
+        
 	inter = (height>btv->win.cropheight/2) ? 1 : 0;
 	bpl=width*fmtbppx2[fmt&0xf]/2;
 	
@@ -652,6 +803,151 @@
 	*x+=dx;
 }
 
+static void make_clip_tab(struct bttv *btv, struct cliprec *cr, int count)
+{
+        int i,ncr;
+	int yy, y, x, dx;
+	struct cliprec first, *cur, *cur2, *nx, first2, *prev, *nx2;
+	int bpp, bpl, width, height, inter;
+	unsigned int **rp,*ro,*re;
+	unsigned long adr;
+	int cx,cx2,cy,cy2;
+
+	inter=(btv->win.interlace&1)^1;
+	bpp=btv->win.bpp;
+	bpl=btv->win.bpl;
+	ncr=btv->ncr;
+	ro=btv->risc_odd;
+	re=btv->risc_even;
+	width=btv->win.width;
+	height=btv->win.height;
+	adr=btv->win.vidadr+btv->win.x*bpp+btv->win.y*bpl;
+
+	/* clip clipping rects against viewing window AND screen 
+	   so we do not have to rely on the user program
+	 */
+	cx=(btv->win.x<0) ? (-btv->win.x) : 0;
+	cy=(btv->win.y<0) ? (-btv->win.y) : 0;
+	cx2=(btv->win.x+width>btv->win.swidth) ? 
+	        (btv->win.swidth-btv->win.x) : width;
+	cy2=(btv->win.y+height>btv->win.sheight) ? 
+	        (btv->win.sheight-btv->win.y) : height;
+	first.next=NULL;
+	for (i=0; i<ncr; i++)
+	{
+                if (cr[i].y<cy)
+		{
+		        if (cr[i].y2<cy)
+			        continue;
+			cr[i].y=cy;
+		} 
+		if (cr[i].y2>=cy2) 
+		{
+		        if (cr[i].y>=cy2)
+			        continue;
+			cr[i].y2=cy2-1;
+		}
+                if (cr[i].x<cx)
+		{
+		        if (cr[i].x2<cx)
+			        continue;
+			cr[i].x=cx;
+		} 
+		if (cr[i].x2>=cx2) 
+		{
+		        if (cr[i].x>=cx2)
+			        continue;
+			cr[i].x2=cx2-1;
+		}
+	        cur=&first;
+		while ((nx=cur->next) && (cr[i].y > cur->next->y))
+		        cur=nx; 
+		cur->next=&(cr[i]);
+		cr[i].next=nx;
+	}
+	first2.next=NULL;
+	
+	*(ro++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(ro++)=0;
+	*(re++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(re++)=0;
+	
+	/* loop through all lines */
+	for (yy=0; yy<(height<<inter); yy++) 
+	{
+		y=yy>>inter;
+		rp= (yy&1) ? &re : &ro;
+	  
+		/* remove rects with y2 > y */
+		if ((cur=first2.next))
+		{
+			prev=&first2;
+			do
+                	{
+				if (cur->y2 < y) 
+					prev->next=cur->next;
+				else
+					prev=cur;
+			} 
+                	while ((cur=cur->next));
+		}
+
+		/* add rect to second (x-sorted) list if rect.y == y  */
+		if ((cur=first.next))
+		{
+		 	while ((cur) && (cur->y == y))
+			{ 
+			        first.next=cur->next;
+				cur2=&first2;
+				while ((nx2=cur2->next) && (cur->x > cur2->next->x)) 
+				        cur2=nx2; 
+				cur2->next=cur;
+				cur->next=nx2;
+				cur=first.next;
+			}
+		}
+        	x=0;
+		if ((btv->win.y+y<=0)||(btv->win.y+y>=btv->win.sheight))
+			write_risc_segment(rp, adr, BT848_RISC_SKIP, &x,
+ 					width, bpp, width);
+		else 
+		{
+			dx=cx;
+			for (cur2=first2.next; cur2; cur2=cur2->next)
+	       	        {
+				if (x+dx < cur2->x)
+				{
+					write_risc_segment(rp, adr, BT848_RISC_SKIP,
+						&x, dx, bpp, width);
+					dx=cur2->x-x;
+					write_risc_segment(rp, adr, BT848_RISC_WRITEC,
+						&x, dx, bpp, width);
+					dx=cur2->x2-x+1;
+				}
+				else if (x+dx < cur2->x2) 
+					dx=cur2->x2-x+1; 
+			}
+			if (cx2<width)
+			{
+				write_risc_segment(rp, adr, BT848_RISC_SKIP,
+					&x, dx, bpp, width);
+				write_risc_segment(rp, adr, BT848_RISC_WRITEC,
+					&x, cx2-x, bpp, width);
+				dx=width-x;
+			}
+			write_risc_segment(rp, adr, BT848_RISC_SKIP,
+				&x, dx, bpp, width);
+			write_risc_segment(rp, adr, BT848_RISC_WRITEC,
+				&x, width-x, bpp, width);
+		}
+		if ((!inter)||(yy&1))
+			adr+=bpl;
+	}
+	
+	*(ro++)=BT848_RISC_JUMP;
+	*(ro++)=btv->bus_vbi_even;
+	*(re++)=BT848_RISC_JUMP;
+	*(re++)=btv->bus_vbi_odd;
+}
+
 /*
  *	Set the registers for the size we have specified. Don't bother
  *	trying to understand this without the BT848 manual in front of
@@ -683,10 +979,16 @@
 	    944, 186, 922, 0x20},
 	/* PAL-M */
 	{ 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
-	    780, 186, 922, 0x16},
+	    780, 135, 754, 0x16},
 	/* PAL-N */
 	{ 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
 	    944, 186, 922, 0x20},
+	/* PAL-NC */
+	{ 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
+	    944, 186, 922, 0x20},
+	/* NTSC-Japan */
+	{ 640, 480,  910, 0x68, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
+	    780, 135, 754, 0x16},
 };
 #define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm))
 
@@ -709,7 +1011,7 @@
 	btwrite(vtc, BT848_E_VTC+off);
 	btwrite(hscale>>8, BT848_E_HSCALE_HI+off);
 	btwrite(hscale&0xff, BT848_E_HSCALE_LO+off);
-	btaor((vscale>>8), 0xc0, BT848_E_VSCALE_HI+off);
+	btaor((vscale>>8), 0xe0, BT848_E_VSCALE_HI+off);
 	btwrite(vscale&0xff, BT848_E_VSCALE_LO+off);
 	btwrite(hactive&0xff, BT848_E_HACTIVE_LO+off);
 	btwrite(hdelay&0xff, BT848_E_HDELAY_LO+off);
@@ -747,6 +1049,8 @@
 	btwrite(tvn->adelay, BT848_ADELAY);
 	btwrite(tvn->bdelay, BT848_BDELAY);
 	btaor(tvn->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), BT848_IFORM);
+	
+	set_pll(btv);
 
 	btwrite(fmt, BT848_COLOR_FMT);
 	hactive=width;
@@ -790,6 +1094,9 @@
         unsigned short format;
 	int bpp;
 
+        btv->win.color_fmt=format= (btv->win.depth==15) ? BT848_COLOR_FMT_RGB15 :
+		bpp2fmt[(btv->win.bpp-1)&3];
+#if 0
 	bpp=fmtbppx2[btv->win.color_fmt&0x0f]/2;
 	if (btv->win.bpp == 0) 
 	{
@@ -800,6 +1107,7 @@
 		btv->win.color_fmt=format=bpp2fmt[(btv->win.bpp-1)&3];
 	else
                 format=btv->win.color_fmt;
+#endif
                 	
 	/*	RGB8 seems to be a 9x5x5 GRB color cube starting at
 	 *	color 16. Why the h... can't they even mention this in the
@@ -807,11 +1115,12 @@
 	 *	it never occured them]
 	 *	Enable dithering in this mode
 	 */
+#if 0	 
 	if (format==BT848_COLOR_FMT_RGB8)
                 btand(~0x10, BT848_CAP_CTL); 
 	else
 	        btor(0x10, BT848_CAP_CTL);
-
+#endif
         bt848_set_geo(btv,btv->win.width, btv->win.height, format);
 }
 
@@ -876,6 +1185,8 @@
 		if(fbuffer_alloc(btv))
 			return -ENOBUFS;
 	}
+	if(btv->grabbing>1)
+		return -ENOBUFS;
 	
 	/*
 	 *	No grabbing past the end of the buffer!
@@ -887,7 +1198,7 @@
 	if(mp->height <0 || mp->width <0)
 		return -EINVAL;
 	
-	if(mp->height>480 || mp->width>640)
+	if(mp->height>576 || mp->width>768)
 		return -EINVAL;
 
 	/*
@@ -900,11 +1211,11 @@
 	 *	Ok load up the BT848
 	 */
 	 
-	vbuf=(unsigned int *)(btv->fbuffer+0x144000*mp->frame);
+	vbuf=(unsigned int *)(btv->fbuffer+BTTV_MAX_FBUF*mp->frame);
 	if (!(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC))
                 return -EAGAIN;
-	ro=btv->grisc+(((btv->grabcount++)&1) ? 2048 :0);
-	re=ro+1024;
+	ro=btv->grisc+(((btv->grabcount++)&1) ? 4096 :0);
+	re=ro+2048;
 	btv->gwidth=mp->width;
 	btv->gheight=mp->height;
 	btv->gfmt=mp->format;
@@ -912,8 +1223,13 @@
 	/* bt848_set_risc_jmps(btv); */
 	btor(3, BT848_CAP_CTL);
 	btor(3, BT848_GPIO_DMA_CTL);
-	btv->gro=virt_to_bus(ro);
-	btv->gre=virt_to_bus(re);
+        if (btv->grabbing) {
+		btv->gro_next=virt_to_bus(ro);
+		btv->gre_next=virt_to_bus(re);
+        } else {
+		btv->gro=virt_to_bus(ro);
+		btv->gre=virt_to_bus(re);
+        }
 	if (!(btv->grabbing++)) 
 		btv->risc_jmp[12]=BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ;
 	/* interruptible_sleep_on(&btv->capq); */
@@ -990,7 +1306,7 @@
 				find_vga();
 			btv->fbuffer=NULL;
 			if (!btv->fbuffer)
-				btv->fbuffer=(unsigned char *) rvmalloc(2*0x144000);
+				btv->fbuffer=(unsigned char *) rvmalloc(2*BTTV_MAX_FBUF);
 			if (!btv->fbuffer)
                         { 
 				btv->user--;
@@ -1014,7 +1330,7 @@
 	bt848_set_risc_jmps(btv);
 
 	if(btv->fbuffer)
-		rvfree((void *) btv->fbuffer, 2*0x144000);
+		rvfree((void *) btv->fbuffer, 2*BTTV_MAX_FBUF);
 	btv->fbuffer=0;
 	MOD_DEC_USE_COUNT;  
 }
@@ -1439,13 +1755,15 @@
 		case VIDIOCGPICT:
 		{
 			struct video_picture p=btv->picture;
-			if(btv->win.bpp==1)
+			if(btv->win.depth==8)
 				p.palette=VIDEO_PALETTE_HI240;
-			if(btv->win.bpp==2)
+			if(btv->win.depth==15)
+				p.palette=VIDEO_PALETTE_RGB555;
+			if(btv->win.depth==16)
 				p.palette=VIDEO_PALETTE_RGB565;
-			if(btv->win.bpp==3)
+			if(btv->win.depth==24)
 				p.palette=VIDEO_PALETTE_RGB24;
-			if(btv->win.bpp==4)
+			if(btv->win.depth==32)
 				p.palette=VIDEO_PALETTE_RGB32;
 			
 			if(copy_to_user(arg, &p, sizeof(p)))
@@ -1455,6 +1773,7 @@
 		case VIDIOCSPICT:
 		{
 			struct video_picture p;
+			int format;
 			if(copy_from_user(&p, arg,sizeof(p)))
 				return -EFAULT;
 			/* We want -128 to 127 we get 0-65535 */
@@ -1466,8 +1785,15 @@
 			bt848_hue(btv, (p.hue>>8)-128);
 			/* 0-511 */
 			bt848_contrast(btv, p.contrast>>7);
-			btv->picture=p;
-			return 0;
+			btv->picture = p;
+
+                        /* set palette if bpp matches */
+                        if (p.palette < sizeof(palette2fmt)/sizeof(int)) {
+                                format = palette2fmt[p.palette];
+                                if (fmtbppx2[format&0x0f]/2 == btv->win.bpp)
+                                        btv->win.color_fmt = format;
+                        }
+                	return 0;
 		}
 		case VIDIOCSWIN:
 		{
@@ -1571,7 +1897,7 @@
 			v.base=(void *)btv->win.vidadr;
 			v.height=btv->win.sheight;
 			v.width=btv->win.swidth;
-			v.depth=btv->win.bpp*8;
+			v.depth=btv->win.depth;
 			v.bytesperline=btv->win.bpl;
 			if(copy_to_user(arg, &v,sizeof(v)))
 				return -EFAULT;
@@ -1585,7 +1911,7 @@
 				return -EPERM;
 			if(copy_from_user(&v, arg,sizeof(v)))
 				return -EFAULT;
-			if(v.depth!=8 && v.depth!=16 && v.depth!=24 && v.depth!=32)
+			if(v.depth!=8 && v.depth!=15 && v.depth!=16 && v.depth!=24 && v.depth!=32)
 				return -EINVAL;
 			if (v.base) {
 			        /* also handle virtual base addresses */
@@ -1596,7 +1922,8 @@
 			}
 			btv->win.sheight=v.height;
 			btv->win.swidth=v.width;
-			btv->win.bpp=v.depth/8;
++			btv->win.bpp=((v.depth+1)&0x18)/8;
+			btv->win.depth=v.depth;
 			btv->win.bpl=v.bytesperline;
 			
 			DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n",
@@ -1635,13 +1962,21 @@
 			strcpy(v.name,"TV");
 			if (btv->have_msp3400) 
 			{
-				v.flags|=VIDEO_AUDIO_VOLUME;
-				i2c_control_device(&(btv->i2c),
-					I2C_DRIVERID_MSP3400,
-					MSP_GET_VOLUME,&(v.volume));
-				i2c_control_device(&(btv->i2c),
-					I2C_DRIVERID_MSP3400,
-					MSP_GET_STEREO,&(v.mode));
+                                v.flags|=VIDEO_AUDIO_VOLUME |
+                                	VIDEO_AUDIO_BASS |
+                                	VIDEO_AUDIO_TREBLE;
+                                i2c_control_device(&(btv->i2c),
+                                                I2C_DRIVERID_MSP3400,
+                                                MSP_GET_VOLUME,&(v.volume));
+                                i2c_control_device(&(btv->i2c),
+                                                I2C_DRIVERID_MSP3400,
+                                                MSP_GET_BASS,&(v.bass));
+                                i2c_control_device(&(btv->i2c),
+                                                I2C_DRIVERID_MSP3400,
+                                                MSP_GET_TREBLE,&(v.treble));
+                        	i2c_control_device(&(btv->i2c),
+                                		I2C_DRIVERID_MSP3400,
+                                                MSP_GET_STEREO,&(v.mode));
 			}
 			else v.mode = VIDEO_SOUND_MONO;
 			if(copy_to_user(arg,&v,sizeof(v)))
@@ -1655,28 +1990,37 @@
 				return -EFAULT;
 			if(v.flags&VIDEO_AUDIO_MUTE)
 				audio(btv, AUDIO_MUTE);
-			if(v.audio<0||v.audio>2)
+			/* One audio source per tuner */
+			if(v.audio!=0)
 				return -EINVAL;
 			bt848_muxsel(btv,v.audio);
 			if(!(v.flags&VIDEO_AUDIO_MUTE))
 				audio(btv, AUDIO_UNMUTE);
 			if (btv->have_msp3400) 
 			{
-				i2c_control_device(&(btv->i2c),
-					I2C_DRIVERID_MSP3400,
-					MSP_SET_VOLUME,&(v.volume));
-				i2c_control_device(&(btv->i2c),
-					I2C_DRIVERID_MSP3400,
-					MSP_SET_STEREO,&(v.mode));
+                                i2c_control_device(&(btv->i2c),
+                                                I2C_DRIVERID_MSP3400,
+                                                MSP_SET_VOLUME,&(v.volume));
+                                i2c_control_device(&(btv->i2c),
+                                                I2C_DRIVERID_MSP3400,
+                                                MSP_SET_BASS,&(v.bass));
+                                i2c_control_device(&(btv->i2c),
+                                                I2C_DRIVERID_MSP3400,
+                                                MSP_SET_TREBLE,&(v.treble));
+                        	i2c_control_device(&(btv->i2c),
+                                		I2C_DRIVERID_MSP3400,
+                                		MSP_SET_STEREO,&(v.mode));
 			}
 			btv->audio_dev=v;
 			return 0;
 		}
 
 	        case VIDIOCSYNC:
-		        if (btv->grabbing && btv->grab==btv->lastgrab)
+	        	if (!btv->grabbing)
+	        		return -EAGAIN;
+		        if (btv->grab==btv->lastgrab)
 			        interruptible_sleep_on(&btv->capq);
-		        btv->lastgrab=btv->grab;
+		        btv->lastgrab++;
 		        return 0;
 
 		case BTTV_WRITEE:
@@ -1728,7 +2072,7 @@
         unsigned long start=(unsigned long) adr;
 	unsigned long page,pos;
 
-	if (size>2*0x144000)
+	if (size>2*BTTV_MAX_FBUF)
 	        return -EINVAL;
 	if (!btv->fbuffer)
 	{
@@ -1960,18 +2304,20 @@
 };
 
 static struct vidbases vbs[] = {
-	{ PCI_VENDOR_ID_TSENG, 0, "TSENG", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL,
-		"Matrox Millennium", PCI_BASE_ADDRESS_1},
-	{ PCI_VENDOR_ID_MATROX, 0x051a, "Matrox Mystique", PCI_BASE_ADDRESS_1},
-	{ PCI_VENDOR_ID_S3, 0, "S3", PCI_BASE_ADDRESS_0},
 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_210888GX,
 		"ATI MACH64 Winturbo", PCI_BASE_ADDRESS_0},
+ 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_215GT,
+ 		"ATI MACH64 GT", PCI_BASE_ADDRESS_0},
 	{ PCI_VENDOR_ID_CIRRUS, 0, "Cirrus Logic", PCI_BASE_ADDRESS_0},
-	{ PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128, 
-		"Number Nine Imagine 128", PCI_BASE_ADDRESS_0},
 	{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA,
 		"DEC DC21030", PCI_BASE_ADDRESS_0},
+	{ PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL,
+		"Matrox Millennium", PCI_BASE_ADDRESS_1},
+	{ PCI_VENDOR_ID_MATROX, 0x051a, "Matrox Mystique", PCI_BASE_ADDRESS_1},
+	{ PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128, 
+		"Number Nine Imagine 128", PCI_BASE_ADDRESS_0},
+	{ PCI_VENDOR_ID_S3, 0, "S3", PCI_BASE_ADDRESS_0},
+	{ PCI_VENDOR_ID_TSENG, 0, "TSENG", PCI_BASE_ADDRESS_0},
 };
 
 
@@ -2159,6 +2505,15 @@
 	}
 }
 
+static void init_tda8425(struct i2c_bus *bus) 
+{
+        I2CWrite(bus, I2C_TDA8425, TDA8425_VL, 0xFC, 1); /* volume left 0dB  */
+        I2CWrite(bus, I2C_TDA8425, TDA8425_VR, 0xFC, 1); /* volume right 0dB */
+        I2CWrite(bus, I2C_TDA8425, TDA8425_BA, 0xF6, 1); /* bass 0dB         */
+        I2CWrite(bus, I2C_TDA8425, TDA8425_TR, 0xF6, 1); /* treble 0dB       */
+	I2CWrite(bus, I2C_TDA8425, TDA8425_S1, 0xCE, 1); /* mute off         */
+}
+
 
 static void init_tda9850(struct i2c_bus *bus)
 {
@@ -2198,10 +2553,27 @@
 			        btv->type=BTTV_STB;
 	}
 
-	btv->dbx = I2CRead(&(btv->i2c), I2C_TDA9850) ? 0 : 1;
+        if (I2CRead(&(btv->i2c), I2C_TDA9850) >=0)
+        {
+        	btv->audio_chip = TDA9850;
+        	printk(KERN_INFO "bttv: audio chip: TDA9850\n");
+        }
 
-	if (btv->dbx)
-		init_tda9850(&(btv->i2c));
+        if (I2CRead(&(btv->i2c), I2C_TDA8425) >=0)
+        {
+            btv->audio_chip = TDA8425;
+            printk("bttv: audio chip: TDA8425\n");
+        }
+        
+        switch(btv->audio_chip)
+        {
+                case TDA9850:
+                        init_tda9850(&(btv->i2c));
+                        break;
+		case TDA8425:
+                        init_tda8425(&(btv->i2c));
+                        break;
+        }
 
 	/* How do I detect the tuner type for other cards but Miro ??? */
 	printk(KERN_INFO "bttv: model: ");
@@ -2238,6 +2610,10 @@
 			printk("AVerMedia\n");
 			strcpy(btv->video_dev.name,"BT848(AVerMedia)");
 			break;
+		case BTTV_MATRIX_VISION: 
+			printk("MATRIX-Vision\n");
+			strcpy(btv->video_dev.name,"BT848(MATRIX-Vision)");
+			break;
 	}
 	audio(btv, AUDIO_MUTE);
 }
@@ -2321,6 +2697,7 @@
 	btv->win.cropx=0;
 	btv->win.cropy=0;
 	btv->win.bpp=2;
+	btv->win.depth=16;
 	btv->win.color_fmt=BT848_COLOR_FMT_RGB16;
 	btv->win.bpl=1024*btv->win.bpp;
 	btv->win.swidth=1024;
@@ -2359,7 +2736,7 @@
 	btv->vbibuf=(unsigned char *) vmalloc(VBIBUF_SIZE);
 	if (!btv->vbibuf) 
 		return -1;
-	if (!(btv->grisc=(unsigned int *) kmalloc(16384, GFP_KERNEL)))
+	if (!(btv->grisc=(unsigned int *) kmalloc(32768, GFP_KERNEL)))
 		return -1;
 
 	btv->fbuffer=NULL;
@@ -2372,7 +2749,11 @@
 	btwrite(0x00, BT848_CAP_CTL);
 	btwrite(0xfc, BT848_GPIO_DMA_CTL);
 
-	btwrite(0x0ff, BT848_VBI_PACK_SIZE);
+        /* select direct input */
+	btwrite(0x00, BT848_GPIO_REG_INP);
+
+
+	btwrite(0xff, BT848_VBI_PACK_SIZE);
 	btwrite(1, BT848_VBI_PACK_DEL);
 
 		
@@ -2502,10 +2883,11 @@
 			/* captured full frame */
 			if (stat&(2<<28)) 
 			{
-			        btv->grab++;
 				wake_up_interruptible(&btv->capq);
 			        if ((--btv->grabbing))
 				{
+					btv->gro = btv->gro_next;
+					btv->gre = btv->gre_next;
                                         btv->risc_jmp[5]=btv->gro;
 					btv->risc_jmp[11]=btv->gre;
 					bt848_set_geo(btv, btv->gwidth,
@@ -2517,6 +2899,7 @@
 						      btv->win.height,
 						      btv->win.color_fmt);
 				}
+				wake_up_interruptible(&btv->capq);
 				break;
 			}
 			if (stat&(8<<28)) 
@@ -2647,12 +3030,21 @@
 		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_CLASS_REVISION,
 			     &btv->revision);
 		printk(KERN_INFO "bttv: Brooktree Bt%d (rev %d) ",
-		       btv->id, btv->revision);
+			btv->id, btv->revision);
 		printk("bus: %d, devfn: %d, ",
 			btv->bus, btv->devfn);
 		printk("irq: %d, ",btv->irq);
 		printk("memory: 0x%08x.\n", btv->bt848_adr);
     
+    		btv->pll = 0;
+#ifdef USE_PLL
+                if (btv->id==849 || (btv->id==848 && btv->revision==0x12))
+                {
+                        printk(KERN_INFO "bttv: internal PLL, single crystal operation enabled.\n");
+                        btv->pll=1;
+                }
+#endif
+                
 		btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000);
 
 		result = request_irq(btv->irq, bttv_irq,
diff -u --recursive --new-file v2.1.104/linux/drivers/char/bttv.h linux/drivers/char/bttv.h
--- v2.1.104/linux/drivers/char/bttv.h	Wed Apr  8 19:36:25 1998
+++ linux/drivers/char/bttv.h	Sun Jun  7 10:37:41 1998
@@ -33,14 +33,7 @@
 
 #define MAX_CLIPRECS	100
 #define RISCMEM_LEN	(32744*2)
-#define MAX_FBUF	0x144000
-
-struct riscprog 
-{
-	unsigned int length;  
-	u32 *busadr;
-	u32 *prog;
-};
+#define BTTV_MAX_FBUF	0x144000
 
 
 /* clipping rectangle */
@@ -51,29 +44,6 @@
 };
 
 
-/* grab buffer */
-struct gbuffer 
-{
-	struct gbuffer *next;
-	struct gbuffer *next_active;
-	void *adr;
-	int x, y;
-	int width, height;
-	unsigned int bpl;
-	unsigned int fmt;
-	int flags;
-#define GBUF_ODD  1
-#define GBUF_EVEN 2
-#define GBUF_LFB  4
-#define GBUF_INT  8
-	unsigned int length;
-	void *ro;
-	void *re;
-	u32 bro;
-	u32 bre;
-};
-
-
 #ifdef __KERNEL__
 
 struct bttv_window 
@@ -89,6 +59,7 @@
 	int norm;
 	int interlace;
 	int color_fmt;
+	ushort depth;
 };
 
 
@@ -119,7 +90,7 @@
 	int type;            /* card type  */
 	int audio;           /* audio mode */
 	int user;
-	int dbx;
+	int audio_chip;
 	int radio;
 
 	u32 *risc_jmp;
@@ -145,12 +116,15 @@
 	u32 *grisc;
 	unsigned long gro;
 	unsigned long gre;
+	unsigned long gro_next;
+	unsigned long gre_next;
 	char *fbuffer;
 	int gmode;
 	int grabbing;
 	int lastgrab;
 	int grab;
 	int grabcount;
+	int pll;
 };
 
 #endif
@@ -184,6 +158,8 @@
 #define BTTV_INTEL         0x04
 #define BTTV_DIAMOND       0x05 
 #define BTTV_AVERMEDIA     0x06 
+#define BTTV_MATRIX_VISION 0x07 
+#define BTTV_FLYVIDEO      0x08
 
 #define AUDIO_TUNER        0x00
 #define AUDIO_RADIO        0x01
@@ -194,8 +170,12 @@
 #define AUDIO_MUTE         0x80
 #define AUDIO_UNMUTE       0x81
 
+#define TDA9850            0x01
+#define TDA8425            0x02
+
 #define I2C_TSA5522        0xc2
 #define I2C_TDA9850        0xb6
+#define I2C_TDA8425        0x82
 #define I2C_HAUPEE         0xa0
 #define I2C_STBEE          0xae
 
@@ -206,5 +186,13 @@
 #define TDA9850_ALI1       0x08
 #define TDA9850_ALI2       0x09
 #define TDA9850_ALI3       0x0a
+
+
+#define TDA8425_VL         0x00
+#define TDA8425_VR         0x01
+#define TDA8425_BA         0x02
+#define TDA8425_TR         0x03
+#define TDA8425_S1         0x08
+ 
 
 #endif
diff -u --recursive --new-file v2.1.104/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v2.1.104/linux/drivers/char/lp.c	Sun Jun  7 11:16:30 1998
+++ linux/drivers/char/lp.c	Sat Jun  6 19:17:55 1998
@@ -112,7 +112,7 @@
 #ifdef LP_STATS
 			   0, 0, {0},
 #endif
-			   NULL, 0, 0}
+			   NULL, 0, 0, 0}
 };
 
 /* Test if printer is ready (and optionally has no error conditions) */
diff -u --recursive --new-file v2.1.104/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v2.1.104/linux/drivers/char/mem.c	Sun Jun  7 11:16:30 1998
+++ linux/drivers/char/mem.c	Sun Jun  7 10:37:41 1998
@@ -536,7 +536,10 @@
 	misc_init();
 #endif
 #ifdef CONFIG_SOUND
+	soundcore_init();
+#ifdef CONFIG_SOUND_OSS	
 	soundcard_init();
+#endif	
 #endif
 #ifdef CONFIG_JOYSTICK
 	/*
diff -u --recursive --new-file v2.1.104/linux/drivers/char/msp3400.c linux/drivers/char/msp3400.c
--- v2.1.104/linux/drivers/char/msp3400.c	Sun Jun  7 11:16:30 1998
+++ linux/drivers/char/msp3400.c	Sun Jun  7 10:37:41 1998
@@ -48,23 +48,39 @@
 
 #include "msp3400.h"
 
+
+/* sound mixer stuff */ 
+#include <linux/config.h>
+
+#if LINUX_VERSION_CODE > 0x020140 /* need modular sound driver */
+# if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE)
+#  define REGISTER_MIXER 1
+# endif
+#endif
+
+
 int debug = 0; /* insmod parameter */
 
-struct msp3400c 
-{
+struct msp3400c {
 	struct i2c_bus     *bus;
 
 	int nicam;
 	int mode;
 	int norm;
-	int volume;
 	int stereo;
 
+	int mixer;
+	int left, right; /* volume */
+	int bass, treble;
+
 	/* thread */
-	struct task_struct	*thread;
-	struct semaphore	*wait;
-	struct semaphore	*notify;
-	int active,restart,rmmod;
+	struct task_struct  *thread;
+	struct semaphore    *wait;
+	struct semaphore    *notify;
+	int                  active,restart,rmmod;
+
+	int                  watch_stereo;
+	struct timer_list    wake_stereo;
 };
 
 #define VIDEO_MODE_RADIO 16      /* norm magic for radio mode */
@@ -98,10 +114,9 @@
 	mdelay(2);
 	i2c_start(bus);
 	if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) ||
-		0 != i2c_sendbyte(bus, 0x00,0) ||
-		0 != i2c_sendbyte(bus, 0x00,0) ||
-		0 != i2c_sendbyte(bus, 0x00,0)) 
-	{
+	    0 != i2c_sendbyte(bus, 0x00,0) ||
+	    0 != i2c_sendbyte(bus, 0x00,0) ||
+	    0 != i2c_sendbyte(bus, 0x00,0)) {
 		ret = -1;
 		printk(KERN_ERR "msp3400: chip reset failed, penguin on i2c bus?\n");
 	}
@@ -110,60 +125,52 @@
 	return ret;
 }
 
-static int msp3400c_read(struct i2c_bus *bus, int dev, int addr)
+static int
+msp3400c_read(struct i2c_bus *bus, int dev, int addr)
 {
 	int ret=0;
 	short val = 0;
 	i2c_start(bus);
 	if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) ||
-		0 != i2c_sendbyte(bus, dev+1,       0)    ||
-		0 != i2c_sendbyte(bus, addr >> 8,   0)    ||
-		0 != i2c_sendbyte(bus, addr & 0xff, 0)) 
-	{
+	    0 != i2c_sendbyte(bus, dev+1,       0)    ||
+	    0 != i2c_sendbyte(bus, addr >> 8,   0)    ||
+	    0 != i2c_sendbyte(bus, addr & 0xff, 0)) {
 		ret = -1;
-	}
-	else
-	{
+	} else {
 		i2c_start(bus);
-		if (0 != i2c_sendbyte(bus, I2C_MSP3400C+1,2000)) 
-		{
+		if (0 != i2c_sendbyte(bus, I2C_MSP3400C+1,2000)) {
 			ret = -1;
-		}
-		else
-		{
+		} else {
 			val |= (int)i2c_readbyte(bus,0) << 8;
 			val |= (int)i2c_readbyte(bus,1);
 		}
 	}
 	i2c_stop(bus);
-	if (-1 == ret) 
-	{
+	if (-1 == ret) {
 		printk(KERN_WARNING "msp3400: I/O error, trying reset (read %s 0x%x)\n",
-			(dev == I2C_MSP3400C_DEM) ? "Demod" : "Audio", addr);
+		       (dev == I2C_MSP3400C_DEM) ? "Demod" : "Audio", addr);
 		msp3400c_reset(bus);
 	}
 	return val;
 }
 
-static int msp3400c_write(struct i2c_bus *bus, int dev, int addr, int val)
+static int
+msp3400c_write(struct i2c_bus *bus, int dev, int addr, int val)
 {
 	int ret = 0;
     
 	i2c_start(bus);
 	if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) ||
-		0 != i2c_sendbyte(bus, dev,         0)    ||
-		0 != i2c_sendbyte(bus, addr >> 8,   0)    ||
-		0 != i2c_sendbyte(bus, addr & 0xff, 0)    ||
-		0 != i2c_sendbyte(bus, val >> 8,    0)    ||
-		0 != i2c_sendbyte(bus, val & 0xff,  0))
-	{
+	    0 != i2c_sendbyte(bus, dev,         0)    ||
+	    0 != i2c_sendbyte(bus, addr >> 8,   0)    ||
+	    0 != i2c_sendbyte(bus, addr & 0xff, 0)    ||
+	    0 != i2c_sendbyte(bus, val >> 8,    0)    ||
+	    0 != i2c_sendbyte(bus, val & 0xff,  0))
 		ret = -1;
-	}
 	i2c_stop(bus);
-	if (-1 == ret) 
-	{
-		printk(KERN_ERR "msp3400: I/O error, trying reset (write %s 0x%x)\n",
-			(dev == I2C_MSP3400C_DEM) ? "Demod" : "Audio", addr);
+	if (-1 == ret) {
+		printk(KERN_WARNING "msp3400: I/O error, trying reset (write %s 0x%x)\n",
+		       (dev == I2C_MSP3400C_DEM) ? "Demod" : "Audio", addr);
 		msp3400c_reset(bus);
 	}
 	return ret;
@@ -182,8 +189,7 @@
 #define MSP_MODE_FM_NICAM1   5
 #define MSP_MODE_FM_NICAM2   6
 
-static struct MSP_INIT_DATA_DEM 
-{
+static struct MSP_INIT_DATA_DEM {
 	int fir1[6];
 	int fir2[6];
 	int cdo1;
@@ -193,43 +199,48 @@
 	int dfp_src;
 	int dfp_matrix;
 } msp_init_data[] = {
-		/* AM (for carrier detect / msp3400) */
-		{ { 75, 19, 36, 35, 39, 40 }, { 75, 19, 36, 35, 39, 40 },
-		MSP_CARRIER(5.5), MSP_CARRIER(5.5),  0x00d0, 0x0500,   0x0020, 0x3000},
-
-		/* AM (for carrier detect / msp3410) */
-		{ { -1, -1, -8, 2, 59, 126 }, { -1, -1, -8, 2, 59, 126 },
-		MSP_CARRIER(5.5), MSP_CARRIER(5.5),  0x00d0, 0x0100,   0x0020, 0x3000},
-
-		/* FM Radio */
-		{ { -8, -8, 4, 6, 78, 107 }, { -8, -8, 4, 6, 78, 107 },
-		MSP_CARRIER(10.7), MSP_CARRIER(10.7),  0x00d0, 0x0480, 0x0020, 0x3002 },
-
-		/* Terrestial FM-mono */
-		{ {  3, 18, 27, 48, 66, 72 }, {  3, 18, 27, 48, 66, 72 },
-		MSP_CARRIER(5.5), MSP_CARRIER(5.5),  0x00d0, 0x0480,   0x0030, 0x3000},
-
-		/* Sat FM-mono */
-		{ {  1,  9, 14, 24, 33, 37 }, {  3, 18, 27, 48, 66, 72 },
-		MSP_CARRIER(6.5), MSP_CARRIER(6.5),  0x00c6, 0x0480,   0x0000, 0x3000},
-
-		/* NICAM B/G, D/K */
-		{ { -2, -8, -10, 10, 50, 86 }, {  3, 18, 27, 48, 66, 72 },
-		MSP_CARRIER(5.5), MSP_CARRIER(5.5),  0x00d0, 0x0040,   0x0120, 0x3000},
-
-		/* NICAM I */
-		{ {  2, 4, -6, -4, 40, 94 }, {  3, 18, 27, 48, 66, 72 },
-		MSP_CARRIER(5.5), MSP_CARRIER(5.5),  0x00d0, 0x0040,   0x0120, 0x3000},
+	/* AM (for carrier detect / msp3400) */
+	{ { 75, 19, 36, 35, 39, 40 }, { 75, 19, 36, 35, 39, 40 },
+	  MSP_CARRIER(5.5), MSP_CARRIER(5.5),
+	  0x00d0, 0x0500,   0x0020, 0x3000},
+
+	/* AM (for carrier detect / msp3410) */
+	{ { -1, -1, -8, 2, 59, 126 }, { -1, -1, -8, 2, 59, 126 },
+	  MSP_CARRIER(5.5), MSP_CARRIER(5.5),
+	  0x00d0, 0x0100,   0x0020, 0x3000},
+
+	/* FM Radio */
+	{ { -8, -8, 4, 6, 78, 107 }, { -8, -8, 4, 6, 78, 107 },
+	  MSP_CARRIER(10.7), MSP_CARRIER(10.7),
+	  0x00d0, 0x0480, 0x0020, 0x3002 },
+
+	/* Terrestial FM-mono */
+	{ {  3, 18, 27, 48, 66, 72 }, {  3, 18, 27, 48, 66, 72 },
+	  MSP_CARRIER(5.5), MSP_CARRIER(5.5),
+	  0x00d0, 0x0480,   0x0030, 0x3000},
+
+	/* Sat FM-mono */
+	{ {  1,  9, 14, 24, 33, 37 }, {  3, 18, 27, 48, 66, 72 },
+	  MSP_CARRIER(6.5), MSP_CARRIER(6.5),
+	  0x00c6, 0x0480,   0x0000, 0x3000},
+
+	/* NICAM B/G, D/K */
+	{ { -2, -8, -10, 10, 50, 86 }, {  3, 18, 27, 48, 66, 72 },
+	  MSP_CARRIER(5.5), MSP_CARRIER(5.5),
+	  0x00d0, 0x0040,   0x0120, 0x3000},
+
+	/* NICAM I */
+	{ {  2, 4, -6, -4, 40, 94 }, {  3, 18, 27, 48, 66, 72 },
+	  MSP_CARRIER(5.5), MSP_CARRIER(5.5),
+	  0x00d0, 0x0040,   0x0120, 0x3000},
 };
 
-struct CARRIER_DETECT 
-{
+struct CARRIER_DETECT {
 	int   cdo;
 	char *name;
 };
 
-static struct CARRIER_DETECT carrier_detect_main[] = 
-{
+static struct CARRIER_DETECT carrier_detect_main[] = {
 	/* main carrier */
 	{ MSP_CARRIER(4.5),        "4.5   NTSC"                   }, 
 	{ MSP_CARRIER(5.5),        "5.5   PAL B/G"                }, 
@@ -262,15 +273,39 @@
 	msp3400c_write(bus,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12);
 }
 
-static void msp3400c_setvolume(struct i2c_bus *bus, int vol)
+static void msp3400c_setvolume(struct i2c_bus *bus, int left, int right)
 {
-	int val = (vol * 0x73 / 65535) << 8;
+	int vol,val,balance;
 
-	dprintk("msp3400: setvolume: 0x%02x\n",val>>8);
+	vol     = (left > right) ? left : right;
+	val     = (vol * 0x73 / 65535) << 8;
+	balance = 0;
+	if (vol > 0)
+		balance = ((right-left) * 127) / vol;
+
+	dprintk("msp3400: setvolume: %d:%d 0x%02x 0x%02x\n",
+		left,right,val>>8,balance);
 	msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */
 	msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0006, val); /* headphones  */
 	/* scart - on/off only */
 	msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0);
+	msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0001, balance << 8);
+}
+
+static void msp3400c_setbass(struct i2c_bus *bus, int bass)
+{
+	int val = ((bass-32768) * 0x60 / 65535) << 8;
+
+	dprintk("msp3400: setbass: %d 0x%02x\n",bass, val>>8);
+	msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0002, val); /* loudspeaker */
+}
+
+static void msp3400c_settreble(struct i2c_bus *bus, int treble)
+{
+	int val = ((treble-32768) * 0x60 / 65535) << 8;
+
+	dprintk("msp3400: settreble: %d 0x%02x\n",treble, val>>8);
+	msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0003, val); /* loudspeaker */
 }
 
 static void msp3400c_setmode(struct msp3400c *msp, int type)
@@ -282,38 +317,37 @@
 	msp->stereo = VIDEO_SOUND_MONO;
 
 	msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x00bb,          /* ad_cv */
-		msp_init_data[type].ad_cv);
+		       msp_init_data[type].ad_cv);
     
 	for (i = 5; i >= 0; i--)                                   /* fir 1 */
 		msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0001,
-			msp_init_data[type].fir1[i]);
+			       msp_init_data[type].fir1[i]);
     
 	msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0004); /* fir 2 */
 	msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0040);
 	msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0000);
 	for (i = 5; i >= 0; i--)
 		msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005,
-			msp_init_data[type].fir2[i]);
+			       msp_init_data[type].fir2[i]);
     
 	msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0083,          /* MODE_REG */
-		msp_init_data[type].mode_reg);
+		       msp_init_data[type].mode_reg);
     
 	msp3400c_setcarrier(msp->bus, msp_init_data[type].cdo1,
-		msp_init_data[type].cdo2);
+			    msp_init_data[type].cdo2);
     
 	msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008,
-		msp_init_data[type].dfp_src);
+		       msp_init_data[type].dfp_src);
 	msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009,
-		msp_init_data[type].dfp_src);
+		       msp_init_data[type].dfp_src);
 	msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a,
-		msp_init_data[type].dfp_src);
+		       msp_init_data[type].dfp_src);
 	msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e,
-		msp_init_data[type].dfp_matrix);
+		       msp_init_data[type].dfp_matrix);
 
-	if (msp->nicam) 
-	{
+	if (msp->nicam) {
 		/* msp3410 needs some more initialization */
-        	msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0010, 0x3000);
+		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0010, 0x3000);
 	}
 }
 
@@ -322,86 +356,81 @@
 	int nicam=0; /* channel source: FM/AM or nicam */
 
 	/* switch demodulator */
-	switch (msp->mode) 
-	{
-		case MSP_MODE_FM_TERRA:
-			dprintk("msp3400: B/G setstereo: %d\n",mode);
-			msp->stereo = mode;
-			msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.7421875),MSP_CARRIER(5.5));
-			switch (mode) 
-			{
-				case VIDEO_SOUND_STEREO:
-					msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3001);
-					break;
-				case VIDEO_SOUND_MONO:
-				case VIDEO_SOUND_LANG1:
-				case VIDEO_SOUND_LANG2:
-					msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3000);
-					break;
-			}
+	switch (msp->mode) {
+	case MSP_MODE_FM_TERRA:
+		dprintk("msp3400: B/G setstereo: %d\n",mode);
+		msp->stereo = mode;
+		msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.7421875),MSP_CARRIER(5.5));
+		switch (mode) {
+		case VIDEO_SOUND_STEREO:
+			msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3001);
 			break;
-		case MSP_MODE_FM_SAT:
-			dprintk("msp3400: sat setstereo: %d\n",mode);
-			msp->stereo = mode;
-			switch (mode) 
-			{
-				case VIDEO_SOUND_MONO:
-					msp3400c_setcarrier(msp->bus, MSP_CARRIER(6.5), MSP_CARRIER(6.5));
-					break;
-				case VIDEO_SOUND_STEREO:
-					msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.2), MSP_CARRIER(7.02));
-					break;
-				case VIDEO_SOUND_LANG1:
-					msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02));
-					break;
-				case VIDEO_SOUND_LANG2:
-					msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02));
-					break;
-			}
+		case VIDEO_SOUND_MONO:
+		case VIDEO_SOUND_LANG1:
+		case VIDEO_SOUND_LANG2:
+			msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3000);
 			break;
-		case MSP_MODE_FM_NICAM1:
-			dprintk("msp3400: NICAM1 setstereo: %d\n",mode);
-			msp->stereo = mode;
-			msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.85),MSP_CARRIER(5.5));
-			nicam=0x0100;
+		}
+		break;
+	case MSP_MODE_FM_SAT:
+		dprintk("msp3400: sat setstereo: %d\n",mode);
+		msp->stereo = mode;
+		switch (mode) {
+		case VIDEO_SOUND_MONO:
+			msp3400c_setcarrier(msp->bus, MSP_CARRIER(6.5), MSP_CARRIER(6.5));
 			break;
-		default:
-			/* can't do stereo - abort here */
-			return;
-	}
-
-	/* switch audio */
-	switch (mode) 
-	{
 		case VIDEO_SOUND_STEREO:
-			msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008, 0x0020|nicam);
-			msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009, 0x0020|nicam);
-			msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a, 0x0020|nicam);
-			msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0005, 0x4000);
+			msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.2), MSP_CARRIER(7.02));
 			break;
-		case VIDEO_SOUND_MONO:
 		case VIDEO_SOUND_LANG1:
-			msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008, 0x0000|nicam);
-			msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009, 0x0000|nicam);
-			msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a, 0x0000|nicam);
+			msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02));
 			break;
 		case VIDEO_SOUND_LANG2:
-			msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008, 0x0010|nicam);
-			msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009, 0x0010|nicam);
-			msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a, 0x0010|nicam);
+			msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02));
 			break;
+		}
+		break;
+	case MSP_MODE_FM_NICAM1:
+		dprintk("msp3400: NICAM1 setstereo: %d\n",mode);
+		msp->stereo = mode;
+		msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.85),MSP_CARRIER(5.5));
+		nicam=0x0100;
+		break;
+	default:
+		/* can't do stereo - abort here */
+		return;
+	}
+
+	/* switch audio */
+	switch (mode) {
+	case VIDEO_SOUND_STEREO:
+		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008,0x0020|nicam);
+		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009,0x0020|nicam);
+		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a,0x0020|nicam);
+		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0005,0x4000);
+		break;
+	case VIDEO_SOUND_MONO:
+	case VIDEO_SOUND_LANG1:
+		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008,0x0000|nicam);
+		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009,0x0000|nicam);
+		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a,0x0000|nicam);
+		break;
+	case VIDEO_SOUND_LANG2:
+		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008,0x0010|nicam);
+		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009,0x0010|nicam);
+		msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a,0x0010|nicam);
+		break;
 	}
 }
 
 /* ----------------------------------------------------------------------- */
 
 struct REGISTER_DUMP {
-	int addr;
+	int   addr;
 	char *name;
 };
 
-struct REGISTER_DUMP d1[] = 
-{
+struct REGISTER_DUMP d1[] = {
 	{ 0x007e, "autodetect" },
 	{ 0x0023, "C_AD_BITS " },
 	{ 0x0038, "ADD_BITS  " },
@@ -414,15 +443,23 @@
  * in the ioctl while doing the sound carrier & stereo detect
  */
 
-int msp3400c_thread(void *data)
+static void msp3400c_stereo_wake(unsigned long data)
+{
+	struct msp3400c *msp = (struct msp3400c*)data;   /* XXX alpha ??? */
+
+	if (!msp->active)
+		up(msp->wait);    
+}
+
+static int msp3400c_thread(void *data)
 {
 	unsigned long flags;
 	struct msp3400c *msp = data;
 	struct semaphore sem = MUTEX_LOCKED;
     
 	struct CARRIER_DETECT *cd;
-	int count, max1,max2,val1,val2, val,this, check_stereo;
-	int i;
+	int                   count, max1,max2,val1,val2, val,this;
+	int                   newstereo;
     
 	/* lock_kernel(); */
     
@@ -442,57 +479,86 @@
 	if(msp->notify != NULL)
 		up(msp->notify);
 		
-	for (;;) 
-	{
+	for (;;) {
 		if (msp->rmmod)
 			goto done;
 		dprintk("msp3400: thread: sleep\n");
 		down_interruptible(&sem);
 		dprintk("msp3400: thread: wakeup\n");
-		if (msp->rmmod)
+		if (msp->rmmod || signal_pending(current))
 			goto done;
-#if 0
-		if (VIDEO_MODE_RADIO == msp->norm) 
-		{
-			msp->active = 1;
-			current->state = TASK_INTERRUPTIBLE;
-			current->timeout = jiffies + HZ/10;
-			schedule();
-			if (signal_pending(current))
-				goto done;
+
+		if (VIDEO_MODE_RADIO == msp->norm)
+			continue;  /* nothing to do */
+	
+		msp->active = 1;
+
+		if (msp->watch_stereo) {
+			/* do that stereo/multilang handling */
 			LOCK_I2C_BUS(msp->bus);
-			val1 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b);
-			val2 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c);
+			newstereo = msp->stereo;
+			switch (msp->mode) {
+			case MSP_MODE_FM_TERRA:
+				val = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x18);
+				dprintk("msp3400: stereo detect register: %d\n",val);
+		
+				if (val > 4096) {
+					newstereo = VIDEO_SOUND_STEREO;
+				} else if (val < -4096) {
+					newstereo = VIDEO_SOUND_LANG1;
+				} else {
+					newstereo = VIDEO_SOUND_MONO;
+				}
+				break;
+			case MSP_MODE_FM_NICAM1:
+				val = msp3400c_read(msp->bus, I2C_MSP3400C_DEM, 0x23);
+				switch ((val & 0x1e) >> 1)  {
+				case 0:
+				case 8:
+					newstereo = VIDEO_SOUND_STEREO;
+					break;
+				default:
+					newstereo = VIDEO_SOUND_MONO;
+					break;
+				}
+				break;
+			}
+			if (msp->stereo != newstereo) {
+				dprintk("msp3400: watch: stereo %d ==> %d\n",
+					msp->stereo,newstereo);
+				msp3400c_setstereo(msp,newstereo);
+			}
 			UNLOCK_I2C_BUS(msp->bus);
-			printk("msp3400: DC %d/%d\n",val1,val2);
+			if (msp->watch_stereo) {
+				del_timer(&msp->wake_stereo);
+				msp->wake_stereo.expires = jiffies + 5*HZ;
+				add_timer(&msp->wake_stereo);
+			}
+
 			msp->active = 0;
 			continue;
 		}
-#endif
 	
-		if (VIDEO_MODE_RADIO == msp->norm)
-			continue;  /* nothing to do */
-	
-		msp->active = 1;
-restart:
+	restart:
 		LOCK_I2C_BUS(msp->bus);
-		msp3400c_setvolume(msp->bus, 0);
+		msp3400c_setvolume(msp->bus, 0, 0);
 		msp3400c_setmode(msp, MSP_MODE_AM_DETECT);
-		val1 = val2 = max1 = max2 = check_stereo = 0;
+		val1 = val2 = max1 = max2 = 0;
+		del_timer(&msp->wake_stereo);
+		msp->watch_stereo = 0;
 
 		/* carrier detect pass #1 -- main carrier */
 		cd = carrier_detect_main; count = CARRIER_COUNT(carrier_detect_main);
-		for (this = 0; this < count; this++) 
-		{
+		for (this = 0; this < count; this++) {
 			msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo);
 			UNLOCK_I2C_BUS(msp->bus);
+
 			current->state = TASK_INTERRUPTIBLE;
 			current->timeout = jiffies + HZ/25;
 			schedule();
 			if (signal_pending(current))
 				goto done;
-			if (msp->restart) 
-			{
+			if (msp->restart) {
 				msp->restart = 0;
 				goto restart;
 			}
@@ -505,22 +571,20 @@
 		}
 
 		/* carrier detect pass #2 -- second (stereo) carrier */
-		switch (max1) 
-		{
-			case 1: /* 5.5 */
-				cd = carrier_detect_55; count = CARRIER_COUNT(carrier_detect_55);
-				break;
-			case 3: /* 6.5 */
-				cd = carrier_detect_65; count = CARRIER_COUNT(carrier_detect_65);
-				break;
-			case 0: /* 4.5 */
-			case 2: /* 6.0 */
-			default:
-				cd = NULL; count = 0;
-				break;
+		switch (max1) {
+		case 1: /* 5.5 */
+			cd = carrier_detect_55; count = CARRIER_COUNT(carrier_detect_55);
+			break;
+		case 3: /* 6.5 */
+			cd = carrier_detect_65; count = CARRIER_COUNT(carrier_detect_65);
+			break;
+		case 0: /* 4.5 */
+		case 2: /* 6.0 */
+		default:
+			cd = NULL; count = 0;
+			break;
 		}
-		for (this = 0; this < count; this++) 
-		{
+		for (this = 0; this < count; this++) {
 			msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo);
 			UNLOCK_I2C_BUS(msp->bus);
 
@@ -529,8 +593,7 @@
 			schedule();
 			if (signal_pending(current))
 				goto done;
-			if (msp->restart) 
-			{
+			if (msp->restart) {
 				msp->restart = 0;
 				goto restart;
 			}
@@ -543,105 +606,45 @@
 		}
 
 		/* programm the msp3400 according to the results */
-		switch (max1) 
-		{
-			case 0: /* 4.5 */
-			case 1: /* 5.5 */
-				msp3400c_setmode(msp, MSP_MODE_FM_TERRA);
-				msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo,
-				carrier_detect_main[max1].cdo);
-				if (max2 == 0) 
-				{
-					/* B/G FM-stereo */
-					msp3400c_setstereo(msp, VIDEO_SOUND_MONO);
-					check_stereo = 1;
-				}
-				if (max2 == 1 && msp->nicam) 
-				{
-					/* B/G NICAM */
-					msp3400c_setmode(msp, MSP_MODE_FM_NICAM1);
-					/* msp3400c_write(msp->bus, I2C_MSP3400C_DFP, 0x21, 0x01); */
-					msp3400c_setcarrier(msp->bus, MSP_CARRIER(5.85),
-						MSP_CARRIER(5.5));
-					check_stereo = 1;
-				}
-				break;
-			case 2: /* 6.0 */
-			case 3: /* 6.5 */
-			default:
-				msp3400c_setmode(msp, MSP_MODE_FM_TERRA);
-				msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo,
-					carrier_detect_main[max1].cdo);
-				msp3400c_setstereo(msp, VIDEO_SOUND_STEREO);
-				break;
-		}
-	
-		/* unmute */
-		msp3400c_setvolume(msp->bus, msp->volume);
-
-		if (check_stereo) 
-		{
-			/* stereo available -- check current mode */
-			UNLOCK_I2C_BUS(msp->bus);
-		   
-			current->state = TASK_INTERRUPTIBLE;
-			current->timeout = jiffies + HZ;
-			schedule();
-			if (signal_pending(current))
-				goto done;
-			if (msp->restart) 
-			{
-				msp->restart = 0;
-				goto restart;
+		switch (max1) {
+		case 0: /* 4.5 */
+		case 1: /* 5.5 */
+			msp3400c_setmode(msp, MSP_MODE_FM_TERRA);
+			msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo,
+					    carrier_detect_main[max1].cdo);
+			if (max2 == 0) {
+				/* B/G FM-stereo */
+				msp3400c_setstereo(msp, VIDEO_SOUND_MONO);
+				msp->watch_stereo = 1;
 			}
-		    
-			LOCK_I2C_BUS(msp->bus);
-			switch (msp->mode) 
-			{
-				case MSP_MODE_FM_TERRA:
-					val = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x18);
-					dprintk("msp3400: stereo detect register: %d\n",val);
-			
-					if (val > 4096) 
-					{
-						msp3400c_setstereo(msp, VIDEO_SOUND_STEREO);
-					}
-					else if (val < -4096) 
-					{
-						msp3400c_setstereo(msp, VIDEO_SOUND_LANG1);
-					}
-					else
-					{
-						msp3400c_setstereo(msp, VIDEO_SOUND_MONO);
-					}
-					break;
-				case MSP_MODE_FM_NICAM1:
-  					val = msp3400c_read(msp->bus, I2C_MSP3400C_DEM, 0x23);
- 					switch ((val & 0x1e) >> 1)  
- 					{
-						case 0:
-						case 8:
-							msp3400c_setstereo(msp, VIDEO_SOUND_STEREO);
-							break;
-						default:
-							msp3400c_setstereo(msp, VIDEO_SOUND_MONO);
-							break;
- 					}			
-
-					/* dump registers (for debugging) */
-					if (debug)
-					{
-						for (i=0; i<sizeof(d1)/sizeof(struct REGISTER_DUMP); i++) 
-						{
-							val = msp3400c_read(msp->bus,I2C_MSP3400C_DEM, d1[i].addr);
-							printk(KERN_DEBUG "msp3400: %s = 0x%x\n",
-		       						d1[i].name,val);
-						}
-					}
-					break;
+			if (max2 == 1 && msp->nicam) {
+				/* B/G NICAM */
+				msp3400c_setmode(msp, MSP_MODE_FM_NICAM1);
+				/* msp3400c_write(msp->bus, I2C_MSP3400C_DFP, 0x21, 0x01); */
+				msp3400c_setcarrier(msp->bus, MSP_CARRIER(5.85),
+						    MSP_CARRIER(5.5));
+				msp->watch_stereo = 1;
 			}
+			break;
+		case 2: /* 6.0 */
+		case 3: /* 6.5 */
+		default:
+			msp3400c_setmode(msp, MSP_MODE_FM_TERRA);
+			msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo,
+					    carrier_detect_main[max1].cdo);
+			msp3400c_setstereo(msp, VIDEO_SOUND_STEREO);
+			break;
 		}
+
+		/* unmute */
+		msp3400c_setvolume(msp->bus, msp->left, msp->right);
 		UNLOCK_I2C_BUS(msp->bus);
+
+		if (msp->watch_stereo) {
+			del_timer(&msp->wake_stereo);
+			msp->wake_stereo.expires = jiffies + HZ;
+			add_timer(&msp->wake_stereo);
+		}
 		msp->active = 0;
 	}
 
@@ -656,12 +659,15 @@
 	return 0;
 }
 
-int msp3410d_thread(void *data)
+
+#if 0 /* not finished yet */
+
+static int msp3410d_thread(void *data)
 {
 	unsigned long flags;
 	struct msp3400c *msp = data;
 	struct semaphore sem = MUTEX_LOCKED;
-	int i, val;
+	int              i, val;
 
 	/* lock_kernel(); */
     
@@ -681,8 +687,7 @@
 	if(msp->notify != NULL)
 		up(msp->notify);
 		
-	for (;;) 
-	{
+	for (;;) {
 		if (msp->rmmod)
 			goto done;
 		dprintk("msp3410: thread: sleep\n");
@@ -696,7 +701,7 @@
 	
 		msp->active = 1;
 
-restart:
+	restart:
 		LOCK_I2C_BUS(msp->bus);
 		/* mute */
 		msp3400c_setvolume(msp->bus, 0);
@@ -714,16 +719,14 @@
 		schedule();
 		if (signal_pending(current))
 			goto done;
-		if (msp->restart) 
-		{
+		if (msp->restart) {
 			msp->restart = 0;
 			goto restart;
 		}
 	
 		LOCK_I2C_BUS(msp->bus);
 		/* debug register dump */
-		for (i = 0; i < sizeof(d1)/sizeof(struct REGISTER_DUMP); i++) 
-		{
+		for (i = 0; i < sizeof(d1)/sizeof(struct REGISTER_DUMP); i++) {
 			val = msp3400c_read(msp->bus,I2C_MSP3400C_DEM,d1[i].addr);
 			printk(KERN_DEBUG "msp3400: %s = 0x%x\n",d1[i].name,val);
 		}	
@@ -744,6 +747,151 @@
 		up(msp->notify);
 	return 0;
 }
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* mixer stuff -- with the modular sound driver in 2.1.x we can easily     */
+/* register the msp3400 as mixer device                                    */
+
+#ifdef REGISTER_MIXER
+
+#include <linux/soundcard.h>
+#include <../drivers/sound/sound_config.h>
+#include <../drivers/sound/dev_table.h>
+
+static int mix_to_v4l(int i)
+{
+	int r;
+
+	r = ((i & 0xff) * 65536 + 50) / 100;
+	if (r > 65535) r = 65535;
+	if (r <     0) r =     0;
+	return r;
+}
+
+static int v4l_to_mix(int i)
+{
+	int r;
+
+	r = (i * 100 + 32768) / 65536;
+	if (r > 100) r = 100;
+	if (r <   0) r =   0;
+	return r | (r << 8);
+}
+
+static int v4l_to_mix2(int l, int r)
+{
+	r = (r * 100 + 32768) / 65536;
+	if (r > 100) r = 100;
+	if (r <   0) r =   0;
+	l = (l * 100 + 32768) / 65536;
+	if (l > 100) l = 100;
+	if (l <   0) l =   0;
+	return (r << 8) | l;
+}
+
+static int msp3400c_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
+{
+	struct msp3400c *msp = mixer_devs[dev]->devc;
+	unsigned long flags;
+	int ret,val = 0;
+
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE)
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+    
+	switch (cmd) {
+	case MIXER_READ(SOUND_MIXER_RECMASK):
+	case MIXER_READ(SOUND_MIXER_CAPS):
+	case MIXER_READ(SOUND_MIXER_RECSRC):
+	case MIXER_WRITE(SOUND_MIXER_RECSRC):
+		ret = 0;
+		break;
+
+	case MIXER_READ(SOUND_MIXER_STEREODEVS):
+		ret = SOUND_MASK_VOLUME;
+		break;
+	case MIXER_READ(SOUND_MIXER_DEVMASK):
+		ret = SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE;
+		break;
+
+	case MIXER_WRITE(SOUND_MIXER_VOLUME):
+		msp->left  = mix_to_v4l(val);
+		msp->right = mix_to_v4l(val >> 8);
+		LOCK_I2C_BUS(msp->bus);
+		msp3400c_setvolume(msp->bus,msp->left,msp->right);
+		UNLOCK_I2C_BUS(msp->bus);
+		/* fall */
+	case MIXER_READ(SOUND_MIXER_VOLUME):
+		ret = v4l_to_mix2(msp->left, msp->right);
+		break;
+
+	case MIXER_WRITE(SOUND_MIXER_BASS):
+		msp->bass = mix_to_v4l(val);
+		LOCK_I2C_BUS(msp->bus);
+		msp3400c_setbass(msp->bus,msp->bass);
+		UNLOCK_I2C_BUS(msp->bus);
+		/* fall */
+	case MIXER_READ(SOUND_MIXER_BASS):
+		ret = v4l_to_mix(msp->bass);
+		break;
+
+	case MIXER_WRITE(SOUND_MIXER_TREBLE):
+		msp->treble = mix_to_v4l(val);
+		LOCK_I2C_BUS(msp->bus);
+		msp3400c_settreble(msp->bus,msp->treble);
+		UNLOCK_I2C_BUS(msp->bus);
+		/* fall */
+	case MIXER_READ(SOUND_MIXER_TREBLE):
+		ret = v4l_to_mix(msp->treble);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	if (put_user(ret, (int *)arg))
+		return -EFAULT;
+	return 0;
+}
+
+struct mixer_operations msp3400c_mixer = {
+	"video4linux",
+	"TV card sound (msp3400)",
+	msp3400c_mixer_ioctl
+};
+
+static int msp3400c_mixer_init(struct msp3400c *msp)
+{
+	int m;
+    
+	msp->mixer = m = sound_alloc_mixerdev();
+	if (m == -1)
+		return -1;
+
+	mixer_devs[m] = (struct mixer_operations *)
+		kmalloc(sizeof(struct mixer_operations), GFP_KERNEL);
+	if (mixer_devs[m] == NULL) {
+		printk(KERN_ERR "msp3400c: can't allocate memory\n");
+		sound_unload_mixerdev(m);
+		return -1;
+	}
+	memcpy(mixer_devs[m],&msp3400c_mixer,sizeof(struct mixer_operations));
+	mixer_devs[m]->devc = msp;
+	return 0;
+}
+
+static int msp3400c_mixer_close(struct msp3400c *msp)
+{
+	int m = msp->mixer;
+
+	if (m != -1 ) {
+		sound_unload_mixerdev(m);
+		kfree(mixer_devs[m]);
+	}
+	return 0;
+}
+
+#endif
 
 /* ----------------------------------------------------------------------- */
 
@@ -752,35 +900,30 @@
 	unsigned long flags;
 	struct semaphore sem = MUTEX_LOCKED;
 	struct msp3400c *msp;
-	int rev1,rev2;
-	
-	/*
-	 *	MSP3400's are for now only assumed to live on busses
-	 *	connected to a BT848. Adjust as and when you get new
-	 *	funky cards using these components.
-	 */
-	
-	if(device->bus->id != I2C_BUSID_BT848)
-		return -EINVAL;
+	int              rev1,rev2;
 
 	device->data = msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL);
 	if (NULL == msp)
 		return -ENOMEM;
 	memset(msp,0,sizeof(struct msp3400c));
 	msp->bus = device->bus;
-	msp->volume = 65535;
+	msp->left   = 65535;
+	msp->right  = 65535;
+	msp->bass   = 32768;
+	msp->treble = 32768;
 
 	LOCK_I2C_BUS(msp->bus);
-	if (-1 == msp3400c_reset(msp->bus)) 
-	{
+	if (-1 == msp3400c_reset(msp->bus)) {
 		UNLOCK_I2C_BUS(msp->bus);
 		kfree(msp);
 		return -1;
 	}
     
 	msp3400c_setmode(msp, MSP_MODE_FM_TERRA);
-	msp3400c_setvolume(msp->bus, msp->volume);
-
+	msp3400c_setvolume(msp->bus, msp->left, msp->right);
+	msp3400c_setbass(msp->bus, msp->bass);
+	msp3400c_settreble(msp->bus, msp->treble);
+    
 	rev1 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1e);
 	rev2 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1f);
 
@@ -793,17 +936,28 @@
 	sprintf(device->name,"MSP34%02d%c-%c%d",
 		(rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f);
 	msp->nicam = (((rev2>>8)&0xff) == 10) ? 1 : 0;
-	printk(KERN_INFO "msp3400: init: chip=%s%s\n",
-		device->name, msp->nicam ? ", can decode nicam" : "");
 
-	MOD_INC_USE_COUNT;
+	/* timer for stereo checking */
+	msp->wake_stereo.function = msp3400c_stereo_wake;
+	msp->wake_stereo.data     = (unsigned long)msp;
+
 	/* startup control thread */
+	MOD_INC_USE_COUNT;
 	msp->notify = &sem;
 	kernel_thread(msp3400c_thread, (void *)msp, 0);
 	down(&sem);
 	msp->notify = NULL;
 	if (!msp->active)
 		up(msp->wait);
+
+	printk(KERN_INFO "msp3400: init: chip=%s",device->name);
+	if (msp->nicam)
+		printk(", has NICAM support");
+#ifdef REGISTER_MIXER
+	if (0 == msp3400c_mixer_init(msp))
+		printk(", registered as sound mixer");
+#endif
+	printk("\n");
 	return 0;
 }
 
@@ -813,14 +967,22 @@
 	struct semaphore sem = MUTEX_LOCKED;
 	struct msp3400c *msp  = (struct msp3400c*)device->data;
     
+#ifdef REGISTER_MIXER
+	msp3400c_mixer_close(msp);
+#endif
+
 	/* shutdown control thread */
-	msp->notify = &sem;
-	msp->rmmod = 1;
-	if (!msp->active)
-		up(msp->wait);
-	down(&sem);
-	msp->notify = NULL;
-   
+	del_timer(&msp->wake_stereo);
+	if (msp->thread) 
+	{
+		msp->notify = &sem;
+		msp->rmmod = 1;
+		if (!msp->active)
+			up(msp->wait);
+		down(&sem);
+		msp->notify = NULL;
+	}
+    
 	LOCK_I2C_BUS(msp->bus);
 	msp3400c_reset(msp->bus);
 	UNLOCK_I2C_BUS(msp->bus);
@@ -834,67 +996,90 @@
 		 unsigned int cmd, void *arg)
 {
 	unsigned long flags;
-	struct msp3400c *msp = (struct msp3400c*)device->data;
-	int *iarg = (int*)arg;
+	struct msp3400c *msp  = (struct msp3400c*)device->data;
+	int             *iarg = (int*)arg;
 
-	switch (cmd) 
-	{
-		case MSP_SET_RADIO:
-			msp->norm = VIDEO_MODE_RADIO;
-			LOCK_I2C_BUS(msp->bus);
-			msp3400c_setmode(msp,MSP_MODE_FM_RADIO);
-			msp3400c_setcarrier(msp->bus, MSP_CARRIER(10.7),MSP_CARRIER(10.7));
-			UNLOCK_I2C_BUS(msp->bus);
-			break;
-		case MSP_SET_TVNORM:
-			msp->norm = *iarg;
-			break;
-		case MSP_NEWCHANNEL:
-			if (!msp->active)
-				up(msp->wait);
-			else
-				msp->restart = 1;
-			break;
+	switch (cmd) {
+	case MSP_SET_RADIO:
+		msp->norm = VIDEO_MODE_RADIO;
+		msp->watch_stereo=0;
+		del_timer(&msp->wake_stereo);
+		LOCK_I2C_BUS(msp->bus);
+		msp3400c_setmode(msp,MSP_MODE_FM_RADIO);
+		msp3400c_setcarrier(msp->bus, MSP_CARRIER(10.7),MSP_CARRIER(10.7));
+		UNLOCK_I2C_BUS(msp->bus);
+		break;
+	case MSP_SET_TVNORM:
+		msp->norm = *iarg;
+		break;
+	case MSP_NEWCHANNEL:
+		msp->watch_stereo=0;
+		del_timer(&msp->wake_stereo);
+		if (!msp->active)
+			up(msp->wait);
+		else
+			msp->restart = 1;
+		break;
 
-		case MSP_GET_VOLUME:
-			*iarg = msp->volume;
-			break;
-		case MSP_SET_VOLUME:
-			msp->volume = *iarg;
-			LOCK_I2C_BUS(msp->bus);
-			msp3400c_setvolume(msp->bus,msp->volume);
-			UNLOCK_I2C_BUS(msp->bus);
-			break;
+	case MSP_GET_VOLUME:
+		*iarg = (msp->left > msp->right) ? msp->left : msp->right;
+		break;
+	case MSP_SET_VOLUME:
+		msp->left = msp->right = *iarg;
+		LOCK_I2C_BUS(msp->bus);
+		msp3400c_setvolume(msp->bus,msp->left, msp->right);
+		UNLOCK_I2C_BUS(msp->bus);
+		break;
 
-		case MSP_GET_STEREO:
-			*iarg = msp->stereo;
-			break;
-		case MSP_SET_STEREO:
-			if (*iarg) 
-			{
-				LOCK_I2C_BUS(msp->bus);
-				msp3400c_setstereo(msp,*iarg);
-				UNLOCK_I2C_BUS(msp->bus);
-			}
-			break;
+	case MSP_GET_BASS:
+		*iarg = msp->bass;
+		break;
+	case MSP_SET_BASS:
+		msp->bass = *iarg;
+		LOCK_I2C_BUS(msp->bus);
+		msp3400c_setbass(msp->bus,msp->bass);
+		UNLOCK_I2C_BUS(msp->bus);
+		break;
+
+	case MSP_GET_TREBLE:
+		*iarg = msp->treble;
+		break;
+	case MSP_SET_TREBLE:
+		msp->treble = *iarg;
+		LOCK_I2C_BUS(msp->bus);
+		msp3400c_settreble(msp->bus,msp->treble);
+		UNLOCK_I2C_BUS(msp->bus);
+		break;
 
-		case MSP_GET_DC:
+	case MSP_GET_STEREO:
+		*iarg = msp->stereo;
+		break;
+	case MSP_SET_STEREO:
+		if (*iarg) {
+			msp->watch_stereo=0;
+			del_timer(&msp->wake_stereo);
 			LOCK_I2C_BUS(msp->bus);
-			*iarg = (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b) +
-				(int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c);
+			msp3400c_setstereo(msp,*iarg);
 			UNLOCK_I2C_BUS(msp->bus);
-			break;
+		}
+		break;
+
+	case MSP_GET_DC:
+		LOCK_I2C_BUS(msp->bus);
+		*iarg = (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b) +
+			(int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c);
+		UNLOCK_I2C_BUS(msp->bus);
+		break;
 	
-		default:
-			return -EINVAL;
+	default:
+		return -EINVAL;
 	}
 	return 0;
 }
 
 /* ----------------------------------------------------------------------- */
 
-struct i2c_driver i2c_driver_msp = 
-{
+struct i2c_driver i2c_driver_msp = {
 	"msp3400",                    /* name       */
 	I2C_DRIVERID_MSP3400,         /* ID         */
 	I2C_MSP3400C, I2C_MSP3400C,   /* addr range */
@@ -907,7 +1092,7 @@
 #ifdef MODULE
 int init_module(void)
 #else
-int msp3400c_init(void)
+     int msp3400c_init(void)
 #endif
 {
 	i2c_register_driver(&i2c_driver_msp);
@@ -921,3 +1106,10 @@
 }
 #endif
 
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -u --recursive --new-file v2.1.104/linux/drivers/char/msp3400.h linux/drivers/char/msp3400.h
--- v2.1.104/linux/drivers/char/msp3400.h	Wed Apr  8 19:36:26 1998
+++ linux/drivers/char/msp3400.h	Sun Jun  7 10:37:41 1998
@@ -15,4 +15,9 @@
 
 #define MSP_GET_DC         _IOW('m',8,int)
 
+#define MSP_GET_BASS       _IOR('m', 9,int)
+#define MSP_SET_BASS       _IOW('m',10,int)
+#define MSP_GET_TREBLE     _IOR('m',11,int)
+#define MSP_SET_TREBLE     _IOW('m',12,int)
+
 #endif /* MSP3400_H */
diff -u --recursive --new-file v2.1.104/linux/drivers/char/radio-aimslab.c linux/drivers/char/radio-aimslab.c
--- v2.1.104/linux/drivers/char/radio-aimslab.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/radio-aimslab.c	Sun Jun  7 10:37:41 1998
@@ -0,0 +1,331 @@
+/* radiotrack (radioreveal) driver for Linux radio support
+ * (c) 1997 M. Kirkwood
+ * Coverted to new API by Alan Cox <Alan.Cox@linux.org>
+ *
+ * TODO: Allow for more than one of these foolish entities :-)
+ *
+ * Notes on the hardware (reverse engineered from other peoples'
+ * reverse engineering of AIMS' code :-)
+ *
+ *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
+ *
+ *  The signal strength query is unsurprisingly inaccurate.  And it seems
+ *  to indicate that (on my card, at least) the frequency setting isn't
+ *  too great.  (I have to tune up .025MHz from what the freq should be
+ *  to get a report that the thing is tuned.)
+ *
+ *  Volume control is (ugh) analogue:
+ *   out(port, start_increasing_volume);
+ *   wait(a_wee_while);
+ *   out(port, stop_changing_the_volume);
+ *  
+ */
+
+#include <linux/module.h>	/* Modules 			*/
+#include <linux/init.h>		/* Initdata			*/
+#include <linux/ioport.h>	/* check_region, request_region	*/
+#include <linux/delay.h>	/* udelay			*/
+#include <asm/io.h>		/* outb, outb_p			*/
+#include <asm/uaccess.h>	/* copy to/from user		*/
+#include <linux/videodev.h>	/* kernel radio structs		*/
+#include <linux/config.h>	/* CONFIG_RADIO_RTRACK_PORT 	*/
+
+#ifndef CONFIG_RADIO_RTRACK_PORT
+#define CONFIG_RADIO_RTRACK_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_RTRACK_PORT; 
+static int users = 0;
+
+struct rt_device
+{
+	int port;
+	int curvol;
+	unsigned long curfreq;
+};
+
+
+/* local things */
+
+static void sleep_delay(int n)
+{
+	/* Sleep nicely for 'n' uS */
+	int d=n/1000000/HZ;
+	if(!d)
+		udelay(n);
+	else
+	{
+		/* Yield CPU time */
+		unsigned long x=jiffies;
+		while((jiffies-x)<=d)
+			schedule();
+	}
+}
+	
+/* Clock out data to the chip. This looks suspiciously like i2c as usual */
+
+static void outbits(int bits, int data, int port)
+{
+	while(bits--) 
+	{
+		if(data & 1) 
+		{
+			outw(5, port);
+			outw(5, port);
+			outw(7, port);
+			outw(7, port);
+		}
+		else 
+		{
+			outw(1, port);
+			outw(1, port);
+			outw(3, port);
+			outw(3, port);
+		}
+		data>>=1;
+	}
+}
+
+static void rt_decvol(int port)
+{
+	outb(0x48, port);
+	sleep_delay(100000);
+	outb(0xc8, port);
+}
+
+static void rt_incvol(int port)
+{
+	outb(0x88, port);
+	sleep_delay(100000);
+	outb(0xc8, port);
+}
+
+static void rt_mute(int port)
+{
+	outb(0, port);
+	outb(0xc0, port);
+}
+
+static void rt_unmute(int port)
+{
+	outb(0, port);
+	outb(0xc8, port);
+}
+
+static int rt_setvol(struct rt_device *dev, int vol)
+{
+	int i;
+	if(vol == dev->curvol)
+		return 0;
+
+	if(vol == 0)
+		rt_mute(dev->port);
+
+	if(vol > dev->curvol)
+		for(i = dev->curvol; i < vol; i++)
+			rt_incvol(dev->port);
+	else
+		for(i = dev->curvol; i > vol; i--)
+			rt_decvol(dev->port);
+
+	if(dev->curvol == 0)
+		rt_unmute(dev->port);
+
+	return 0;
+}
+
+static int rt_setfreq(struct rt_device *dev, unsigned long frequency)
+{
+	int myport = dev->port;
+#define	RTRACK_ENCODE(x)	(((((x)*2)/5)-(40*88))+0xf6c)
+	outbits(16, RTRACK_ENCODE(frequency), myport);
+	outbits(8, 0xa0, myport);
+/* XXX - get rid of this once setvol is implemented properly - XXX */
+/* these insist on turning the thing on.  not sure I approve... */
+	udelay(1000);
+	outb(0, myport);
+	outb(0xc8, myport);
+
+	return 0;
+}
+
+int rt_getsigstr(struct rt_device *dev)
+{
+	int res;
+	int myport = dev->port;
+
+	outb(0xf8, myport);
+	sleep_delay(200000);
+	res = (int)inb(myport);
+	sleep_delay(10000);
+	outb(0xe8, myport);
+	if(res == 0xfd)
+		return 1;
+	else
+		return 0;
+}
+
+static int rt_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+	struct rt_device *rt=dev->priv;
+	
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability v;
+			v.type=VID_TYPE_TUNER;
+			v.channels=1;
+			v.audios=1;
+			/* No we don't do pictures */
+			v.maxwidth=0;
+			v.maxheight=0;
+			v.minwidth=0;
+			v.minheight=0;
+			if(copy_to_user(arg,&v,sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner v;
+			if(copy_from_user(&v, arg,sizeof(v))!=0) 
+				return -EFAULT;
+			if(v.tuner)	/* Only 1 tuner */ 
+				return -EINVAL;
+			v.rangelow=(int)(88.0*16);
+			v.rangehigh=(int)(108.0*16);
+			v.flags=0;
+			v.mode=VIDEO_MODE_AUTO;
+			v.signal=0xFFFF*rt_getsigstr(rt);
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner v;
+			if(copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if(v.tuner!=0)
+				return -EINVAL;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+		case VIDIOCGFREQ:
+			if(copy_to_user(arg, &rt->curfreq, sizeof(rt->curfreq)))
+				return -EFAULT;
+			return 0;
+		case VIDIOCSFREQ:
+			if(copy_from_user(&rt->curfreq, arg,sizeof(rt->curfreq)))
+				return -EFAULT;
+			rt_setfreq(rt, rt->curfreq);
+			return 0;
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio v;
+			memset(&v,0, sizeof(v));
+			v.flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
+			v.volume=rt->curvol;
+			strcpy(v.name, "Radio");
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio v;
+			if(copy_from_user(&v, arg, sizeof(v))) 
+				return -EFAULT;	
+			if(v.audio) 
+				return -EINVAL;
+			rt->curvol=v.volume;
+
+			if(v.flags&VIDEO_AUDIO_MUTE) 
+				rt_mute(rt->port);
+			else
+				rt_setvol(rt,rt->curvol/6554);	
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int rt_open(struct video_device *dev, int flags)
+{
+	if(users)
+		return -EBUSY;
+	users++;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void rt_close(struct video_device *dev)
+{
+	users--;
+	MOD_DEC_USE_COUNT;
+}
+
+static struct rt_device rtrack_unit;
+
+static struct video_device rtrack_radio=
+{
+	"RadioTrack radio",
+	VID_TYPE_TUNER,
+	VID_HARDWARE_RTRACK,
+	rt_open,
+	rt_close,
+	NULL,	/* Can't read  (no capture ability) */
+	NULL,	/* Can't write */
+	rt_ioctl,
+	NULL,
+	NULL
+};
+
+__initfunc(int rtrack_init(struct video_init *v))
+{
+	if (check_region(io, 2)) 
+	{
+		printk(KERN_ERR "rtrack: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+
+	rtrack_radio.priv=&rtrack_unit;
+	
+	if(video_register_device(&rtrack_radio, VFL_TYPE_RADIO)==-1)
+		return -EINVAL;
+		
+	request_region(io, 2, "rtrack");
+	printk(KERN_INFO "AIMSlab Radiotrack/radioreveal card driver.\n");
+	/* mute card - prevents noisy bootups */
+	rt_mute(io);
+	return 0;
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("M.Kirkwood");
+MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
+
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+	if(io==-1)
+	{
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+		return -EINVAL;
+	}
+	return rtrack_init(NULL);
+}
+
+void cleanup_module(void)
+{
+	video_unregister_device(&rtrack_radio);
+	release_region(io,2);
+}
+
+#endif
diff -u --recursive --new-file v2.1.104/linux/drivers/char/radio-aztech.c linux/drivers/char/radio-aztech.c
--- v2.1.104/linux/drivers/char/radio-aztech.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/radio-aztech.c	Sun Jun  7 10:37:41 1998
@@ -0,0 +1,322 @@
+/* aztech.c - Aztech radio card driver for Linux 2.1 by Russell Kroll
+ *
+ * Heavily modified to support the new 2.1 radio card interfaces by
+ * Russell Kroll (rkroll@exploits.org)
+ *
+ * Based on code by
+ *
+ * Quay Ly
+ * Donald Song
+ * Jason Lewis      (jlewis@twilight.vtc.vsc.edu) 
+ * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
+ * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
+ *
+ * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
+ * along with more information on the card itself.
+ *
+ * Notable changes from the original source:
+ * - includes stripped down to the essentials
+ * - for loops used as delays replaced with udelay()
+ * - #defines removed, changed to static values
+ * - tuning structure changed - no more character arrays, other changes
+*/
+
+#include <linux/module.h>	/* Modules 			*/
+#include <linux/init.h>		/* Initdata			*/
+#include <linux/ioport.h>	/* check_region, request_region	*/
+#include <linux/delay.h>	/* udelay			*/
+#include <asm/io.h>		/* outb, outb_p			*/
+#include <asm/uaccess.h>	/* copy to/from user		*/
+#include <linux/videodev.h>	/* kernel radio structs		*/
+#include <linux/config.h>	/* CONFIG_RADIO_AZTECH_PORT 	*/
+
+/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
+
+#ifndef CONFIG_RADIO_AZTECH_PORT
+#define CONFIG_RADIO_AZTECH_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_AZTECH_PORT; 
+static int radio_wait_time = 1000;
+static int users = 0;
+
+struct az_device
+{
+	int curvol;
+	unsigned long curfreq;
+	int stereo;
+};
+
+static int volconvert(int level)
+{
+	level>>=14;	 	/* Map 16bits down to 2 bit */
+ 	level&=3;
+	
+	/* convert to card-friendly values */
+	switch (level) 
+	{
+		case 0: 
+			return 0;
+		case 1: 
+			return 1;
+		case 2:
+			return 4;
+		case 3:
+			return 5;
+	}
+	return 0;	/* Quieten gcc */
+}
+
+static void send_0_byte (struct az_device *dev)
+{
+	udelay(radio_wait_time);
+	outb_p(2+volconvert(dev->curvol), io);
+	outb_p(64+2+volconvert(dev->curvol), io);
+}
+
+static void send_1_byte (struct az_device *dev)
+{
+	udelay (radio_wait_time);
+	outb_p(128+2+volconvert(dev->curvol), io);
+	outb_p(128+64+2+volconvert(dev->curvol), io);
+}
+
+static int az_setvol(struct az_device *dev, int vol)
+{
+	outb (volconvert(vol), io);
+	return 0;
+}
+
+/* thanks to Michael Dwyer for giving me a dose of clues in
+ * the signal strength department..
+ *
+ * This card has a stereo bit - bit 0 set = mono, not set = stereo
+ * It also has a "signal" bit - bit 1 set = bad signal, not set = good
+ *
+ */
+
+static int az_getsigstr(struct az_device *dev)
+{
+	if (inb(io) & 2)	/* bit set = no signal present */
+		return 0;
+	return 1;		/* signal present */
+}
+
+static int az_getstereo(struct az_device *dev)
+{
+	if (inb(io) & 1) 	/* bit set = mono */
+		return 0;
+	return 1;		/* stereo */
+}
+
+static int az_setfreq(struct az_device *dev, unsigned long frequency)
+{
+	int  i;
+
+	frequency = (frequency / 16.0) * 100;	/* massage data a bit */
+  
+	frequency += 1070;		/* tuning needs 24 data bits  */
+	frequency /= 5;
+					
+	send_0_byte (dev);		/*  0: LSB of frequency       */
+
+	for (i = 0; i < 13; i++)	/*   : frequency bits (1-13)  */
+		if (frequency & (1 << i))
+			send_1_byte (dev);
+		else
+			send_0_byte (dev);
+
+	send_0_byte (dev);		/* 14: test bit - always 0    */
+	send_0_byte (dev);		/* 15: test bit - always 0    */
+	send_0_byte (dev);		/* 16: band data 0 - always 0 */
+	if (dev->stereo)		/* 17: stereo (1 to enable)   */
+		send_1_byte (dev);
+	else
+		send_0_byte (dev);
+
+	send_1_byte (dev);		/* 18: band data 1 - unknown  */
+	send_0_byte (dev);		/* 19: time base - always 0   */
+	send_0_byte (dev);		/* 20: spacing (0 = 25 kHz)   */
+	send_1_byte (dev);		/* 21: spacing (1 = 25 kHz)   */
+	send_0_byte (dev);		/* 22: spacing (0 = 25 kHz)   */
+	send_1_byte (dev);		/* 23: AM/FM (FM = 1, always) */
+
+	/* latch frequency */
+
+	udelay (radio_wait_time);
+	outb_p(128+64+volconvert(dev->curvol), io);
+
+	return 0;
+}
+
+static int az_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+	struct az_device *az=dev->priv;
+	
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability v;
+			v.type=VID_TYPE_TUNER;
+			v.channels=1;
+			v.audios=1;
+			/* No we don't do pictures */
+			v.maxwidth=0;
+			v.maxheight=0;
+			v.minwidth=0;
+			v.minheight=0;
+			if(copy_to_user(arg,&v,sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner v;
+			if(copy_from_user(&v, arg,sizeof(v))!=0) 
+				return -EFAULT;
+			if(v.tuner)	/* Only 1 tuner */ 
+				return -EINVAL;
+			v.rangelow=(int)(87.9*16);
+			v.rangehigh=(int)(107.8*16);
+			v.flags=0;
+			v.mode=VIDEO_MODE_AUTO;
+			v.signal=0xFFFF*az_getsigstr(az);
+			if(az_getstereo(az))
+				v.flags|=VIDEO_TUNER_STEREO_ON;
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner v;
+			if(copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if(v.tuner!=0)
+				return -EINVAL;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+		case VIDIOCGFREQ:
+			if(copy_to_user(arg, &az->curfreq, sizeof(az->curfreq)))
+				return -EFAULT;
+			return 0;
+		case VIDIOCSFREQ:
+			if(copy_from_user(&az->curfreq, arg,sizeof(az->curfreq)))
+				return -EFAULT;
+			az_setfreq(az, az->curfreq);
+			return 0;
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio v;
+			memset(&v,0, sizeof(v));
+			v.flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
+			if(az->stereo)
+				v.mode=VIDEO_SOUND_STEREO;
+			else
+				v.mode=VIDEO_SOUND_MONO;
+			v.volume=az->curvol;
+			strcpy(v.name, "Radio");
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio v;
+			if(copy_from_user(&v, arg, sizeof(v))) 
+				return -EFAULT;	
+			if(v.audio) 
+				return -EINVAL;
+			az->curvol=v.volume;
+
+			az->stereo=(v.mode&VIDEO_SOUND_STEREO)?1:0;
+			if(v.flags&VIDEO_AUDIO_MUTE) 
+				az_setvol(az,0);
+			else
+				az_setvol(az,az->curvol);								
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int az_open(struct video_device *dev, int flags)
+{
+	if(users)
+		return -EBUSY;
+	users++;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void az_close(struct video_device *dev)
+{
+	users--;
+	MOD_DEC_USE_COUNT;
+}
+
+static struct az_device aztech_unit;
+
+static struct video_device aztech_radio=
+{
+	"Aztech radio",
+	VID_TYPE_TUNER,
+	VID_HARDWARE_AZTECH,
+	az_open,
+	az_close,
+	NULL,	/* Can't read  (no capture ability) */
+	NULL,	/* Can't write */
+	az_ioctl,
+	NULL,
+	NULL
+};
+
+__initfunc(int aztech_init(struct video_init *v))
+{
+	if (check_region(io, 2)) 
+	{
+		printk(KERN_ERR "aztech: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+
+	aztech_radio.priv=&aztech_unit;
+	
+	if(video_register_device(&aztech_radio, VFL_TYPE_RADIO)==-1)
+		return -EINVAL;
+		
+	request_region(io, 2, "aztech");
+	printk(KERN_INFO "Aztech radio card driver v0.40/19980422 rkroll@exploits.org\n");
+	/* mute card - prevents noisy bootups */
+	outb (0, io);
+	return 0;
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
+MODULE_DESCRIPTION("A driver for the Aztech radio card.");
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
+
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+	if(io==-1)
+	{
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+		return -EINVAL;
+	}
+	return aztech_init(NULL);
+}
+
+void cleanup_module(void)
+{
+	video_unregister_device(&aztech_radio);
+	release_region(io,2);
+}
+
+#endif
diff -u --recursive --new-file v2.1.104/linux/drivers/char/radio-sf16fmi.c linux/drivers/char/radio-sf16fmi.c
--- v2.1.104/linux/drivers/char/radio-sf16fmi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/radio-sf16fmi.c	Sun Jun  7 10:37:41 1998
@@ -0,0 +1,266 @@
+/* SF16FMI radio driver for Linux radio support
+ * heavily based on rtrack driver...
+ * (c) 1997 M. Kirkwood
+ * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz
+ *
+ * Fitted to new interface by Alan Cox <alan.cox@linux.org>
+ *
+ * Notes on the hardware
+ *
+ *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
+ *  No volume control - only mute/unmute - you have to use line volume
+ *  control on SB-part of SF16FMI
+ *  
+ */
+
+#include <linux/module.h>	/* Modules 			*/
+#include <linux/init.h>		/* Initdata			*/
+#include <linux/ioport.h>	/* check_region, request_region	*/
+#include <linux/delay.h>	/* udelay			*/
+#include <asm/io.h>		/* outb, outb_p			*/
+#include <asm/uaccess.h>	/* copy to/from user		*/
+#include <linux/videodev.h>	/* kernel radio structs		*/
+#include <linux/config.h>	/* CONFIG_RADIO_SF16MI_PORT 	*/
+
+#include "rsf16fmi.h"
+
+struct fmi_device
+{
+	int port;
+	int curvol;
+	unsigned long curfreq;
+};
+
+#ifndef CONFIG_RADIO_SF16FMI_PORT
+#define CONFIG_RADIO_SF16FMI_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_SF16FMI_PORT; 
+static int users = 0;
+
+/* local things */
+#define RSF16_ENCODE(x)	((x*(1000/RADIO_FM_RES)+10700)/50)
+
+static void outbits(int bits, int data, int port)
+{
+	while(bits--) {
+ 		if(data & 1) {
+			outb(5, port);
+			udelay(6);
+			outb(7, port);
+			udelay(6);
+		} else {
+			outb(1, port);
+			udelay(6);
+			outb(3, port);
+			udelay(6);
+		}
+		data>>=1;
+	}
+}
+
+static void fmi_mute(int port)
+{
+	outb(0x00, port);
+}
+
+static void fmi_unmute(int port)
+{
+	outb(0x08, port);
+}
+
+/* FREQ is in 1/16ths of a MHz so this is probably wrong atm */
+
+static int fmi_setfreq(struct fmi_device *dev, unsigned long freq)
+{
+	int myport = dev->port;
+
+	outbits(16, RSF16_ENCODE(freq), myport);
+	outbits(8, 0xC0, myport);
+	/* we should wait here... */
+	return 0;
+}
+
+static int fmi_getsigstr(struct fmi_device *dev)
+{
+	int val;
+	int res;
+	int myport = dev->port;
+
+	val = dev->curvol ? 0x08 : 0x00;	/* unmute/mute */
+	outb(val, myport);
+	outb(val | 0x10, myport);
+	udelay(140000);
+	res = (int)inb(myport+1);
+	outb(val, myport);
+	return (res & 2) ? 0 : 1;
+}
+
+static int fmi_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+	struct fmi_device *fmi=dev->priv;
+	
+	switch(cmd)
+	{
+		case VIDIOCGCAP:
+		{
+			struct video_capability v;
+			v.type=VID_TYPE_TUNER;
+			v.channels=1;
+			v.audios=1;
+			/* No we don't do pictures */
+			v.maxwidth=0;
+			v.maxheight=0;
+			v.minwidth=0;
+			v.minheight=0;
+			if(copy_to_user(arg,&v,sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCGTUNER:
+		{
+			struct video_tuner v;
+			if(copy_from_user(&v, arg,sizeof(v))!=0)
+				return -EFAULT;
+			if(v.tuner)	/* Only 1 tuner */
+				return -EINVAL;
+			v.rangelow=(int)(87.5*16);
+			v.rangehigh=(int)(108.0*16);
+			v.flags=0;
+			v.mode=VIDEO_MODE_AUTO;
+			v.signal=0xFFFF*fmi_getsigstr(fmi);
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;
+		}
+		case VIDIOCSTUNER:
+		{
+			struct video_tuner v;
+			if(copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if(v.tuner!=0)
+				return -EINVAL;
+			/* Only 1 tuner so no setting needed ! */
+			return 0;
+		}
+		case VIDIOCGFREQ:
+			if(copy_to_user(arg, &fmi->curfreq, sizeof(fmi->curfreq)))
+				return -EFAULT;
+			return 0;
+		case VIDIOCSFREQ:
+			if(copy_from_user(&fmi->curfreq, arg,sizeof(fmi->curfreq)))
+				return -EFAULT;
+			fmi_setfreq(fmi, fmi->curfreq);
+			return 0;
+		case VIDIOCGAUDIO:
+		{	
+			struct video_audio v;
+			memset(&v,0, sizeof(v));
+			v.flags|=VIDEO_AUDIO_MUTABLE;
+			v.mode=VIDEO_SOUND_MONO;
+			v.volume=fmi->curvol;
+			strcpy(v.name, "Radio");
+			if(copy_to_user(arg,&v, sizeof(v)))
+				return -EFAULT;
+			return 0;			
+		}
+		case VIDIOCSAUDIO:
+		{
+			struct video_audio v;
+			if(copy_from_user(&v, arg, sizeof(v)))
+				return -EFAULT;
+			if(v.audio)
+				return -EINVAL;
+			fmi->curvol=v.volume;
+			if(v.flags&VIDEO_AUDIO_MUTE)
+				fmi_mute(fmi->port);
+			else if(fmi->curvol)
+				fmi_unmute(fmi->port);
+			else
+				fmi_mute(fmi->port);
+			return 0;
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int fmi_open(struct video_device *dev, int flags)
+{
+	if(users)
+		return -EBUSY;
+	users++;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void fmi_close(struct video_device *dev)
+{
+	users--;
+	MOD_DEC_USE_COUNT;
+}
+
+static struct fmi_device fmi_unit;
+
+static struct video_device fmi_radio=
+{
+	"SF16FMI radio",
+	VID_TYPE_TUNER,
+	VID_HARDWARE_SF16MI,
+	fmi_open,
+	fmi_close,
+	NULL,	/* Can't read  (no capture ability) */
+	NULL,	/* Can't write */
+	fmi_ioctl,
+	NULL,
+	NULL
+};
+
+__initfunc(int fmi_init(struct video_init *v))
+{
+	if (check_region(io, 2)) 
+	{
+		printk(KERN_ERR "fmi: port 0x%x already in use\n", io);
+		return -EBUSY;
+	}
+
+	fmi_unit.port=io;
+	fmi_radio.priv=&fmi_unit;
+	
+	if(video_register_device(&fmi_radio, VFL_TYPE_RADIO)==-1)
+		return -EINVAL;
+		
+	request_region(io, 2, "fmi");
+	printk(KERN_INFO "SF16FMI radio card driver.\n");
+	printk(KERN_INFO "(c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz.\n");
+	/* mute card - prevents noisy bootups */
+	fmi_mute(io);
+	return 0;
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
+MODULE_DESCRIPTION("A driver for the SF16MI radio.");
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)");
+
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+	if(io==-1)
+	{
+		printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+		return -EINVAL;
+	}
+	return fmi_init(NULL);
+}
+
+void cleanup_module(void)
+{
+	video_unregister_device(&fmi_radio);
+	release_region(io,2);
+}
+
+#endif
diff -u --recursive --new-file v2.1.104/linux/drivers/char/rsf16fmi.h linux/drivers/char/rsf16fmi.h
--- v2.1.104/linux/drivers/char/rsf16fmi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/rsf16fmi.h	Sun Jun  7 10:37:41 1998
@@ -0,0 +1,15 @@
+/* SF16FMI FMRadio include file.
+ * (c) 1998 Petr Vandrovec
+ *
+ * Not in include/linux/ because there's no need for anyone
+ * to know about these details, I reckon.
+ */
+
+#ifndef __RSF16FMI_H
+#define __RSF16FMI_H
+
+#include <linux/radio.h>
+
+int radiosf16fmi_init(void);
+
+#endif /* __RSF16FMI_H */
diff -u --recursive --new-file v2.1.104/linux/drivers/char/saa5249.c linux/drivers/char/saa5249.c
--- v2.1.104/linux/drivers/char/saa5249.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/saa5249.c	Sun Jun  7 10:37:41 1998
@@ -0,0 +1,709 @@
+/*
+ *	Cleaned up to use existing videodev interface and allow the idea
+ *	of multiple teletext decoders on the video4linux iface. Changed i2c
+ *	to cover addressing clashes on device busses. It's also rebuilt so
+ *	you can add arbitary multiple teletext devices to Linux video4linux
+ *	now (well 32 anyway).
+ *
+ *	Alan Cox <Alan.Cox@linux.org>
+ *
+ *	The original driver was heavily modified to match the i2c interface
+ *	It was truncated to use the WinTV boards, too.
+ *
+ *	Copyright (c) 1998 Richard Guenther <richard.guenther@student.uni-tuebingen.de>
+ *
+ * $Id: saa5249.c,v 1.1 1998/03/30 22:23:23 alan Exp $
+ *
+ *	Derived From
+ *
+ * vtx.c:
+ * This is a loadable character-device-driver for videotext-interfaces
+ * (aka teletext). Please check the Makefile/README for a list of supported
+ * interfaces.
+ *
+ * Copyright (c) 1994-97 Martin Buck  <martin-2.buck@student.uni-ulm.de>
+ *
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/malloc.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <stdarg.h>
+#include "i2c.h"
+#include <linux/videotext.h>
+#include <linux/videodev.h>
+
+#define VTX_VER_MAJ 1
+#define VTX_VER_MIN 7
+
+
+
+#define NUM_DAUS 4
+#define NUM_BUFS 8
+#define IF_NAME "SAA5249"
+
+static const int disp_modes[8][3] = 
+{
+	{ 0x46, 0x03, 0x03 },	/* DISPOFF */
+	{ 0x46, 0xcc, 0xcc },	/* DISPNORM */
+	{ 0x44, 0x0f, 0x0f },	/* DISPTRANS */
+	{ 0x46, 0xcc, 0x46 },	/* DISPINS */
+	{ 0x44, 0x03, 0x03 },	/* DISPOFF, interlaced */
+	{ 0x44, 0xcc, 0xcc },	/* DISPNORM, interlaced */
+	{ 0x44, 0x0f, 0x0f },	/* DISPTRANS, interlaced */
+	{ 0x44, 0xcc, 0x46 }	/* DISPINS, interlaced */
+};
+
+
+
+#define PAGE_WAIT 30				/* Time in jiffies between requesting page and */
+						/* checking status bits */
+#define PGBUF_EXPIRE 1500			/* Time in jiffies to wait before retransmitting */
+						/* page regardless of infobits */
+typedef struct {
+	u8 pgbuf[VTX_VIRTUALSIZE];		/* Page-buffer */
+	u8 laststat[10];			/* Last value of infobits for DAU */
+	u8 sregs[7];				/* Page-request registers */
+	unsigned long expire;			/* Time when page will be expired */
+	unsigned clrfound : 1;			/* VTXIOCCLRFOUND has been called */
+	unsigned stopped : 1;			/* VTXIOCSTOPDAU has been called */
+} vdau_t;
+
+struct saa5249_device
+{
+	vdau_t vdau[NUM_DAUS];			/* Data for virtual DAUs (the 5249 only has one */
+						/* real DAU, so we have to simulate some more) */
+	int vtx_use_count;
+	int is_searching[NUM_DAUS];
+	int disp_mode;
+	int virtual_mode;
+	struct i2c_bus *bus;
+};
+
+
+#define CCTWR 34		/* I²C write/read-address of vtx-chip */
+#define CCTRD 35
+#define NOACK_REPEAT 10		/* Retry access this many times on failure */
+#define CLEAR_DELAY 5		/* Time in jiffies required to clear a page */
+#define I2C_TIMEOUT 300		/* open/close/SDA-check timeout in jiffies */
+#define READY_TIMEOUT 3		/* Time in jiffies to wait for ready signal of I²C-bus interface */
+#define INIT_DELAY 500		/* Time in usec to wait at initialization of CEA interface */
+#define START_DELAY 10		/* Time in usec to wait before starting write-cycle (CEA) */
+
+#define VTX_DEV_MINOR 0
+
+/* General defines and debugging support */
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE 1
+#endif
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#define RESCHED \
+        do { \
+          if (need_resched) \
+            schedule(); \
+        } while (0)
+
+static struct video_device saa_template;	/* Declared near bottom */
+
+/*
+ *	We do most of the hard work when we become a device on the i2c.
+ */
+ 
+static int saa5249_attach(struct i2c_device *device)
+{
+	int pgbuf;
+	int err;
+	struct video_device *vd;
+	struct saa5249_device *t;
+	/* Only attach these chips to the BT848 bus for now */
+	
+	if(device->bus->id!=I2C_BUSID_BT848)
+		return -EINVAL;
+	
+	printk(KERN_DEBUG "saa5249_attach: bus %p\n", device->bus);
+	strcpy(device->name, IF_NAME);
+	
+	/*
+	 *	Now create a video4linux device
+	 */
+	 
+	vd=(struct video_device *)kmalloc(sizeof(struct video_device), GFP_KERNEL);
+	if(vd==NULL)
+		return -ENOMEM;
+	
+	memcpy(vd, &saa_template, sizeof(*vd));
+	
+	/*
+	 *	Attach an saa5249 device
+	 */
+	
+	t=(struct saa5249_device *)kmalloc(sizeof(struct saa5249_device), GFP_KERNEL);
+	if(t==NULL)
+	{
+		kfree(vd);
+		return -ENOMEM;
+	}
+	for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) 
+	{
+		memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
+		memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs));
+		memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat));
+		t->vdau[pgbuf].expire = 0;
+		t->vdau[pgbuf].clrfound = TRUE;
+		t->vdau[pgbuf].stopped = TRUE;
+		t->is_searching[pgbuf] = FALSE;
+	}
+	vd->priv=t;		 
+	device->data=vd;
+	
+	/*
+	 *	Register it
+	 */
+
+	if((err=video_register_device(vd, VFL_TYPE_VTX))<0)
+	{
+		kfree(t);
+		kfree(vd);
+		return err;
+	}
+	t->bus = device->bus;
+	return 0;
+}
+
+static int saa5249_detach(struct i2c_device *device)
+{
+	struct video_device *vd=device->data;
+	printk(KERN_DEBUG "saa5249_detach\n");
+	video_unregister_device(vd);
+	kfree(vd->priv);
+	kfree(vd);
+	return 0;
+}
+
+static int saa5249_command(struct i2c_device *device,
+			     unsigned int cmd, void *arg)
+{
+	printk(KERN_DEBUG "saa5249_command\n");
+	return -EINVAL;
+}
+
+/* new I2C driver support */
+
+static struct i2c_driver i2c_driver_videotext = 
+{
+	IF_NAME,		/* name */
+	I2C_DRIVERID_VIDEOTEXT, /* in i2c.h */
+	34, 35,			/* Addr range */
+	saa5249_attach,
+	saa5249_detach,
+	saa5249_command
+};
+
+/*
+ *	Wait the given number of jiffies (10ms). This calls the scheduler, so the actual
+ *	delay may be longer.
+ */
+
+static void jdelay(unsigned long delay) 
+{
+	sigset_t oldblocked = current->blocked;
+
+	spin_lock_irq(&current->sigmask_lock);
+	sigfillset(&current->blocked);
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + delay;
+	schedule();
+
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = oldblocked;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+}
+
+
+/* Send arbitrary number of bytes to I²C-bus. Start & stop handshaking is done by this routine.
+ * adr should be address of I²C-device, varargs-list of values to send must be terminated by -1
+ * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise
+ */
+
+static int i2c_senddata(struct saa5249_device *t, int adr, ...) 
+{
+	int val, loop;
+	va_list argp;
+
+	for (loop = 0; loop <= NOACK_REPEAT; loop++) 
+	{
+		i2c_start(t->bus);
+		if (i2c_sendbyte(t->bus, adr, 0) != 0)
+			goto loopend;
+
+		va_start(argp, adr);
+		while ((val = va_arg(argp, int)) != -1) 
+		{
+			if (val < 0 || val > 255) 
+			{
+				printk(KERN_ERR "vtx: internal error in i2c_senddata\n");
+				break;
+			}
+			if (i2c_sendbyte(t->bus, val, 0) != 0)
+				goto loopend;
+		}
+		va_end(argp);
+		i2c_stop(t->bus);
+		return 0;
+loopend:
+		i2c_stop(t->bus);
+	}
+	va_end(argp);
+	return -1;
+}
+
+
+/* Send count number of bytes from buffer buf to I²C-device adr. Start & stop handshaking is
+ * done by this routine. If uaccess is TRUE, data is read from user-space with get_user.
+ * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise
+ */
+
+static int i2c_sendbuf(struct saa5249_device *t, int adr, int reg, int count, u8 *buf, int uaccess) 
+{
+	int pos, loop;
+	u8 val;
+
+	for (loop = 0; loop <= NOACK_REPEAT; loop++) 
+	{
+		i2c_start(t->bus);
+		if (i2c_sendbyte(t->bus, adr, 0) != 0 || i2c_sendbyte(t->bus, reg, 0) != 0)
+			goto loopend;
+		for (pos = 0; pos < count; pos++) 
+		{
+			/* FIXME: FAULT WITH CLI/SPINLOCK ?? */
+			if (uaccess)
+				get_user(val, buf + pos);
+			else
+				val = buf[pos];
+			if (i2c_sendbyte(t->bus, val, 0) != 0)
+				goto loopend;
+			RESCHED;
+		}
+		i2c_stop(t->bus);
+		return 0;
+loopend:
+		i2c_stop(t->bus);
+	}
+	return -1;
+}
+
+
+/* Get count number of bytes from I²C-device at address adr, store them in buf. Start & stop
+ * handshaking is done by this routine, ack will be sent after the last byte to inhibit further
+ * sending of data. If uaccess is TRUE, data is written to user-space with put_user.
+ * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise
+ */
+
+static int i2c_getdata(struct saa5249_device *t, int adr, int count, u8 *buf, int uaccess) 
+{
+	int pos, loop, val;
+
+	for (loop = 0; loop <= NOACK_REPEAT; loop++) 
+	{
+		i2c_start(t->bus);
+		if (i2c_sendbyte(t->bus, adr, 1) != 0)
+			goto loopend;
+		for (pos = 0; pos < count; pos++) 
+		{
+			val = i2c_readbyte(t->bus, (pos==count-1) ? 1 : 0);
+			if (uaccess) 
+				put_user(val, buf + pos);
+			else
+				buf[pos] = val;
+			RESCHED;
+		}
+		i2c_stop(t->bus);
+		return 0;
+loopend:
+		i2c_stop(t->bus);
+	}
+	return -1;
+}
+
+
+/*
+ *	Standard character-device-driver functions
+ */
+
+static int saa5249_ioctl(struct video_device *vd, unsigned int cmd, void *arg) 
+{
+	struct saa5249_device *t=vd->priv;
+	static int virtual_mode = FALSE;
+
+	switch(cmd) 
+	{
+		case VTXIOCGETINFO: 
+		{
+			vtx_info_t info;
+			info.version_major = VTX_VER_MAJ;
+			info.version_minor = VTX_VER_MIN;
+			info.numpages = NUM_DAUS;
+			/*info.cct_type = CCT_TYPE;*/
+			if(copy_to_user((void*)arg, &info, sizeof(vtx_info_t)))
+				return -EFAULT;
+			return 0;
+		}
+
+		case VTXIOCCLRPAGE: 
+		{
+			vtx_pagereq_t req;
+      
+			if(copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t)))
+				return -EFAULT;
+			if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS)
+				return -EINVAL;
+			memset(t->vdau[req.pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
+			t->vdau[req.pgbuf].clrfound = TRUE;
+			return 0;
+		}
+
+		case VTXIOCCLRFOUND: 
+		{
+			vtx_pagereq_t req;
+      
+			if(copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t)))
+				return -EFAULT;
+			if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS)
+				return -EINVAL;
+			t->vdau[req.pgbuf].clrfound = TRUE;
+			return 0;
+		}
+
+		case VTXIOCPAGEREQ: 
+		{
+			vtx_pagereq_t req;
+			if(copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t)))
+				return -EFAULT;
+			if (!(req.pagemask & PGMASK_PAGE))
+				req.page = 0;
+			if (!(req.pagemask & PGMASK_HOUR))
+				req.hour = 0;
+			if (!(req.pagemask & PGMASK_MINUTE))
+				req.minute = 0;
+			if (req.page < 0 || req.page > 0x8ff) /* 7FF ?? */
+				return -EINVAL;
+			req.page &= 0x7ff;
+			if (req.hour < 0 || req.hour > 0x3f || req.minute < 0 || req.minute > 0x7f ||
+				req.pagemask < 0 || req.pagemask >= PGMASK_MAX || req.pgbuf < 0 || req.pgbuf >= NUM_DAUS)
+				return -EINVAL;
+			t->vdau[req.pgbuf].sregs[0] = (req.pagemask & PG_HUND ? 0x10 : 0) | (req.page / 0x100);
+			t->vdau[req.pgbuf].sregs[1] = (req.pagemask & PG_TEN ? 0x10 : 0) | ((req.page / 0x10) & 0xf);
+			t->vdau[req.pgbuf].sregs[2] = (req.pagemask & PG_UNIT ? 0x10 : 0) | (req.page & 0xf);
+			t->vdau[req.pgbuf].sregs[3] = (req.pagemask & HR_TEN ? 0x10 : 0) | (req.hour / 0x10);
+			t->vdau[req.pgbuf].sregs[4] = (req.pagemask & HR_UNIT ? 0x10 : 0) | (req.hour & 0xf);
+			t->vdau[req.pgbuf].sregs[5] = (req.pagemask & MIN_TEN ? 0x10 : 0) | (req.minute / 0x10);
+			t->vdau[req.pgbuf].sregs[6] = (req.pagemask & MIN_UNIT ? 0x10 : 0) | (req.minute & 0xf);
+			t->vdau[req.pgbuf].stopped = FALSE;
+			t->vdau[req.pgbuf].clrfound = TRUE;
+			t->is_searching[req.pgbuf] = TRUE;
+			return 0;
+		}
+
+		case VTXIOCGETSTAT: 
+		{
+			vtx_pagereq_t req;
+			u8 infobits[10];
+			vtx_pageinfo_t info;
+			int a;
+
+			if(copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t)))
+				return -EFAULT;
+			if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS)
+				return -EINVAL;
+			if (!t->vdau[req.pgbuf].stopped) 
+			{
+				if (i2c_senddata(t, CCTWR, 2, 0, -1) ||
+					i2c_sendbuf(t, CCTWR, 3, sizeof(t->vdau[0].sregs), t->vdau[req.pgbuf].sregs, FALSE) ||
+					i2c_senddata(t, CCTWR, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) ||
+					i2c_senddata(t, CCTWR, 2, 0, t->vdau[req.pgbuf].sregs[0] | 8, -1) ||
+					i2c_senddata(t, CCTWR, 8, 0, 25, 0, -1))
+					return -EIO;
+				jdelay(PAGE_WAIT);
+				if (i2c_getdata(t, CCTRD, 10, infobits, FALSE))
+					return -EIO;
+
+				if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) &&	/* check FOUND-bit */
+					(memcmp(infobits, t->vdau[req.pgbuf].laststat, sizeof(infobits)) || 
+					jiffies >= t->vdau[req.pgbuf].expire))
+				{		/* check if new page arrived */
+					if (i2c_senddata(t, CCTWR, 8, 0, 0, 0, -1) ||
+						i2c_getdata(t, CCTRD, VTX_PAGESIZE, t->vdau[req.pgbuf].pgbuf, FALSE))
+						return -EIO;
+					t->vdau[req.pgbuf].expire = jiffies + PGBUF_EXPIRE;
+					memset(t->vdau[req.pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE);
+					if (t->virtual_mode) 
+					{
+						/* Packet X/24 */
+						if (i2c_senddata(t, CCTWR, 8, 0, 0x20, 0, -1) ||
+							i2c_getdata(t, CCTRD, 40, t->vdau[req.pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40, FALSE))
+							return -EIO;
+						/* Packet X/27/0 */
+						if (i2c_senddata(t, CCTWR, 8, 0, 0x21, 0, -1) ||
+							i2c_getdata(t, CCTRD, 40, t->vdau[req.pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40, FALSE))
+							return -EIO;
+						/* Packet 8/30/0...8/30/15
+						 * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30,
+						 *        so we should undo this here.
+						 */
+						if (i2c_senddata(t, CCTWR, 8, 0, 0x22, 0, -1) ||
+							i2c_getdata(t, CCTRD, 40, t->vdau[req.pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40, FALSE))
+							return -EIO;
+					}
+					t->vdau[req.pgbuf].clrfound = FALSE;
+					memcpy(t->vdau[req.pgbuf].laststat, infobits, sizeof(infobits));
+				}
+				else
+				{
+					memcpy(infobits, t->vdau[req.pgbuf].laststat, sizeof(infobits));
+				}
+			}
+			else
+			{
+				memcpy(infobits, t->vdau[req.pgbuf].laststat, sizeof(infobits));
+			}
+
+			info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f);
+			if (info.pagenum < 0x100)
+				info.pagenum += 0x800;
+			info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f);
+			info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f);
+			info.charset = ((infobits[7] >> 1) & 7);
+			info.delete = !!(infobits[3] & 8);
+			info.headline = !!(infobits[5] & 4);
+			info.subtitle = !!(infobits[5] & 8);
+			info.supp_header = !!(infobits[6] & 1);
+			info.update = !!(infobits[6] & 2);
+			info.inter_seq = !!(infobits[6] & 4);
+			info.dis_disp = !!(infobits[6] & 8);
+			info.serial = !!(infobits[7] & 1);
+			info.notfound = !!(infobits[8] & 0x10);
+			info.pblf = !!(infobits[9] & 0x20);
+			info.hamming = 0;
+			for (a = 0; a <= 7; a++) 
+			{
+				if (infobits[a] & 0xf0) 
+				{
+					info.hamming = 1;
+					break;
+				}
+			}
+			if (t->vdau[req.pgbuf].clrfound)
+				info.notfound = 1;
+			if(copy_to_user(req.buffer, &info, sizeof(vtx_pageinfo_t)))
+				return -EFAULT;
+			if (!info.hamming && !info.notfound) 
+			{
+				t->is_searching[req.pgbuf] = FALSE;
+			}
+			return 0;
+		}
+
+		case VTXIOCGETPAGE: 
+		{
+			vtx_pagereq_t req;
+			int start, end;
+
+			if(copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t)))
+				return -EFAULT;
+			if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS || req.start < 0 ||
+				req.start > req.end || req.end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE))
+				return -EINVAL;
+			if(copy_to_user(req.buffer, &t->vdau[req.pgbuf].pgbuf[req.start], req.end - req.start + 1))
+				return -EFAULT;
+				
+			 /* 
+			  *	Always read the time directly from SAA5249
+			  */
+			  
+			if (req.start <= 39 && req.end >= 32) 
+			{
+				start = MAX(req.start, 32);
+				end = MIN(req.end, 39);
+				if (i2c_senddata(t, CCTWR, 8, 0, 0, start, -1) ||
+					i2c_getdata(t, CCTRD, end - start + 1, req.buffer + start - req.start, TRUE))
+					return -EIO;
+			}
+			/* Insert the current header if DAU is still searching for a page */
+			if (req.start <= 31 && req.end >= 7 && t->is_searching[req.pgbuf]) 
+			{
+				start = MAX(req.start, 7);
+				end = MIN(req.end, 31);
+				if (i2c_senddata(t, CCTWR, 8, 0, 0, start, -1) ||
+					i2c_getdata(t, CCTRD, end - start + 1, req.buffer + start - req.start, TRUE))
+					return -EIO;
+			}
+			return 0;
+		}
+
+		case VTXIOCSTOPDAU: 
+		{
+			vtx_pagereq_t req;
+
+			if(copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t)))
+				return -EFAULT;
+			if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS)
+				return -EINVAL;
+			t->vdau[req.pgbuf].stopped = TRUE;
+			t->is_searching[req.pgbuf] = FALSE;
+			return 0;
+		}
+
+		case VTXIOCPUTPAGE: 
+		case VTXIOCSETDISP: 
+		case VTXIOCPUTSTAT: 
+			return 0;
+			
+		case VTXIOCCLRCACHE: 
+		{
+			if (i2c_senddata(t ,CCTWR, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, CCTWR, 11,
+				' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
+				' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', -1))
+				return -EIO;
+			if (i2c_senddata(t, CCTWR, 3, 0x20, -1))
+				return -EIO;
+			jdelay(10 * CLEAR_DELAY);			/* I have no idea how long we have to wait here */
+			return 0;
+		}
+
+		case VTXIOCSETVIRT: 
+		{
+			/* The SAA5249 has virtual-row reception turned on always */
+			t->virtual_mode = (int)arg;
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+
+static int saa5249_open(struct video_device *vd, int nb) 
+{
+	struct saa5249_device *t=vd->priv;
+	int pgbuf;
+
+	printk("t=%p\n",t);
+	if (t->bus==NULL) 
+		return -ENODEV;
+
+	printk("Do i2c %p\n",t->bus);
+	if (i2c_senddata(t, CCTWR, 0, 0, -1) ||		/* Select R11 */
+						/* Turn off parity checks (we do this ourselves) */
+		i2c_senddata(t, CCTWR, 1, disp_modes[t->disp_mode][0], 0, -1) ||
+						/* Display TV-picture, no virtual rows */
+		i2c_senddata(t, CCTWR, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1)) /* Set display to page 4 */
+	
+	{
+		return -EIO;
+	}
+
+	printk("clean\n");
+	for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) 
+	{
+		memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
+		memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs));
+		memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat));
+		t->vdau[pgbuf].expire = 0;
+		t->vdau[pgbuf].clrfound = TRUE;
+		t->vdau[pgbuf].stopped = TRUE;
+		t->is_searching[pgbuf] = FALSE;
+	}
+	t->virtual_mode=FALSE;
+	printk("Go\n");
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+
+
+static void saa5249_release(struct video_device *vd) 
+{
+	struct saa5249_device *t=vd->priv;
+	i2c_senddata(t, CCTWR, 1, 0x20, -1);		/* Turn off CCT */
+	i2c_senddata(t, CCTWR, 5, 3, 3, -1);		/* Turn off TV-display */
+	MOD_DEC_USE_COUNT;
+	return;
+}
+
+static long saa5249_write(struct video_device *v, const char *buf, unsigned long l, int nb)
+{
+	return -EINVAL;
+}
+
+static long saa5249_read(struct video_device *v, char *buf, unsigned long l, int nb)
+{
+	return -EINVAL;
+}
+
+static struct video_device saa_template=
+{
+	IF_NAME,
+	VID_TYPE_TELETEXT,	/*| VID_TYPE_TUNER ?? */
+	VID_HARDWARE_SAA5249,
+	saa5249_open,
+	saa5249_release,
+	saa5249_read,
+	saa5249_write,
+	saa5249_ioctl,
+	NULL,
+	NULL,
+	NULL,
+	0,
+	0
+};
+
+/*
+ *	Routines for loadable modules
+ */
+
+int init_module(void) 
+{
+	printk(KERN_INFO "SAA5249 driver (" IF_NAME " interface) for VideoText version %d.%d\n",
+			VTX_VER_MAJ, VTX_VER_MIN);
+	i2c_register_driver(&i2c_driver_videotext);
+	return 0;
+}
+
+
+void cleanup_module(void) 
+{
+	i2c_unregister_driver(&i2c_driver_videotext);
+}
diff -u --recursive --new-file v2.1.104/linux/drivers/char/videodev.c linux/drivers/char/videodev.c
--- v2.1.104/linux/drivers/char/videodev.c	Wed Apr  8 19:36:26 1998
+++ linux/drivers/char/videodev.c	Sun Jun  7 10:37:41 1998
@@ -48,6 +48,15 @@
 #ifdef CONFIG_VIDEO_BWQCAM
 extern int init_bw_qcams(struct video_init *);
 #endif
+#ifdef CONFIG_RADIO_AZTECH
+extern int aztech_init(struct video_init *);
+#endif
+#ifdef CONFIG_RADIO_RTRACK
+extern int rtrack_init(struct video_init *);
+#endif
+#ifdef CONFIG_RADIO_SF16FMI
+extern int fmi_init(struct video_init *);
+#endif
 
 static struct video_init video_init_list[]={
 #ifdef CONFIG_VIDEO_BT848
@@ -65,6 +74,15 @@
 #ifdef CONFIG_VIDEO_PMS
 	{"PMS", init_pms_cards}, 
 #endif	
+#ifdef CONFIG_RADIO_AZTECH
+	{"Aztech", aztech_init}, 
+#endif	
+#ifdef CONFIG_RADIO_RTRACK
+	{"RTrack", rtrack_init}, 
+#endif	
+#ifdef CONFIG_RADIO_SF16FMI
+	{"SF16FMI", fmi_init}, 
+#endif	
 	{"end", NULL}
 };
 
@@ -77,7 +95,10 @@
 	char *buf, size_t count, loff_t *ppos)
 {
 	struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
-	return vfl->read(vfl, buf, count, file->f_flags&O_NONBLOCK);
+	if(vfl->read)
+		return vfl->read(vfl, buf, count, file->f_flags&O_NONBLOCK);
+	else
+		return -EINVAL;
 }
 
 
@@ -91,7 +112,10 @@
 	size_t count, loff_t *ppos)
 {
 	struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)];
-	return vfl->write(vfl, buf, count, file->f_flags&O_NONBLOCK);
+	if(vfl->write)
+		return vfl->write(vfl, buf, count, file->f_flags&O_NONBLOCK);
+	else
+		return 0;
 }
 
 /*
@@ -223,12 +247,15 @@
 			/* The init call may sleep so we book the slot out
 			   then call */
 			MOD_INC_USE_COUNT;
-			err=vfd->initialize(vfd);
-			if(err<0)
+			if(vfd->initialize)
 			{
-				video_device[i]=NULL;
-				MOD_DEC_USE_COUNT;
-				return err;
+				err=vfd->initialize(vfd);
+				if(err<0)
+				{
+					video_device[i]=NULL;
+					MOD_DEC_USE_COUNT;
+					return err;
+				}
 			}
 			return 0;
 		}
diff -u --recursive --new-file v2.1.104/linux/drivers/misc/parport_arc.c linux/drivers/misc/parport_arc.c
--- v2.1.104/linux/drivers/misc/parport_arc.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/misc/parport_arc.c	Sat Jun  6 19:17:55 1998
@@ -88,6 +88,7 @@
 	NULL, /* ecp_write_block */
 	NULL, /* epp_write_block */
 	
+	arc_init_state,
 	arc_save_state,
 	arc_restore_state,
 
diff -u --recursive --new-file v2.1.104/linux/drivers/misc/parport_ax.c linux/drivers/misc/parport_ax.c
--- v2.1.104/linux/drivers/misc/parport_ax.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/misc/parport_ax.c	Sat Jun  6 19:17:55 1998
@@ -208,6 +208,13 @@
 }
 
 void
+parport_ax_init_state(struct parport_state *s)
+{
+	s->u.pc.ctr = 0xc;
+	s->u.pc.ecr = 0x0;
+}
+
+void
 parport_ax_save_state(struct parport *p, struct parport_state *s)
 {
 	s->u.pc.ctr = parport_ax_read_control(p);
@@ -301,6 +308,7 @@
 	parport_ax_ecp_write_block,
 	parport_ax_ecp_read_block,
 	
+	parport_ax_init_state,
 	parport_ax_save_state,
 	parport_ax_restore_state,
 
diff -u --recursive --new-file v2.1.104/linux/drivers/misc/parport_pc.c linux/drivers/misc/parport_pc.c
--- v2.1.104/linux/drivers/misc/parport_pc.c	Thu Apr 23 20:21:33 1998
+++ linux/drivers/misc/parport_pc.c	Sat Jun  6 19:17:55 1998
@@ -3,7 +3,8 @@
  * Authors: Phil Blundell <Philip.Blundell@pobox.com>
  *          Tim Waugh <tim@cyberelk.demon.co.uk>
  *	    Jose Renau <renau@acm.org>
- *          David Campbell <campbell@tirian.che.curtin.edu.au>
+ *          David Campbell <campbell@torque.net>
+ *          Andrea Arcangeli <arcangeli@mbox.queen.it>
  *
  * based on work by Grant Guenther <grant@torque.net> and Phil Blundell.
  */
@@ -171,16 +172,24 @@
 	return 0;
 }
 
+void parport_pc_init_state(struct parport_state *s)
+{
+	s->u.pc.ctr = 0xc;
+	s->u.pc.ecr = 0x0;
+}
+
 void parport_pc_save_state(struct parport *p, struct parport_state *s)
 {
 	s->u.pc.ctr = parport_pc_read_control(p);
-	s->u.pc.ecr = parport_pc_read_econtrol(p);
+	if (p->modes & PARPORT_MODE_PCECR)
+		s->u.pc.ecr = parport_pc_read_econtrol(p);
 }
 
 void parport_pc_restore_state(struct parport *p, struct parport_state *s)
 {
 	parport_pc_write_control(p, s->u.pc.ctr);
-	parport_pc_write_econtrol(p, s->u.pc.ecr);
+	if (p->modes & PARPORT_MODE_PCECR)
+		parport_pc_write_econtrol(p, s->u.pc.ecr);
 }
 
 size_t parport_pc_epp_read_block(struct parport *p, void *buf, size_t length)
@@ -264,6 +273,7 @@
 	parport_pc_ecp_write_block,
 	parport_pc_ecp_read_block,
 	
+	parport_pc_init_state,
 	parport_pc_save_state,
 	parport_pc_restore_state,
 
@@ -304,7 +314,6 @@
 static int parport_SPP_supported(struct parport *pb)
 {
 	/* Do a simple read-write test to make sure the port exists. */
-	parport_pc_write_econtrol(pb, 0xc);
 	parport_pc_write_control(pb, 0xc);
 	parport_pc_write_data(pb, 0xaa);
 	if (parport_pc_read_data(pb) != 0xaa) return 0;
@@ -362,12 +371,13 @@
 static int parport_ECP_supported(struct parport *pb)
 {
 	int i;
-	unsigned char oecr = parport_pc_read_econtrol(pb);
+	unsigned char oecr;
 	
 	/* If there is no ECR, we have no hope of supporting ECP. */
 	if (!(pb->modes & PARPORT_MODE_PCECR))
 		return 0;
 
+	oecr = parport_pc_read_econtrol(pb);
 	/*
 	 * Using LGS chipset it uses ECR register, but
 	 * it doesn't support ECP or FIFO MODE
@@ -417,11 +427,12 @@
 static int parport_ECPEPP_supported(struct parport *pb)
 {
 	int mode;
-	unsigned char oecr = parport_pc_read_econtrol(pb);
+	unsigned char oecr;
 
 	if (!(pb->modes & PARPORT_MODE_PCECR))
 		return 0;
-	
+
+	oecr = parport_pc_read_econtrol(pb);
 	/* Search for SMC style EPP+ECP mode */
 	parport_pc_write_econtrol(pb, 0x80);
 	
@@ -472,11 +483,12 @@
 static int parport_ECPPS2_supported(struct parport *pb)
 {
 	int mode;
-	unsigned char oecr = parport_pc_read_econtrol(pb);
+	unsigned char oecr;
 
 	if (!(pb->modes & PARPORT_MODE_PCECR))
 		return 0;
-	
+
+	oecr = parport_pc_read_econtrol(pb);
 	parport_pc_write_econtrol(pb, 0x20);
 	
 	mode = parport_PS2_supported(pb);
@@ -536,12 +548,15 @@
 {
 	int irqs;
 	unsigned char octr = parport_pc_read_control(pb);
-	unsigned char oecr = parport_pc_read_econtrol(pb);
+	unsigned char oecr;
 
 #ifndef ADVANCED_DETECT
 	return PARPORT_IRQ_NONE;
 #endif
-	
+
+	if (pb->modes & PARPORT_MODE_PCECR)
+		oecr = parport_pc_read_econtrol(pb);
+
 	sti();
 	irqs = probe_irq_on();
 
@@ -560,7 +575,8 @@
 	udelay(20);
 
 	pb->irq = probe_irq_off (irqs);
-	parport_pc_write_econtrol(pb, oecr);
+	if (pb->modes & PARPORT_MODE_PCECR)
+		parport_pc_write_econtrol(pb, oecr);
 	parport_pc_write_control(pb, octr);
 
 	if (pb->irq <= 0)
@@ -573,12 +589,14 @@
 {
 	int irqs;
 	unsigned char octr = parport_pc_read_control(pb);
-	unsigned char oecr = parport_pc_read_econtrol(pb);
+	unsigned char oecr;
 
 #ifndef ADVANCED_DETECT
 	return PARPORT_IRQ_NONE;
 #endif
 
+	if (pb->modes & PARPORT_MODE_PCECR)
+		oecr = parport_pc_read_econtrol(pb);
 	probe_irq_off(probe_irq_on());	/* Clear any interrupts */
 	irqs = probe_irq_on();
 
@@ -602,7 +620,8 @@
 	if (pb->irq <= 0)
 		pb->irq = PARPORT_IRQ_NONE;	/* No interrupt detected */
 	
-	parport_pc_write_econtrol(pb, oecr);
+	if (pb->modes & PARPORT_MODE_PCECR)
+		parport_pc_write_econtrol(pb, oecr);
 	parport_pc_write_control(pb, octr);
 	return pb->irq;
 }
@@ -616,8 +635,6 @@
  */
 static int parport_irq_probe(struct parport *pb)
 {
-	unsigned char oecr = parport_pc_read_econtrol (pb);
-
 	if (pb->modes & PARPORT_MODE_PCECR) {
 		pb->irq = programmable_irq_support(pb);
 		if (pb->irq != PARPORT_IRQ_NONE)
@@ -628,10 +645,8 @@
 		pb->irq = irq_probe_ECP(pb);
 
 	if (pb->irq == PARPORT_IRQ_NONE && 
-	    (pb->modes & PARPORT_MODE_PCECPEPP)) {
+	    (pb->modes & PARPORT_MODE_PCECPEPP))
 		pb->irq = irq_probe_EPP(pb);
-		parport_pc_write_econtrol(pb, oecr);
-	}
 
 	epp_clear_timeout(pb);
 
@@ -644,7 +659,6 @@
 		pb->irq = irq_probe_SPP(pb);
 
 out:
-	parport_pc_write_econtrol (pb, oecr);
 	return pb->irq;
 }
 
@@ -708,7 +722,11 @@
 	p->flags |= PARPORT_FLAG_COMA;
 
 	/* Done probing.  Now put the port into a sensible start-up state. */
-	parport_pc_write_econtrol(p, 0xc);
+	if (p->modes & PARPORT_MODE_PCECR)
+		/*
+		 * Put the ECP detected port in the more SPP like mode.
+		 */
+		parport_pc_write_econtrol(p, 0x0);
 	parport_pc_write_control(p, 0xc);
 	parport_pc_write_data(p, 0);
 
diff -u --recursive --new-file v2.1.104/linux/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c
--- v2.1.104/linux/drivers/misc/parport_share.c	Sun Jun  7 11:16:31 1998
+++ linux/drivers/misc/parport_share.c	Sat Jun  6 19:17:55 1998
@@ -210,7 +210,7 @@
 	tmp->private = handle;
 	tmp->flags = flags;
 	tmp->irq_func = irq_func;
-	port->ops->save_state(port, tmp->state);
+	port->ops->init_state(tmp->state);
 	tmp->waiting = 0;
 
 	/* Chain this onto the list */
diff -u --recursive --new-file v2.1.104/linux/drivers/net/3c503.c linux/drivers/net/3c503.c
--- v2.1.104/linux/drivers/net/3c503.c	Sun Jun  7 11:16:31 1998
+++ linux/drivers/net/3c503.c	Sun Jun  7 10:37:41 1998
@@ -186,6 +186,9 @@
 	return ENODEV;
     }
 
+    if (load_8390_module("3c503.c"))
+	return -ENOSYS;
+
     /* We should have a "dev" from Space.c or the static module table. */
     if (dev == NULL) {
 	printk("3c503.c: Passed a NULL device.\n");
@@ -346,7 +349,7 @@
 		outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR);
 		outb_p(0x00, E33G_IDCFR);
 		if (*irqp == autoirq_report(0)	 /* It's a good IRQ line! */
-		    && request_irq (dev->irq = *irqp, &ei_interrupt, 0, ei_status.name, dev) == 0)
+		    && request_irq (dev->irq = *irqp, ei_interrupt, 0, ei_status.name, dev) == 0)
 		    break;
 	    }
 	} while (*++irqp);
@@ -355,7 +358,7 @@
 	    return -EAGAIN;
 	}
     } else {
-	if (request_irq(dev->irq, &ei_interrupt, 0, ei_status.name, dev)) {
+	if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, dev)) {
 	    return -EAGAIN;
 	}
     }
@@ -658,11 +661,15 @@
 		}
 		if (register_netdev(dev) != 0) {
 			printk(KERN_WARNING "3c503.c: No 3c503 card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0) return 0;	/* Got at least one. */
+			if (found != 0) {	/* Got at least one. */
+				lock_8390_module();
+				return 0;
+			}
 			return -ENXIO;
 		}
 		found++;
 	}
+	lock_8390_module();
 	return 0;
 }
 
@@ -674,13 +681,15 @@
 	for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
 		struct device *dev = &dev_el2[this_dev];
 		if (dev->priv != NULL) {
+			void *priv = dev->priv;
 			/* NB: el2_close() handles free_irq */
-			unregister_netdev(dev);
-			kfree(dev->priv);
-			dev->priv = NULL;
 			release_region(dev->base_addr, EL2_IO_EXTENT);
+			dev->priv = NULL;
+			unregister_netdev(dev);
+			kfree(priv);
 		}
 	}
+	unlock_8390_module();
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v2.1.104/linux/drivers/net/8390.c linux/drivers/net/8390.c
--- v2.1.104/linux/drivers/net/8390.c	Thu May 14 19:47:39 1998
+++ linux/drivers/net/8390.c	Sun Jun  7 10:37:41 1998
@@ -35,6 +35,8 @@
   Paul Gortmaker	: update packet statistics for v2.1.x
   Alan Cox		: support arbitary stupid port mappings on the
   			  68K Macintosh. Support >16bit I/O spaces
+  Paul Gortmaker	: add kmod support for auto-loading of the 8390
+			  module by all drivers that require it.
 
 
   Sources:
@@ -67,6 +69,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 
+#define NS8390_CORE
 #include "8390.h"
 
 /* These are the operational function interfaces to board-specific
@@ -93,9 +96,7 @@
 #define ei_get_8390_hdr (ei_local->get_8390_hdr)
 
 /* use 0 for production, 1 for verification, >2 for debug */
-#ifdef EI_DEBUG
-int ei_debug = EI_DEBUG;
-#else
+#ifndef ei_debug
 int ei_debug = 1;
 #endif
 
@@ -819,6 +820,11 @@
 	 * ensure multicast mode is off prior to loading up the new hash
 	 * table. If this proves to be not enough, we can always resort
 	 * to stopping the NIC, loading the table and then restarting.
+	 *
+	 * Bug Alert!  The MC regs on the SMC 83C690 (SMC Elite and SMC 
+	 * Elite16) appear to be write-only. The NS 8390 data sheet lists
+	 * them as r/w so this is a bug.  The SMC 83C790 (SMC Ultra and
+	 * Ultra32 EISA) appears to have this bug fixed.
 	 */
 	if (dev->start)
 		outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
@@ -828,8 +834,10 @@
 	for(i = 0; i < 8; i++) 
 	{
 		outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i));
+#ifdef NOT_83C690
 		if(inb_p(e8390_base + EN1_MULT_SHIFT(i))!=ei_local->mcfilter[i])
 			printk(KERN_ERR "Multicast filter read/write mismap %d\n",i);
+#endif
 	}
 	outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD);
 	restore_flags(flags);
@@ -898,7 +906,7 @@
 	outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
 	outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY);	/* 3c503 says 0x3f,NS0x26*/
 	ei_local->current_page = ei_local->rx_start_page;		/* assert boundary+1 */
-	outb_p(ei_local->stop_page,	  e8390_base + EN0_STOPPG);
+	outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
 	/* Clear the pending interrupts and mask. */
 	outb_p(0xFF, e8390_base + EN0_ISR);
 	outb_p(0x00,  e8390_base + EN0_IMR);
@@ -929,7 +937,7 @@
 		outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
 		outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
 		/* 3c503 TechMan says rxconfig only after the NIC is started. */
-		outb_p(E8390_RXCONFIG,	e8390_base + EN0_RXCR); /* rx on,  */
+		outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on,  */
 		set_multicast_list(dev);	/* (re)load the mcast table */
 	}
 	return;
@@ -939,7 +947,6 @@
 static void NS8390_trigger_send(struct device *dev, unsigned int length,
 								int start_page)
 {
-	struct ei_device *ei_local = (struct ei_device *) dev->priv;
 	int e8390_base = dev->base_addr;
     
 	outb_p(E8390_NODMA+E8390_PAGE0, e8390_base+E8390_CMD);
@@ -958,13 +965,17 @@
 
 #ifdef MODULE
 
+struct module *NS8390_module = NULL;
+
 int init_module(void)
 {
+	NS8390_module = &__this_module;
 	return 0;
 }
 
 void cleanup_module(void)
 {
+	NS8390_module = NULL;
 }
 
 #endif /* MODULE */
diff -u --recursive --new-file v2.1.104/linux/drivers/net/8390.h linux/drivers/net/8390.h
--- v2.1.104/linux/drivers/net/8390.h	Thu May 14 19:47:39 1998
+++ linux/drivers/net/8390.h	Sun Jun  7 10:37:41 1998
@@ -1,6 +1,6 @@
 /* Generic NS8390 register definitions. */
 /* This file is part of Donald Becker's 8390 drivers, and is distributed
-   under the same license.
+   under the same license. Auto-loading of 8390.o added by Paul Gortmaker.
    Some of these names and comments originated from the Crynwr
    packet drivers, which are distributed under the GPL. */
 
@@ -12,6 +12,11 @@
 #include <linux/ioport.h>
 #include <linux/skbuff.h>
 
+/* With kmod, drivers can now load the 8390 module themselves! */
+#ifdef CONFIG_KMOD
+#define LOAD_8390_BY_KMOD
+#endif
+
 #define TX_2X_PAGES 12
 #define TX_1X_PAGES 6
 
@@ -33,21 +38,117 @@
   unsigned short count; /* header + packet length in bytes */
 };
 
-/* From 8390.c */
+#ifdef notdef
 extern int ei_debug;
-extern struct sigaction ei_sigaction;
+#else
+#define ei_debug 1
+#endif
 
-extern int ethif_init(struct device *dev);
+#ifndef HAVE_AUTOIRQ
+/* From auto_irq.c */
+extern void autoirq_setup(int waittime);
+extern unsigned long autoirq_report(int waittime);
+#endif
+
+#if defined(LOAD_8390_BY_KMOD) && defined(MODULE) && !defined(NS8390_CORE)
+
+/* Function pointers to be mapped onto the 8390 core support */
+static int (*S_ethdev_init)(struct device *dev);
+static void (*S_NS8390_init)(struct device *dev, int startp);
+static int (*S_ei_open)(struct device *dev);
+static int (*S_ei_close)(struct device *dev);
+static void (*S_ei_interrupt)(int irq, void *dev_id, struct pt_regs *regs);
+
+
+#define NS8390_KSYSMS_PRESENT	(			\
+	get_module_symbol(NULL, "ethdev_init") != 0 &&	\
+	get_module_symbol(NULL, "NS8390_init") != 0 &&	\
+	get_module_symbol(NULL, "ei_open") != 0 &&	\
+	get_module_symbol(NULL, "ei_close") != 0 &&	\
+	get_module_symbol(NULL, "ei_interrupt") != 0)
+
+extern __inline__ int load_8390_module(const char *driver)
+{
+
+	if (! NS8390_KSYSMS_PRESENT) {
+		int (*request_mod)(const char *module_name);
+
+		if (get_module_symbol("", "request_module") == 0) {
+			printk("%s: module auto-load (kmod) support not present.\n", driver);
+			printk("%s: unable to auto-load required 8390 module.\n", driver);
+			printk("%s: try \"modprobe 8390\" as root 1st.\n", driver);
+			return -ENOSYS;
+		}
+
+		request_mod = (void*)get_module_symbol("", "request_module");
+		if (request_mod("8390")) {
+			printk("%s: request to load the 8390 module failed.\n", driver);
+			return -ENOSYS;
+		}
+
+		/* Check if module really loaded and is valid */
+		if (! NS8390_KSYSMS_PRESENT) {
+			printk("%s: 8390.o not found/invalid or failed to load.\n", driver);
+			return -ENOSYS;
+		}
+
+		printk(KERN_INFO "%s: auto-loaded 8390 module.\n", driver);
+	}
+
+	/* Map the functions into place */
+	S_ethdev_init = (void*)get_module_symbol(0, "ethdev_init");
+	S_NS8390_init = (void*)get_module_symbol(0, "NS8390_init");
+	S_ei_open = (void*)get_module_symbol(0, "ei_open");
+	S_ei_close = (void*)get_module_symbol(0, "ei_close");
+	S_ei_interrupt = (void*)get_module_symbol(0, "ei_interrupt");
+
+	return 0;
+}
+
+/*
+ * Since a kmod aware driver won't explicitly show a dependence on the
+ * exported 8390 functions (due to the mapping above), the 8390 module
+ * (if present, and not in-kernel) needs to be protected from garbage
+ * collection.  NS8390_module is only defined for a modular 8390 core.
+ */
+
+extern __inline__  void lock_8390_module(void)
+{
+	struct module **mod = (struct module**)get_module_symbol(0, "NS8390_module");
+
+	if (mod != NULL && *mod != NULL)
+		__MOD_INC_USE_COUNT(*mod);
+}
+	
+extern __inline__  void unlock_8390_module(void)
+{
+	struct module **mod = (struct module**)get_module_symbol(0, "NS8390_module");
+
+	if (mod != NULL && *mod != NULL)
+		__MOD_DEC_USE_COUNT(*mod);
+}
+	
+/*
+ * These are last so they only have scope over the driver
+ * code (wd, ne, 3c503, etc.)  and not over the above code.
+ */
+#define ethdev_init S_ethdev_init
+#define NS8390_init S_NS8390_init
+#define ei_open S_ei_open
+#define ei_close S_ei_close
+#define ei_interrupt S_ei_interrupt
+
+#else	/* not a module or kmod support not wanted */
+
+#define load_8390_module(driver)	0
+#define lock_8390_module()		do { } while (0)
+#define unlock_8390_module()		do { } while (0)
 extern int ethdev_init(struct device *dev);
 extern void NS8390_init(struct device *dev, int startp);
 extern int ei_open(struct device *dev);
 extern int ei_close(struct device *dev);
 extern void ei_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 
-#ifndef HAVE_AUTOIRQ
-/* From auto_irq.c */
-extern void autoirq_setup(int waittime);
-extern unsigned long autoirq_report(int waittime);
 #endif
 
 /* Most of these entries should be in 'struct device' (or most of the
diff -u --recursive --new-file v2.1.104/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.104/linux/drivers/net/Config.in	Sun Jun  7 11:16:31 1998
+++ linux/drivers/net/Config.in	Sun Jun  7 10:37:41 1998
@@ -94,7 +94,7 @@
   fi
   bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA
   if [ "$CONFIG_NET_EISA" = "y" ]; then
-    bool 'AMD PCnet32 (VLB and PCI) support' CONFIG_PCNET32
+    tristate 'AMD PCnet32 (VLB and PCI) support' CONFIG_PCNET32
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate 'Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200
     fi
@@ -107,6 +107,9 @@
     tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate 'Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390
+    fi
+    tristate 'PCI NE2000 support' CONFIG_NE2K_PCI
+    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate 'Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210
       tristate 'SMC EtherPower II (EXPERIMENTAL)' CONFIG_EPIC100
       tristate 'TI ThunderLAN support (EXPERIMENTAL)' CONFIG_TLAN
@@ -177,6 +180,7 @@
 bool 'Token Ring driver support' CONFIG_TR
 if [ "$CONFIG_TR" = "y" ]; then
   tristate 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR
+#  tristate 'IBM Lanstreamer PCI adaptor support' CONFIG_IBMLS
 fi
 
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
diff -u --recursive --new-file v2.1.104/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.104/linux/drivers/net/Makefile	Thu May 14 19:47:39 1998
+++ linux/drivers/net/Makefile	Sun Jun  7 10:37:41 1998
@@ -51,6 +51,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_IBMLS),y)
+L_OBJS += lanstreamer.o
+else
+  ifeq ($(CONFIG_IBMLS),m)
+  M_OBJS += lanstreamer.o
+  endif
+endif
+
 ifeq ($(CONFIG_ETHERTAP),y)
 L_OBJS += ethertap.o
 else
@@ -124,6 +132,16 @@
   endif
 endif
 
+ifeq ($(CONFIG_NE2K_PCI),y)
+L_OBJS += ne2k-pci.o
+CONFIG_8390_BUILTIN = y
+else
+  ifeq ($(CONFIG_NE2K_PCI),m)
+  CONFIG_8390_MODULE = y
+  M_OBJS += ne2k-pci.o
+  endif
+endif
+
 ifeq ($(CONFIG_NE2000),y)
 L_OBJS += ne.o
 CONFIG_8390_BUILTIN = y
@@ -305,6 +323,10 @@
 
 ifeq ($(CONFIG_PCNET32),y)
 L_OBJS += pcnet32.o
+else
+  ifeq ($(CONFIG_PCNET32),m)
+  M_OBJS += pcnet32.o
+  endif
 endif
 
 ifeq ($(CONFIG_DEFXX),y)
diff -u --recursive --new-file v2.1.104/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.1.104/linux/drivers/net/Space.c	Thu May 14 19:47:39 1998
+++ linux/drivers/net/Space.c	Sun Jun  7 10:37:41 1998
@@ -47,6 +47,7 @@
 extern int ultramca_probe(struct device *dev);
 extern int wd_probe(struct device *dev);
 extern int el2_probe(struct device *dev);
+extern int ne2k_pci_probe(struct device *dev);
 extern int ne_probe(struct device *dev);
 extern int hp_probe(struct device *dev);
 extern int hp_plus_probe(struct device *dev);
@@ -179,7 +180,10 @@
 #ifdef CONFIG_E2100		/* Cabletron E21xx series. */
 	&& e2100_probe(dev)
 #endif
-#if defined(CONFIG_NE2000) || defined(NE2000)
+#if defined(CONFIG_NE2K_PCI)
+	&& ne2k_pci_probe(dev)
+#endif
+#if defined(CONFIG_NE2000)
 	&& ne_probe(dev)
 #endif
 #ifdef CONFIG_AT1500
diff -u --recursive --new-file v2.1.104/linux/drivers/net/ac3200.c linux/drivers/net/ac3200.c
--- v2.1.104/linux/drivers/net/ac3200.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/ac3200.c	Sun Jun  7 10:37:41 1998
@@ -7,11 +7,16 @@
 	incorporated herein by reference.
 
 	The author may be reached as becker@cesdis.gsfc.nasa.gov, or
-    C/O Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+	C/O Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 
 	This is driver for the Ansel Communications Model 3200 EISA Ethernet LAN
 	Adapter.  The programming information is from the users manual, as related
 	by glee@ardnassak.math.clemson.edu.
+
+	Changelog:
+
+	Paul Gortmaker 05/98	: add support for shared mem above 1MB.
+
   */
 
 static const char *version =
@@ -29,23 +34,24 @@
 
 #include <asm/system.h>
 #include <asm/io.h>
+#include <asm/irq.h>
 
 #include "8390.h"
 
 /* Offsets from the base address. */
-#define AC_NIC_BASE		0x00
-#define AC_SA_PROM		0x16			/* The station address PROM. */
-#define  AC_ADDR0		 0x00			/* Prefix station address values. */
-#define  AC_ADDR1		 0x40			/* !!!!These are just guesses!!!! */
-#define  AC_ADDR2		 0x90
-#define AC_ID_PORT		0xC80
-#define AC_EISA_ID		 0x0110d305
+#define AC_NIC_BASE	0x00
+#define AC_SA_PROM	0x16			/* The station address PROM. */
+#define AC_ADDR0	0x00			/* Prefix station address values. */
+#define AC_ADDR1	0x40			
+#define AC_ADDR2	0x90
+#define AC_ID_PORT	0xC80
+#define AC_EISA_ID	0x0110d305
 #define AC_RESET_PORT	0xC84
-#define  AC_RESET		 0x00
-#define  AC_ENABLE		 0x01
-#define AC_CONFIG		0xC90	/* The configuration port. */
+#define AC_RESET	0x00
+#define AC_ENABLE	0x01
+#define AC_CONFIG	0xC90	/* The configuration port. */
 
-#define AC_IO_EXTENT 0x10		/* IS THIS REALLY TRUE ??? */
+#define AC_IO_EXTENT 0x20
                                 /* Actually accessed is:
 								 * AC_NIC_BASE (0-15)
 								 * AC_SA_PROM (0-5)
@@ -98,7 +104,6 @@
 	else if (ioaddr > 0)		/* Don't probe at all. */
 		return ENXIO;
 
-	/* If you have a pre 0.99pl15 machine you should delete this line. */
 	if ( ! EISA_bus)
 		return ENXIO;
 
@@ -116,32 +121,19 @@
 {
 	int i;
 
-#ifndef final_version
-	printk("AC3200 ethercard probe at %#3x:", ioaddr);
+	if (inb_p(ioaddr + AC_ID_PORT) == 0xff) 
+		return -ENODEV;
+
+	if (inl(ioaddr + AC_ID_PORT) != AC_EISA_ID)
+		return -ENODEV;
 
-	for(i = 0; i < 6; i++)
-		printk(" %02x", inb(ioaddr + AC_SA_PROM + i));
-#endif
 
-	/* !!!!The values of AC_ADDRn (see above) should be corrected when we
-	   find out the correct station address prefix!!!! */
-	if (inb(ioaddr + AC_SA_PROM + 0) != AC_ADDR0
-		|| inb(ioaddr + AC_SA_PROM + 1) != AC_ADDR1
-		|| inb(ioaddr + AC_SA_PROM + 2) != AC_ADDR2 ) {
 #ifndef final_version
-		printk(" not found (invalid prefix).\n");
+	printk(KERN_DEBUG "AC3200 ethercard configuration register is %#02x,"
+		   " EISA ID %02x %02x %02x %02x.\n", inb(ioaddr + AC_CONFIG),
+		   inb(ioaddr + AC_ID_PORT + 0), inb(ioaddr + AC_ID_PORT + 1),
+		   inb(ioaddr + AC_ID_PORT + 2), inb(ioaddr + AC_ID_PORT + 3));
 #endif
-		return ENODEV;
-	}
-
-	/* The correct probe method is to check the EISA ID. */
-	for (i = 0; i < 4; i++)
-		if (inl(ioaddr + AC_ID_PORT) != AC_EISA_ID) {
-			printk("EISA ID mismatch, %8x vs %8x.\n",
-				   inl(ioaddr + AC_ID_PORT), AC_EISA_ID);
-			return ENODEV;
-		}
-
 
 	/* We should have a "dev" from Space.c or the static module table. */
 	if (dev == NULL) {
@@ -149,33 +141,43 @@
 		dev = init_etherdev(0, 0);
 	}
 
-	for(i = 0; i < ETHER_ADDR_LEN; i++)
-		dev->dev_addr[i] = inb(ioaddr + AC_SA_PROM + i);
+	printk("AC3200 in EISA slot %d, node", ioaddr/0x1000);
+	for(i = 0; i < 6; i++)
+		printk(" %02x", dev->dev_addr[i] = inb(ioaddr + AC_SA_PROM + i));
 
-#ifndef final_version
-	printk("\nAC3200 ethercard configuration register is %#02x,"
-		   " EISA ID %02x %02x %02x %02x.\n", inb(ioaddr + AC_CONFIG),
-		   inb(ioaddr + AC_ID_PORT + 0), inb(ioaddr + AC_ID_PORT + 1),
-		   inb(ioaddr + AC_ID_PORT + 2), inb(ioaddr + AC_ID_PORT + 3));
+#if 0
+	/* Check the vendor ID/prefix. Redundant after checking the EISA ID */
+	if (inb(ioaddr + AC_SA_PROM + 0) != AC_ADDR0
+		|| inb(ioaddr + AC_SA_PROM + 1) != AC_ADDR1
+		|| inb(ioaddr + AC_SA_PROM + 2) != AC_ADDR2 ) {
+		printk(", not found (invalid prefix).\n");
+		return ENODEV;
+	}
 #endif
 
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk (", unable to allocate memory for dev->priv.\n");
+		return -ENOMEM;
+	}
+
 	/* Assign and allocate the interrupt now. */
-	if (dev->irq == 0)
+	if (dev->irq == 0) {
 		dev->irq = config2irq(inb(ioaddr + AC_CONFIG));
-	else if (dev->irq == 2)
-		dev->irq = 9;
+		printk(", using");
+	} else {
+		dev->irq = irq_cannonicalize(dev->irq);
+		printk(", assigning");
+	}
 
 	if (request_irq(dev->irq, ei_interrupt, 0, "ac3200", dev)) {
-		printk (" unable to get IRQ %d.\n", dev->irq);
+		printk (" nothing! Unable to get IRQ %d.\n", dev->irq);
+		kfree(dev->priv);
+		dev->priv = NULL;
 		return EAGAIN;
 	}
 
-	/* Allocate dev->priv and fill in 8390 specific dev fields. */
-	if (ethdev_init(dev)) {
-		printk (" unable to allocate memory for dev->priv.\n");
-		free_irq(dev->irq, dev);
-		return -ENOMEM;
-	}
+	printk(" IRQ %d, %s port\n", dev->irq, port_name[dev->if_port]);
 
 	request_region(ioaddr, AC_IO_EXTENT, "ac3200");
 
@@ -194,6 +196,39 @@
 
 	dev->if_port = inb(ioaddr + AC_CONFIG) >> 6;
 	dev->mem_start = config2mem(inb(ioaddr + AC_CONFIG));
+
+	printk("%s: AC3200 at %#3x with %dkB memory at physical address %#lx.\n", 
+			dev->name, ioaddr, AC_STOP_PG/4, dev->mem_start);
+
+	/*
+	 *  BEWARE!! Some dain-bramaged EISA SCUs will allow you to put
+	 *  the card mem within the region covered by `normal' RAM  !!!
+	 */
+	if (dev->mem_start > 1024*1024) {	/* phys addr > 1MB */
+		if (dev->mem_start < (unsigned long)high_memory) {
+			printk(KERN_CRIT "ac3200.c: Card RAM overlaps with normal memory!!!\n");
+			printk(KERN_CRIT "ac3200.c: Use EISA SCU to set card memory below 1MB,\n");
+			printk(KERN_CRIT "ac3200.c: or to an address above %p.\n", high_memory);
+			printk(KERN_CRIT "ac3200.c: Driver NOT installed.\n");
+			free_irq(dev->irq, dev);
+			kfree(dev->priv);
+			dev->priv = NULL;
+			return EINVAL;
+		}
+		dev->mem_start = (unsigned long)ioremap(dev->mem_start, AC_STOP_PG*0x100);
+		if (dev->mem_start == 0) {
+			printk(KERN_ERR "ac3200.c: Unable to remap card memory above 1MB !!\n");
+			printk(KERN_ERR "ac3200.c: Try using EISA SCU to set memory below 1MB.\n");
+			printk(KERN_ERR "ac3200.c: Driver NOT installed.\n");
+			free_irq(dev->irq, dev);
+			kfree(dev->priv);
+			dev->priv = NULL;
+			return EAGAIN;
+		}
+		printk("ac3200.c: remapped %dkB card memory to virtual address %#lx\n",
+				AC_STOP_PG/4, dev->mem_start);
+	}
+
 	dev->rmem_start = dev->mem_start + TX_PAGES*256;
 	dev->mem_end = dev->rmem_end = dev->mem_start
 		+ (AC_STOP_PG - AC_START_PG)*256;
@@ -204,10 +239,6 @@
 	ei_status.stop_page = AC_STOP_PG;
 	ei_status.word16 = 1;
 
-	printk("\n%s: AC3200 at %#x, IRQ %d, %s port, shared memory %#lx-%#lx.\n",
-		   dev->name, ioaddr, dev->irq, port_name[dev->if_port],
-		   dev->mem_start, dev->mem_end-1);
-
 	if (ei_debug > 0)
 		printk(version);
 
@@ -349,12 +380,15 @@
 		if (io[this_dev] == 0 && this_dev != 0) break;
 		if (register_netdev(dev) != 0) {
 			printk(KERN_WARNING "ac3200.c: No ac3200 card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0) return 0;	/* Got at least one. */
+			if (found != 0) {	/* Got at least one. */
+				lock_8390_module();
+				return 0;
+			}
 			return -ENXIO;
 		}
 		found++;
 	}
-
+	lock_8390_module();
 	return 0;
 }
 
@@ -366,14 +400,16 @@
 	for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
 		struct device *dev = &dev_ac32[this_dev];
 		if (dev->priv != NULL) {
-			unregister_netdev(dev);
-			kfree(dev->priv);
-			dev->priv = NULL;
+			void *priv = dev->priv;
 			/* Someday free_irq may be in ac_close_card() */
 			free_irq(dev->irq, dev);
 			release_region(dev->base_addr, AC_IO_EXTENT);
+			dev->priv = NULL;
+			unregister_netdev(dev);
+			kfree(priv);
 		}
 	}
+	unlock_8390_module();
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v2.1.104/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c
--- v2.1.104/linux/drivers/net/arcnet.c	Sat May  2 14:19:53 1998
+++ linux/drivers/net/arcnet.c	Sun Jun  7 10:42:18 1998
@@ -18,6 +18,13 @@
 
 	**********************
 
+	v3.02 (98/06/07)
+	  - Use register_netdevice() instead of register_netdev() to create
+	    new devices for RFC1051 and Ethernet encapsulation in arcnet_open.
+	    Likewise for unregistering them later. This avoids the deadlock 
+	    encountered because the original routines call rtnl_lock() when
+	    it's already locked. [dw]
+
 	v3.01 (98/04/17)
 	  - Interrupt handler now also checks dev->[se]dev are non-NULL
 	    to avoid crashes in interrupts during card init. [dw]
@@ -174,7 +181,7 @@
 */
 
 static const char *version =
- "arcnet.c: v3.01 98/04/24 Avery Pennarun <apenwarr@bond.net> et al.\n";
+ "arcnet.c: v3.02 98/06/07 Avery Pennarun <apenwarr@bond.net> et al.\n";
 
 #include <linux/module.h>
 #include <linux/config.h>
@@ -462,7 +469,7 @@
   }
   sprintf(lp->edev->name,"%se",dev->name);
   lp->edev->init=arcnetE_init;
-  register_netdev(lp->edev);
+  register_netdevice(lp->edev);
 #endif
 
 #ifdef CONFIG_ARCNET_1051
@@ -472,7 +479,7 @@
   lp->sdev->name=(char *)kmalloc(10,GFP_KERNEL);
   sprintf(lp->sdev->name,"%ss",dev->name);
   lp->sdev->init=arcnetS_init;
-  register_netdev(lp->sdev);
+  register_netdevice(lp->sdev);
 #endif
 
   /* Enable TX if we need to */
@@ -548,7 +555,7 @@
 #ifdef CONFIG_ARCNET_ETH
   /* free the ethernet-encap protocol device */
   lp->edev->priv=NULL;
-  unregister_netdev(lp->edev);
+  unregister_netdevice(lp->edev);
   kfree(lp->edev->name);
   kfree(lp->edev);
   lp->edev=NULL;
@@ -557,7 +564,7 @@
 #ifdef CONFIG_ARCNET_1051
   /* free the RFC1051-encap protocol device */
   lp->sdev->priv=NULL;
-  unregister_netdev(lp->sdev);
+  unregister_netdevice(lp->sdev);
   kfree(lp->sdev->name);
   kfree(lp->sdev);
   lp->sdev=NULL;
diff -u --recursive --new-file v2.1.104/linux/drivers/net/cops.c linux/drivers/net/cops.c
--- v2.1.104/linux/drivers/net/cops.c	Sun Jun  7 11:16:32 1998
+++ linux/drivers/net/cops.c	Sun Jun  7 10:37:41 1998
@@ -24,10 +24,13 @@
  *					Hooks for cops_setup routine
  *					(not yet implemented).
  *	19971101	Jay Schulist	Fixes for multiple lt* devices.
+ *	19980507	Steven Hirsch	Fixed the badly broken support
+ *					for Tangent type cards. (COPS
+ *					LT-1 and the like)
  */
 
 static const char *version =
-	"cops.c:v0.02 3/17/97 Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n";
+	"cops.c:v0.03 3/17/97 Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n";
 /*
  *  Sources:
  *      COPS Localtalk SDK. This provides almost all of the information
@@ -118,12 +121,13 @@
  *
  *      This driver should support:
  *      TANGENT driver mode:
- *              Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200
+ *              Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200,
+ *		COPS LT-1
  *      DAYNA driver mode:
  *              Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, 
  *		Farallon PhoneNET PC III, Farallon PhoneNET PC II
  *	Other cards possibly supported mode unkown though:
- *		Dayna DL2000 (Full length)
+ *		Dayna DL2000 (Full length), COPS LT/M (Micro-Channel)
  *
  *	Cards NOT supported by this driver but supported by the ltpc.c
  *	driver written by Bradford W. Johnson <johns393@maroon.tc.umn.edu>
@@ -149,6 +153,8 @@
 	5, 4, 3, 0 
 };
 
+static struct timer_list cops_timer;
+
 /* use 0 for production, 1 for verification, 2 for debug, 3 for very verbose debug */
 #ifndef COPS_DEBUG
 #define COPS_DEBUG 1 
@@ -181,6 +187,7 @@
 static int  cops_nodeid (struct device *dev, int nodeid);
 
 static void cops_interrupt (int irq, void *dev_id, struct pt_regs *regs);
+static void cops_poll (unsigned long ltdev);
 static void cops_rx (struct device *dev);
 static int  cops_send_packet (struct sk_buff *skb, struct device *dev);
 static void set_multicast_list (struct device *dev);
@@ -193,10 +200,10 @@
 
 
 /*
- *      Check for a network adaptor of this type, and return '0' iff one exists. 
+ *      Check for a network adaptor of this type, and return '0' iff one exists.
  *      If dev->base_addr == 0, probe all likely locations.
  *      If dev->base_addr == 1, always return failure.
- *      If dev->base_addr == 2, allocate space for the device and return success 
+ *      If dev->base_addr == 2, allocate space for the device and return success
  *      (detachable devices only).
  */
 __initfunc(int cops_probe(struct device *dev))
@@ -313,13 +320,22 @@
 	dev->set_multicast_list = &set_multicast_list;
         dev->mc_list            = NULL;
 
+	if(board==TANGENT)	/* Poll 20 times per second */
+	{
+		init_timer(&cops_timer);
+		cops_timer.function 	= cops_poll;
+		cops_timer.data 	= (unsigned long)dev;
+		cops_timer.expires 	= jiffies + 5;
+		add_timer(&cops_timer);
+	}
+
         return 0;
 }
 
 __initfunc(static int cops_irq (int ioaddr, int board))
 {       /*
          * This does not use the IRQ to determine where the IRQ is. We just
-         * assume that when we get a correct status response that is the IRQ then.
+         * assume that when we get a correct status response that its the IRQ.
          * This really just verifies the IO port but since we only have access
          * to such a small number of IRQs (5, 4, 3) this is not bad.
          * This will probably not work for more than one card.
@@ -397,7 +413,7 @@
          *      Once the card has the firmware loaded and has acquired
          *      the nodeid, if it is reset it will lose it all.
          */
-        cops_reset(dev,1);    /* Need to reset card before load firmware. */
+        cops_reset(dev,1);	/* Need to reset card before load firmware. */
         cops_load(dev);		/* Load the firmware. */
 
 	/*
@@ -414,10 +430,14 @@
 
 static int tangent_wait_reset(int ioaddr)
 {
-        int timeout=0;
+	long snapt=jiffies;
 
-        while(timeout < 5000 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0)
-		mdelay(1);   /* Wait 1000 useconds */
+	while(jiffies-snapt<5*HZ)
+	{
+		if((inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY)==0)
+			break;
+		schedule();
+	}
 
 	return 0;
 }
@@ -575,9 +595,9 @@
         	/* Empty any pending adapter responses. */
                 while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0)
                 {
-			outb(0, ioaddr+COPS_CLEAR_INT);	/* Clear any interrupt. */
+			outb(0, ioaddr+COPS_CLEAR_INT);	/* Clear interrupts. */
         		if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST)
-                		cops_rx(dev);	/* Kick out any packet waiting. */
+                		cops_rx(dev);	/* Kick any packets waiting. */
 			schedule();
                 }
 
@@ -634,6 +654,44 @@
 }
 
 /*
+ *	Poll the Tangent type cards to see if we have work.
+ */
+static void cops_poll(unsigned long ltdev)
+{
+	struct cops_local *lp;
+	int ioaddr, status;
+	int boguscount = 0;
+
+	struct device *dev = (struct device *)ltdev;
+
+	del_timer(&cops_timer);
+
+	if(dev == NULL)
+		return;	/* We've been downed */
+
+	ioaddr = dev->base_addr;
+
+	/* Clear any interrupt. */
+	outb(0, ioaddr + COPS_CLEAR_INT);
+	dev->interrupt = 0;
+
+	lp = (struct cops_local *)dev->priv;
+	do {
+		status=inb(ioaddr+TANG_CARD_STATUS);
+		if(status & TANG_RX_READY)
+			cops_rx(dev);
+		if(status & TANG_TX_READY)
+			dev->tbusy = 0;
+		status = inb(ioaddr+TANG_CARD_STATUS);
+	} while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY)));
+
+	cops_timer.expires = jiffies+5;
+	add_timer(&cops_timer);
+
+	return;
+}
+
+/*
  *      The typical workload of the driver:
  *      Handle the network interface interrupts.
  */
@@ -654,29 +712,31 @@
         ioaddr = dev->base_addr;
         lp = (struct cops_local *)dev->priv;
 
-        do
-        {
-                /* Clear any interrupt. */
-                outb(0, ioaddr + COPS_CLEAR_INT);
-
-                if(lp->board==DAYNA)
-                {
-                        status=inb(ioaddr+DAYNA_CARD_STATUS);
-                        if((status&0x03)==DAYNA_RX_REQUEST)
-                                cops_rx(dev);
-                }
-                else
-                {
-                        status=inb(ioaddr+TANG_CARD_STATUS);
-                        if(status&TANG_RX_READY)
-                                cops_rx(dev);
-                }
+	if(lp->board==DAYNA)
+	{
+		do {
+			outb(0, ioaddr + COPS_CLEAR_INT);
+                       	status=inb(ioaddr+DAYNA_CARD_STATUS);
+                       	if((status&0x03)==DAYNA_RX_REQUEST)
+                       	        cops_rx(dev);
+			dev->tbusy = 0;
+			mark_bh(NET_BH);
+		} while(++boguscount < 20);
+	}
+	else
+	{
+		do {
+                       	status=inb(ioaddr+TANG_CARD_STATUS);
+			if(status & TANG_RX_READY)
+				cops_rx(dev);
+			if(status & TANG_TX_READY)
+				dev->tbusy = 0;
+			status=inb(ioaddr+TANG_CARD_STATUS);
+		} while((++boguscount < 20) &&
+				(status&(TANG_RX_READY|TANG_TX_READY)));
+	}
 
-                dev->tbusy = 0;
-                mark_bh(NET_BH);
-        } while (++boguscount < 20 );
         dev->interrupt = 0;
-
         return;
 }
 
@@ -715,7 +775,10 @@
         }
 
         /* Get response length. */
-        pkt_len = inb(ioaddr) & 0xFF;
+	if(lp->board==DAYNA)
+        	pkt_len = inb(ioaddr) & 0xFF;
+	else
+		pkt_len = inb(ioaddr) & 0x00FF;
         pkt_len |= (inb(ioaddr) << 8);
         /* Input IO code. */
         rsp_type=inb(ioaddr);
@@ -769,7 +832,7 @@
 
         skb->mac.raw    = skb->data;    /* Point to entire packet. */
         skb_pull(skb,3);
-        skb->h.raw      = skb->data;    /* Point to just the data (Skip header). */
+        skb->h.raw      = skb->data;    /* Point to data (Skip header). */
 
         /* Update the counters. */
         lp->stats.rx_packets++;
@@ -819,22 +882,17 @@
         else
         {
 		cli();	/* Disable interrupts. */
-		if(lp->board == DAYNA)		/* Wait for adapter transmit buffer. */
+		if(lp->board == DAYNA)	 /* Wait for adapter transmit buffer. */
 			while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0);
-		if(lp->board == TANGENT)	/* Wait for adapter transmit buffer. */
+		if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */
 			while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0);
 
 		/* Output IO length. */
+		outb(skb->len, ioaddr);
 		if(lp->board == DAYNA)
-		{
-                	outb(skb->len, ioaddr);
                 	outb(skb->len >> 8, ioaddr);
-		}
 		else
-		{
-			outb(skb->len&0x0FF, ioaddr);
 			outb((skb->len >> 8)&0x0FF, ioaddr);
-		}
 
 		/* Output IO code. */
                 outb(LAP_WRITE, ioaddr);
diff -u --recursive --new-file v2.1.104/linux/drivers/net/e2100.c linux/drivers/net/e2100.c
--- v2.1.104/linux/drivers/net/e2100.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/e2100.c	Sun Jun  7 10:37:41 1998
@@ -163,6 +163,9 @@
 	inb(ioaddr + E21_MEDIA); 		/* Point to media selection. */
 	outb(0, ioaddr + E21_ASIC); 	/* and disable the secondary interface. */
 
+	if (load_8390_module("e2100.c"))
+		return -ENOSYS;
+
 	if (ei_debug  &&  version_printed++ == 0)
 		printk(version);
 
@@ -176,6 +179,12 @@
 	for (i = 0; i < 6; i++)
 		printk(" %02X", station_addr[i]);
 
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk (" unable to get memory for dev->priv.\n");
+		return -ENOMEM;
+	}
+
 	if (dev->irq < 2) {
 		int irqlist[] = {15,11,10,12,5,9,3,4}, i;
 		for (i = 0; i < 8; i++)
@@ -190,12 +199,6 @@
 	} else if (dev->irq == 2)	/* Fixup luser bogosity: IRQ2 is really IRQ9 */
 		dev->irq = 9;
 
-	/* Allocate dev->priv and fill in 8390 specific dev fields. */
-	if (ethdev_init(dev)) {
-		printk (" unable to get memory for dev->priv.\n");
-		return -ENOMEM;
-	}
-
 	/* Grab the region so we can find a different board if IRQ select fails. */
 	request_region(ioaddr, E21_IO_EXTENT, "e2100");
 
@@ -423,12 +426,15 @@
 		}
 		if (register_netdev(dev) != 0) {
 			printk(KERN_WARNING "e2100.c: No E2100 card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0) return 0;	/* Got at least one. */
+			if (found != 0) {	/* Got at least one. */
+				lock_8390_module();
+				return 0;
+			}
 			return -ENXIO;
 		}
 		found++;
 	}
-
+	lock_8390_module();
 	return 0;
 }
 
@@ -440,13 +446,15 @@
 	for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
 		struct device *dev = &dev_e21[this_dev];
 		if (dev->priv != NULL) {
+			void *priv = dev->priv;
 			/* NB: e21_close() handles free_irq */
-			unregister_netdev(dev);
-			kfree(dev->priv);
-			dev->priv = NULL;
 			release_region(dev->base_addr, E21_IO_EXTENT);
+			dev->priv = NULL;
+			unregister_netdev(dev);
+			kfree(priv);
 		}
 	}
+	unlock_8390_module();
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v2.1.104/linux/drivers/net/es3210.c linux/drivers/net/es3210.c
--- v2.1.104/linux/drivers/net/es3210.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/es3210.c	Sun Jun  7 10:37:41 1998
@@ -181,6 +181,9 @@
 		return ENODEV;
 	}
 
+	if (load_8390_module("es3210.c"))
+		return -ENOSYS;
+
 	/* We should have a "dev" from Space.c or the static module table. */
 	if (dev == NULL) {
 		printk("es3210.c: Passed a NULL device.\n");
@@ -413,12 +416,15 @@
 		if (io[this_dev] == 0 && this_dev != 0) break;
 		if (register_netdev(dev) != 0) {
 			printk(KERN_WARNING "es3210.c: No es3210 card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0) return 0;	/* Got at least one. */
+			if (found != 0) {	/* Got at least one. */
+				lock_8390_module();
+				return 0;
+			}
 			return -ENXIO;
 		}
 		found++;
 	}
-
+	lock_8390_module();
 	return 0;
 }
 
@@ -430,12 +436,14 @@
 	for (this_dev = 0; this_dev < MAX_ES_CARDS; this_dev++) {
 		struct device *dev = &dev_es3210[this_dev];
 		if (dev->priv != NULL) {
-			unregister_netdev(dev);
-			kfree(dev->priv);
-			dev->priv = NULL;
+			void *priv = dev->priv;
 			free_irq(dev->irq, dev);
 			release_region(dev->base_addr, ES_IO_EXTENT);
+			dev->priv = NULL;
+			unregister_netdev(dev);
+			kfree(priv);
 		}
 	}
+	unlock_8390_module();
 }
 #endif /* MODULE */
diff -u --recursive --new-file v2.1.104/linux/drivers/net/hamradio/Config.in linux/drivers/net/hamradio/Config.in
--- v2.1.104/linux/drivers/net/hamradio/Config.in	Wed Apr  8 19:36:27 1998
+++ linux/drivers/net/hamradio/Config.in	Sun Jun  7 11:13:45 1998
@@ -11,17 +11,20 @@
   bool '   support for TRX that feedback the tx signal to rx' CONFIG_SCC_TRXECHO
 fi
     
-dep_tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX $CONFIG_AX25
-dep_tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX $CONFIG_AX25
-dep_tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR $CONFIG_AX25
+tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX
+tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX
+tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR
+tristate 'BAYCOM epp driver for AX.25' CONFIG_BAYCOM_EPP
 
-dep_tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM $CONFIG_AX25
+tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM
 if [ "$CONFIG_SOUNDMODEM" != "n" ]; then
   bool '   soundmodem support for Soundblaster and compatible cards' CONFIG_SOUNDMODEM_SBC
   bool '   soundmodem support for WSS and Crystal cards' CONFIG_SOUNDMODEM_WSS
   bool '   soundmodem support for 1200 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK1200
   bool '   soundmodem support for 2400 baud AFSK modulation (7.3728MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_7
   bool '   soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8
+  bool '   soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666
   bool '   soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800
+  bool '   soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800
   bool '   soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600
 fi
diff -u --recursive --new-file v2.1.104/linux/drivers/net/hamradio/Makefile linux/drivers/net/hamradio/Makefile
--- v2.1.104/linux/drivers/net/hamradio/Makefile	Wed Apr  8 19:36:27 1998
+++ linux/drivers/net/hamradio/Makefile	Sun Jun  7 11:13:45 1998
@@ -107,6 +107,16 @@
   endif
 endif
 
+ifeq ($(CONFIG_BAYCOM_EPP),y)
+L_OBJS += baycom_epp.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+  ifeq ($(CONFIG_BAYCOM_EPP),m)
+  CONFIG_HDLCDRV_MODULE = y
+  M_OBJS += baycom_epp.o
+  endif
+endif
+
 ifeq ($(CONFIG_SOUNDMODEM),y)
 ALL_SUB_DIRS += soundmodem
 SUB_DIRS += soundmodem
diff -u --recursive --new-file v2.1.104/linux/drivers/net/hamradio/baycom_epp.c linux/drivers/net/hamradio/baycom_epp.c
--- v2.1.104/linux/drivers/net/hamradio/baycom_epp.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/baycom_epp.c	Sun Jun  7 11:13:45 1998
@@ -0,0 +1,1556 @@
+/*****************************************************************************/
+
+/*
+ *	baycom_epp.c  -- baycom epp radio modem 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.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  History:
+ *   0.1  xx.xx.98  Initial version by Matthias Welwarsky (dg2fef)
+ *   0.2  21.04.98  Massive rework by Thomas Sailer
+ *                  Integrated FPGA EPP modem configuration routines
+ *   0.3  11.05.98  Took FPGA config out and moved it into a separate program
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/socket.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/parport.h>
+#include <linux/bitops.h>
+#include <linux/sched.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+//#include <net/ax25dev.h>
+#include <linux/kmod.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.h>
+#include <linux/soundmodem.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+/* prototypes for ax25_encapsulate and ax25_rebuild_header */
+#include <net/ax25.h> 
+#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+#define BAYCOM_MAGIC 19730510
+
+/* --------------------------------------------------------------------- */
+
+static const char paranoia_str[] = KERN_ERR 
+"baycom_epp: bad magic number for hdlcdrv_state struct in routine %s\n";
+
+#define baycom_paranoia_check(dev,routine,retval)                                              \
+({                                                                                             \
+	if (!dev || !dev->priv || ((struct baycom_state *)dev->priv)->magic != BAYCOM_MAGIC) { \
+		printk(paranoia_str, routine);                                                 \
+		return retval;                                                                 \
+	}                                                                                      \
+})
+
+#define baycom_paranoia_check_void(dev,routine)                                                \
+({                                                                                             \
+	if (!dev || !dev->priv || ((struct baycom_state *)dev->priv)->magic != BAYCOM_MAGIC) { \
+		printk(paranoia_str, routine);                                                 \
+		return;                                                                        \
+	}                                                                                      \
+})
+
+/* --------------------------------------------------------------------- */
+
+static const char bc_drvname[] = "baycom_epp";
+static const char bc_drvinfo[] = KERN_INFO "baycom_epp: (C) 1998 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom_epp: version 0.3 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct device baycom_device[NR_PORTS];
+
+static struct {
+	const char *mode;
+	int iobase;
+} baycom_ports[NR_PORTS] = { { NULL, 0 }, };
+
+/* --------------------------------------------------------------------- */
+
+/* EPP status register */
+#define EPP_DCDBIT      0x80
+#define EPP_PTTBIT      0x08
+#define EPP_NREF        0x01
+#define EPP_NRAEF       0x02
+#define EPP_NRHF        0x04
+#define EPP_NTHF        0x20
+#define EPP_NTAEF       0x10
+#define EPP_NTEF        EPP_PTTBIT
+
+/* EPP control register */
+#define EPP_TX_FIFO_ENABLE 0x10
+#define EPP_RX_FIFO_ENABLE 0x08
+#define EPP_MODEM_ENABLE   0x20
+#define EPP_LEDS           0xC0
+#define EPP_IRQ_ENABLE     0x10
+
+/* LPT registers */
+#define LPTREG_ECONTROL       0x402
+#define LPTREG_CONFIGB        0x401
+#define LPTREG_CONFIGA        0x400
+#define LPTREG_EPPDATA        0x004
+#define LPTREG_EPPADDR        0x003
+#define LPTREG_CONTROL        0x002
+#define LPTREG_STATUS         0x001
+#define LPTREG_DATA           0x000
+
+/* LPT control register */
+#define LPTCTRL_PROGRAM       0x04   /* 0 to reprogram */
+#define LPTCTRL_WRITE         0x01
+#define LPTCTRL_ADDRSTB       0x08
+#define LPTCTRL_DATASTB       0x02
+#define LPTCTRL_INTEN         0x10
+
+/* LPT status register */
+#define LPTSTAT_SHIFT_NINTR   6
+#define LPTSTAT_WAIT          0x80
+#define LPTSTAT_NINTR         (1<<LPTSTAT_SHIFT_NINTR)
+#define LPTSTAT_PE            0x20
+#define LPTSTAT_DONE          0x10
+#define LPTSTAT_NERROR        0x08
+#define LPTSTAT_EPPTIMEOUT    0x01
+
+/* LPT data register */
+#define LPTDATA_SHIFT_TDI     0
+#define LPTDATA_SHIFT_TMS     2
+#define LPTDATA_TDI           (1<<LPTDATA_SHIFT_TDI)
+#define LPTDATA_TCK           0x02
+#define LPTDATA_TMS           (1<<LPTDATA_SHIFT_TMS)
+#define LPTDATA_INITBIAS      0x80
+
+
+/* EPP modem config/status bits */
+#define EPP_DCDBIT            0x80
+#define EPP_PTTBIT            0x08
+#define EPP_RXEBIT            0x01
+#define EPP_RXAEBIT           0x02
+#define EPP_RXHFULL           0x04
+
+#define EPP_NTHF              0x20
+#define EPP_NTAEF             0x10
+#define EPP_NTEF              EPP_PTTBIT
+
+#define EPP_TX_FIFO_ENABLE    0x10
+#define EPP_RX_FIFO_ENABLE    0x08
+#define EPP_MODEM_ENABLE      0x20
+#define EPP_LEDS              0xC0
+#define EPP_IRQ_ENABLE        0x10
+
+/* Xilinx 4k JTAG instructions */
+#define XC4K_IRLENGTH   3
+#define XC4K_EXTEST     0
+#define XC4K_PRELOAD    1
+#define XC4K_CONFIGURE  5
+#define XC4K_BYPASS     7
+
+#define EPP_CONVENTIONAL  0
+#define EPP_FPGA          1
+#define EPP_FPGAEXTSTATUS 2
+
+#define TXBUFFER_SIZE     ((HDLCDRV_MAXFLEN*6/5)+8)
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct baycom_state {
+	int magic;
+
+        struct pardevice *pdev;
+	unsigned int bh_running;
+	struct tq_struct run_bh;
+	unsigned int modem;
+	unsigned int bitrate;
+	unsigned char stat;
+
+	char ifname[HDLCDRV_IFNAMELEN];
+
+	struct {
+		unsigned int intclk;
+		unsigned int divider;
+		unsigned int extmodem;
+		unsigned int loopback;
+	} cfg;
+
+        struct hdlcdrv_channel_params ch_params;
+
+        struct {
+		unsigned int bitbuf, bitstream, numbits, state;
+		unsigned char *bufptr;
+		int bufcnt;
+		unsigned char buf[TXBUFFER_SIZE];
+        } hdlcrx;
+
+        struct {
+		int calibrate;
+                int slotcnt;
+		int flags;
+		enum { tx_idle = 0, tx_keyup, tx_data, tx_tail } state;
+		unsigned char *bufptr;
+		int bufcnt;
+		unsigned char buf[TXBUFFER_SIZE];
+        } hdlctx;
+
+        struct net_device_stats stats;
+	unsigned int ptt_keyed;
+        struct sk_buff_head send_queue;  /* Packets awaiting transmission */
+
+
+#ifdef BAYCOM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		int cur_pllcorr;
+		int last_pllcorr;
+		unsigned int mod_cycles;
+		unsigned int demod_cycles;
+	} debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+#define KISS_VERBOSE
+
+/* --------------------------------------------------------------------- */
+
+#define PARAM_TXDELAY   1
+#define PARAM_PERSIST   2
+#define PARAM_SLOTTIME  3
+#define PARAM_TXTAIL    4
+#define PARAM_FULLDUP   5
+#define PARAM_HARDWARE  6
+#define PARAM_RETURN    255
+
+/* --------------------------------------------------------------------- */
+/*
+ * the CRC routines are stolen from WAMPES
+ * by Dieter Deyke
+ */
+
+static const unsigned short crc_ccitt_table[] = {
+	0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+	0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+	0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+	0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+	0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+	0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+	0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+	0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+	0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+	0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/*---------------------------------------------------------------------------*/
+
+#if 0
+extern inline void append_crc_ccitt(unsigned char *buffer, int len)
+{
+ 	unsigned int crc = 0xffff;
+
+	for (;len>0;len--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
+	crc ^= 0xffff;
+	*buffer++ = crc;
+	*buffer++ = crc >> 8;
+}
+#endif
+
+/*---------------------------------------------------------------------------*/
+
+extern inline int check_crc_ccitt(const unsigned char *buf, int cnt)
+{
+	unsigned int crc = 0xffff;
+
+	for (; cnt > 0; cnt--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
+	return (crc & 0xffff) == 0xf0b8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+extern inline int calc_crc_ccitt(const unsigned char *buf, int cnt)
+{
+	unsigned int crc = 0xffff;
+
+	for (; cnt > 0; cnt--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
+	crc ^= 0xffff;
+	return (crc & 0xffff);
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define tenms_to_flags(bc,tenms) ((tenms * bc->bitrate) / 800)
+
+/* --------------------------------------------------------------------- */
+
+static void inline baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/*
+	 * measure the interrupt frequency
+	 */
+	bc->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+		bc->debug_vals.last_jiffies = cur_jiffies;
+		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+		bc->debug_vals.cur_intcnt = 0;
+		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+		bc->debug_vals.cur_pllcorr = 0;
+	}
+#endif /* BAYCOM_DEBUG */
+}
+
+/* ---------------------------------------------------------------------- */
+/*
+ *    eppconfig_path should be setable  via /proc/sys.
+ */
+
+char eppconfig_path[256] = "/sbin/eppfpga";
+
+static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL };
+
+static int errno;
+
+static int exec_eppfpga(void *b)
+{
+	struct baycom_state *bc = (struct baycom_state *)b;
+	char modearg[256];
+	char portarg[16];
+        char *argv[] = { eppconfig_path, "-s", "-p", portarg, "-m", modearg, NULL};
+        int i;
+
+	/* set up arguments */
+	sprintf(modearg, "%sclk,%smodem,divider=%d%s,extstat",
+		bc->cfg.intclk ? "int" : "ext",
+		bc->cfg.extmodem ? "ext" : "int", bc->cfg.divider,
+		bc->cfg.loopback ? ",loopback" : "");
+	sprintf(portarg, "%d", bc->pdev->port->base);
+	printk(KERN_DEBUG "%s: %s -s -p %s -m %s\n", bc_drvname, eppconfig_path, portarg, modearg);
+
+        for (i = 0; i < current->files->max_fds; i++ )
+		if (current->files->fd[i]) 
+			close(i);
+        set_fs(KERNEL_DS);      /* Allow execve args to be in kernel space. */
+        current->uid = current->euid = current->fsuid = 0;
+        if (execve(eppconfig_path, argv, envp) < 0) {
+                printk(KERN_ERR "%s: failed to exec %s -s -p %s -m %s, errno = %d\n",
+                       bc_drvname, eppconfig_path, portarg, modearg, errno);
+                return -errno;
+        }
+        return 0;
+}
+
+
+/* eppconfig: called during ifconfig up to configure the modem */
+
+static int eppconfig(struct baycom_state *bc)
+{
+        int i, pid, r;
+	mm_segment_t fs;
+
+        pid = kernel_thread(exec_eppfpga, bc, CLONE_FS);
+        if (pid < 0) {
+                printk(KERN_ERR "%s: fork failed, errno %d\n", bc_drvname, -pid);
+                return pid;
+        }
+	fs = get_fs();
+        set_fs(KERNEL_DS);      /* Allow i to be in kernel space. */
+	r = waitpid(pid, &i, __WCLONE);
+	set_fs(fs);
+        if (r != pid) {
+                printk(KERN_ERR "%s: waitpid(%d) failed, returning %d\n",
+		       bc_drvname, pid, r);
+		return -1;
+        }
+	printk(KERN_DEBUG "%s: eppfpga returned %d\n", bc_drvname, i);
+	return i;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void epp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void inline do_kiss_params(struct baycom_state *bc,
+				  unsigned char *data, unsigned long len)
+{
+
+#ifdef KISS_VERBOSE
+#define PKP(a,b) printk(KERN_INFO "%s: channel params: " a "\n", bc->ifname, b)
+#else /* KISS_VERBOSE */	      
+#define PKP(a,b) 
+#endif /* KISS_VERBOSE */	      
+
+	if (len < 2)
+		return;
+	switch(data[0]) {
+	case PARAM_TXDELAY:
+		bc->ch_params.tx_delay = data[1];
+		PKP("TX delay = %ums", 10 * bc->ch_params.tx_delay);
+		break;
+	case PARAM_PERSIST:   
+		bc->ch_params.ppersist = data[1];
+		PKP("p persistence = %u", bc->ch_params.ppersist);
+		break;
+	case PARAM_SLOTTIME:  
+		bc->ch_params.slottime = data[1];
+		PKP("slot time = %ums", bc->ch_params.slottime);
+		break;
+	case PARAM_TXTAIL:    
+		bc->ch_params.tx_tail = data[1];
+		PKP("TX tail = %ums", bc->ch_params.tx_tail);
+		break;
+	case PARAM_FULLDUP:   
+		bc->ch_params.fulldup = !!data[1];
+		PKP("%s duplex", bc->ch_params.fulldup ? "full" : "half");
+		break;
+	default:
+		break;
+	}
+#undef PKP
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * high performance HDLC encoder
+ * yes, it's ugly, but generates pretty good code
+ */
+
+#define ENCODEITERA(j)                         \
+({                                             \
+        if (!(notbitstream & (0x1f0 << j)))    \
+                goto stuff##j;                 \
+  encodeend##j:                                \
+})
+
+#define ENCODEITERB(j)                                          \
+({                                                              \
+  stuff##j:                                                     \
+        bitstream &= ~(0x100 << j);                             \
+        bitbuf = (bitbuf & (((2 << j) << numbit) - 1)) |        \
+                ((bitbuf & ~(((2 << j) << numbit) - 1)) << 1);  \
+        numbit++;                                               \
+        notbitstream = ~bitstream;                              \
+        goto encodeend##j;                                      \
+})
+
+
+static void encode_hdlc(struct baycom_state *bc)
+{
+	struct sk_buff *skb;
+	unsigned char *wp, *bp;
+	int pkt_len;
+        unsigned bitstream, notbitstream, bitbuf, numbit, crc;
+	unsigned char crcarr[2];
+	
+	if (bc->hdlctx.bufcnt > 0)
+		return;
+	while ((skb = skb_dequeue(&bc->send_queue))) {
+		if (skb->data[0] != 0) {
+			do_kiss_params(bc, skb->data, skb->len);
+			dev_kfree_skb(skb);
+			continue;
+		}
+		pkt_len = skb->len-1; /* strip KISS byte */
+		if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) {
+			dev_kfree_skb(skb);
+			continue;
+		}
+		wp = bc->hdlctx.buf;
+		bp = skb->data+1;
+		crc = calc_crc_ccitt(bp, pkt_len);
+		crcarr[0] = crc;
+		crcarr[1] = crc >> 8;
+		*wp++ = 0x7e;
+                bitstream = bitbuf = numbit = 0;
+		while (pkt_len > -2) {
+                        bitstream >>= 8;
+                        bitstream |= ((unsigned int)*bp) << 8;
+                        bitbuf |= ((unsigned int)*bp) << numbit;
+                        notbitstream = ~bitstream;
+			bp++;
+			pkt_len--;
+			if (!pkt_len)
+				bp = crcarr;
+                        ENCODEITERA(0);
+                        ENCODEITERA(1);
+                        ENCODEITERA(2);
+                        ENCODEITERA(3);
+                        ENCODEITERA(4);
+                        ENCODEITERA(5);
+                        ENCODEITERA(6);
+                        ENCODEITERA(7);
+                        goto enditer;
+                        ENCODEITERB(0);
+                        ENCODEITERB(1);
+                        ENCODEITERB(2);
+                        ENCODEITERB(3);
+                        ENCODEITERB(4);
+                        ENCODEITERB(5);
+                        ENCODEITERB(6);
+                        ENCODEITERB(7);
+                  enditer:
+                        numbit += 8;
+                        while (numbit >= 8) {
+                                *wp++ = bitbuf;
+                                bitbuf >>= 8;
+                                numbit -= 8;
+                        }
+                }
+		bitbuf |= 0x7e7e << numbit;
+                numbit += 16;
+                while (numbit >= 8) {
+                        *wp++ = bitbuf;
+                        bitbuf >>= 8;
+                        numbit -= 8;
+                }
+		bc->hdlctx.bufptr = bc->hdlctx.buf;
+		bc->hdlctx.bufcnt = wp - bc->hdlctx.buf;
+		dev_kfree_skb(skb);
+		bc->stats.tx_packets++;
+		return;
+	}
+}
+
+/* ---------------------------------------------------------------------- */
+
+static unsigned short random_seed;
+
+static inline unsigned short random_num(void)
+{
+	random_seed = 28629 * random_seed + 157;
+	return random_seed;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void transmit(struct baycom_state *bc, int cnt, unsigned char stat)
+{
+	struct parport *pp = bc->pdev->port;
+	int i;
+
+	if (bc->hdlctx.state == tx_tail && !(stat & EPP_PTTBIT))
+		bc->hdlctx.state = tx_idle;
+	if (bc->hdlctx.state == tx_idle && bc->hdlctx.calibrate <= 0) {
+		if (bc->hdlctx.bufcnt <= 0)
+			encode_hdlc(bc);
+		if (bc->hdlctx.bufcnt <= 0)
+			return;
+		if (!bc->ch_params.fulldup) {
+			if (!(stat & EPP_DCDBIT)) {
+				bc->hdlctx.slotcnt = bc->ch_params.slottime;
+				return;
+			}
+			if ((--bc->hdlctx.slotcnt) > 0)
+				return;
+			bc->hdlctx.slotcnt = bc->ch_params.slottime;
+			if ((random_num() % 256) > bc->ch_params.ppersist)
+				return;
+		}
+	}
+	if (bc->hdlctx.state == tx_idle && bc->hdlctx.bufcnt > 0) {
+		bc->hdlctx.state = tx_keyup;
+		bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_delay);
+		bc->ptt_keyed++;
+	}
+	while (cnt > 0) {
+		switch (bc->hdlctx.state) {
+		case tx_keyup:
+			i = min(cnt, bc->hdlctx.flags);
+			cnt -= i;
+			bc->hdlctx.flags -= i;
+			if (bc->hdlctx.flags <= 0)
+				bc->hdlctx.state = tx_data;
+			for (; i > 0; i--)
+				parport_epp_write_data(pp, 0x7e);
+			break;
+
+		case tx_data:
+			if (bc->hdlctx.bufcnt <= 0) {
+				encode_hdlc(bc);
+				if (bc->hdlctx.bufcnt <= 0) {
+					bc->hdlctx.state = tx_tail;
+					bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_tail);
+					break;
+				}
+			}
+			i = min(cnt, bc->hdlctx.bufcnt);
+			bc->hdlctx.bufcnt -= i;
+			cnt -= i;
+			for (; i > 0; i--)
+				parport_epp_write_data(pp, *(bc->hdlctx.bufptr)++);
+			break;
+			
+		case tx_tail:
+			encode_hdlc(bc);
+			if (bc->hdlctx.bufcnt > 0) {
+				bc->hdlctx.state = tx_data;
+				break;
+			}
+			i = min(cnt, bc->hdlctx.flags);
+			if (i) {
+				cnt -= i;
+				bc->hdlctx.flags -= i;
+				for (; i > 0; i--)
+					parport_epp_write_data(pp, 0x7e);
+				break;
+			}
+
+		default:  /* fall through */
+			if (bc->hdlctx.calibrate <= 0)
+				return;
+			i = min(cnt, bc->hdlctx.calibrate);
+			cnt -= i;
+			bc->hdlctx.calibrate -= i;
+			for (; i > 0; i--)
+				parport_epp_write_data(pp, 0);
+			break;
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void do_rxpacket(struct device *dev)
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+	struct sk_buff *skb;
+	unsigned char *cp;
+	unsigned pktlen;
+
+	if (bc->hdlcrx.bufcnt < 4) 
+		return;
+	if (!check_crc_ccitt(bc->hdlcrx.buf, bc->hdlcrx.bufcnt)) 
+		return;
+	pktlen = bc->hdlcrx.bufcnt-2+1; /* KISS kludge */
+	if (!(skb = dev_alloc_skb(pktlen))) {
+		printk("%s: memory squeeze, dropping packet\n", bc->ifname);
+		bc->stats.rx_dropped++;
+		return;
+	}
+	skb->dev = dev;
+	cp = skb_put(skb, pktlen);
+	*cp++ = 0; /* KISS kludge */
+	memcpy(cp, bc->hdlcrx.buf, pktlen - 1);
+	skb->protocol = htons(ETH_P_AX25);
+	skb->mac.raw = skb->data;
+	netif_rx(skb);
+	bc->stats.rx_packets++;
+}
+
+#define DECODEITERA(j)                                                        \
+({                                                                            \
+        if (!(notbitstream & (0x0fc << j)))              /* flag or abort */  \
+                goto flgabrt##j;                                              \
+        if ((bitstream & (0x1f8 << j)) == (0xf8 << j))   /* stuffed bit */    \
+                goto stuff##j;                                                \
+  enditer##j:                                                                 \
+})
+
+#define DECODEITERB(j)                                                                 \
+({                                                                                     \
+  flgabrt##j:                                                                          \
+        if (!(notbitstream & (0x1fc << j))) {              /* abort received */        \
+                state = 0;                                                             \
+                goto enditer##j;                                                       \
+        }                                                                              \
+        if ((bitstream & (0x1fe << j)) != (0x0fc << j))   /* flag received */          \
+                goto enditer##j;                                                       \
+        if (state)                                                                     \
+                do_rxpacket(dev);                                                      \
+        bc->hdlcrx.bufcnt = 0;                                                         \
+        bc->hdlcrx.bufptr = bc->hdlcrx.buf;                                            \
+        state = 1;                                                                     \
+        numbits = 7-j;                                                                 \
+        goto enditer##j;                                                               \
+  stuff##j:                                                                            \
+        numbits--;                                                                     \
+        bitbuf = (bitbuf & ((~0xff) << j)) | ((bitbuf & ~((~0xff) << j)) << 1);        \
+        goto enditer##j;                                                               \
+})
+        
+static void receive(struct device *dev, int cnt)
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+	struct parport *pp = bc->pdev->port;
+        unsigned int bitbuf, notbitstream, bitstream, numbits, state;
+        unsigned char ch;
+        
+        numbits = bc->hdlcrx.numbits;
+	state = bc->hdlcrx.state;
+	bitstream = bc->hdlcrx.bitstream;
+	bitbuf = bc->hdlcrx.bitbuf;
+	for (; cnt > 0; cnt--) {
+		ch = parport_epp_read_data(pp);
+		bitstream >>= 8;
+                bitstream |= ch << 8;
+                bitbuf >>= 8;
+                bitbuf |= ch << 8;
+                numbits += 8;
+                notbitstream = ~bitstream;
+                DECODEITERA(0);
+                DECODEITERA(1);
+                DECODEITERA(2);
+                DECODEITERA(3);
+                DECODEITERA(4);
+                DECODEITERA(5);
+                DECODEITERA(6);
+                DECODEITERA(7);
+                goto enddec;
+                DECODEITERB(0);
+                DECODEITERB(1);
+                DECODEITERB(2);
+                DECODEITERB(3);
+                DECODEITERB(4);
+                DECODEITERB(5);
+                DECODEITERB(6);
+                DECODEITERB(7);
+          enddec:
+                while (state && numbits >= 8) {
+                        if (bc->hdlcrx.bufcnt >= TXBUFFER_SIZE) {
+                                state = 0;
+                        } else {
+				*(bc->hdlcrx.bufptr)++ = bitbuf >> (16-numbits);
+				bc->hdlcrx.bufcnt++;
+				numbits -= 8;
+			}
+		}
+        }
+        bc->hdlcrx.numbits = numbits;
+	bc->hdlcrx.state = state;
+	bc->hdlcrx.bitstream = bitstream;
+	bc->hdlcrx.bitbuf = bitbuf;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef __i386__
+#define GETTICK(x)                                                \
+({                                                                \
+	if (current_cpu_data.x86_capability & 0x10)               \
+		__asm__ __volatile__("rdtsc" : "=a" (x) : : "dx");\
+})
+#else /* __i386__ */
+#define GETTICK(x)
+#endif /* __i386__ */
+
+static void epp_bh(struct device *dev)
+{
+	struct baycom_state *bc;
+	struct parport *pp;
+	unsigned char stat;
+	unsigned int time1 = 0, time2 = 0, time3 = 0;
+	int cnt, cnt2;
+	
+	baycom_paranoia_check_void(dev, "epp_bh");
+	bc = (struct baycom_state *)dev->priv;
+	if (!bc->bh_running)
+		return;
+	baycom_int_freq(bc);
+	pp = bc->pdev->port;
+	/* update status */
+	bc->stat = stat = parport_epp_read_addr(pp);
+	bc->debug_vals.last_pllcorr = stat;
+	GETTICK(time1);
+	if (bc->modem == EPP_FPGAEXTSTATUS) {
+		/* get input count */
+		parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|1);
+		cnt = parport_epp_read_addr(pp);
+		cnt |= parport_epp_read_addr(pp) << 8;
+		cnt &= 0x7fff;
+		/* get output count */
+		parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|2);
+		cnt2 = parport_epp_read_addr(pp);
+		cnt2 |= parport_epp_read_addr(pp) << 8;
+		cnt2 = 16384 - (cnt2 & 0x7fff);
+		/* return to normal */
+		parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE);
+		transmit(bc, cnt2, stat);
+		GETTICK(time2);
+		receive(dev, cnt);
+		bc->stat = stat = parport_epp_read_addr(pp);
+	} else {
+		/* try to tx */
+		switch (stat & (EPP_NTAEF|EPP_NTHF)) {
+		case EPP_NTHF:
+			cnt = 2048 - 256;
+			break;
+		
+		case EPP_NTAEF:
+			cnt = 2048 - 1793;
+			break;
+		
+		case 0:
+			cnt = 0;
+			break;
+		
+		default:
+			cnt = 2048 - 1025;
+			break;
+		}
+		transmit(bc, cnt, stat);
+		GETTICK(time2);
+		/* do receiver */
+		while ((stat & (EPP_NRAEF|EPP_NRHF)) != EPP_NRHF) {
+			switch (stat & (EPP_NRAEF|EPP_NRHF)) {
+			case EPP_NRAEF:
+				cnt = 1025;
+				break;
+
+			case 0:
+				cnt = 1793;
+				break;
+
+			default:
+				cnt = 256;
+				break;
+			}
+			receive(dev, cnt);
+			stat = parport_epp_read_addr(pp);
+			if (parport_epp_check_timeout(pp))
+				goto epptimeout;
+		}
+		cnt = 0;
+		if (bc->bitrate < 50000)
+			cnt = 256;
+		else if (bc->bitrate < 100000)
+			cnt = 128;
+		while (cnt > 0 && stat & EPP_NREF) {
+			receive(dev, 1);
+			cnt--;
+			stat = parport_epp_read_addr(pp);
+		}
+	}
+	GETTICK(time3);
+#ifdef BAYCOM_DEBUG
+	bc->debug_vals.mod_cycles = time2 - time1;
+	bc->debug_vals.demod_cycles = time3 - time2;
+#endif /* BAYCOM_DEBUG */
+	if (parport_epp_check_timeout(pp)) 
+		goto epptimeout;
+	queue_task(&bc->run_bh, &tq_timer);
+	return;
+ epptimeout:
+	printk(KERN_ERR "%s: EPP timeout!\n", bc_drvname);
+}
+
+/* ---------------------------------------------------------------------- */
+/*
+ * ===================== network driver interface =========================
+ */
+
+static int baycom_send_packet(struct sk_buff *skb, struct device *dev)
+{
+	struct baycom_state *bc;
+
+	baycom_paranoia_check(dev, "baycom_send_packet", 0);
+	bc = (struct baycom_state *)dev->priv;
+	skb_queue_tail(&bc->send_queue, skb);
+	dev->trans_start = jiffies;	
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_set_mac_address(struct device *dev, void *addr)
+{
+	struct sockaddr *sa = (struct sockaddr *)addr;
+
+	/* addr is an AX.25 shifted ASCII mac address */
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); 
+	return 0;                                         
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct net_device_stats *baycom_get_stats(struct device *dev)
+{
+	struct baycom_state *bc;
+
+	baycom_paranoia_check(dev, "baycom_get_stats", NULL);
+	bc = (struct baycom_state *)dev->priv;
+	/* 
+	 * Get the current statistics.  This may be called with the
+	 * card open or closed. 
+	 */
+	return &bc->stats;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int epp_preempt(void *handle)
+{
+        /* we cannot relinquish the port in the middle of an operation */
+        return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void epp_wakeup(void *handle)
+{
+        struct device *dev = (struct device *)handle;
+        struct baycom_state *bc;
+
+	baycom_paranoia_check_void(dev, "epp_wakeup");
+        bc = (struct baycom_state *)dev->priv;
+        printk(KERN_DEBUG "baycom_epp: %s: why am I being woken up?\n", dev->name);
+        if (!parport_claim(bc->pdev))
+                printk(KERN_DEBUG "baycom_epp: %s: I'm broken.\n", dev->name);
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int epp_open(struct device *dev)
+{
+	struct baycom_state *bc;
+        struct parport *pp;
+	const struct tq_struct run_bh = {
+		0, 0, (void *)(void *)epp_bh, dev
+	};
+	unsigned int i, j;
+	unsigned char stat;
+	unsigned long tstart;
+	
+	baycom_paranoia_check(dev, "epp_open", -ENXIO);
+	bc = (struct baycom_state *)dev->priv;
+	if (dev->start)
+		return 0;
+        pp = parport_enumerate();
+        while (pp && pp->base != dev->base_addr) 
+                pp = pp->next;
+        if (!pp) {
+                printk(KERN_ERR "%s: parport at 0x%lx unknown\n", bc_drvname, dev->base_addr);
+                return -ENXIO;
+        }
+#if 0
+        if (pp->irq < 0) {
+                printk(KERN_ERR "%s: parport at 0x%lx has no irq\n", bc_drvname, pp->base);
+                return -ENXIO;
+        }
+#endif
+	memset(&bc->modem, 0, sizeof(bc->modem));
+        if (!(bc->pdev = parport_register_device(pp, dev->name, epp_preempt, epp_wakeup, 
+                                                 epp_interrupt, PARPORT_DEV_LURK, dev))) {
+                printk(KERN_ERR "%s: cannot register parport at 0x%lx\n", bc_drvname, pp->base);
+                return -ENXIO;
+        }
+        if (parport_claim(bc->pdev)) {
+                printk(KERN_ERR "%s: parport at 0x%lx busy\n", bc_drvname, pp->base);
+                parport_unregister_device(bc->pdev);
+                return -EBUSY;
+        }
+	if (!(pp->modes & (PARPORT_MODE_PCECPEPP|PARPORT_MODE_PCEPP))) {
+                printk(KERN_ERR "%s: parport at 0x%lx does not support any EPP mode\n",
+		       bc_drvname, pp->base);
+                parport_unregister_device(bc->pdev);
+                return -EIO;		
+	}
+        dev->irq = /*pp->irq*/ 0;
+	bc->run_bh = run_bh;
+	bc->bh_running = 1;
+	if (pp->modes & PARPORT_MODE_PCECPEPP) {
+		printk(KERN_INFO "%s: trying to enable EPP mode\n", bc_drvname);
+		parport_frob_econtrol(pp, 0xe0, 0x80);
+	}
+        /* bc->pdev->port->ops->change_mode(bc->pdev->port, PARPORT_MODE_PCEPP);  not yet implemented */
+	bc->modem = EPP_CONVENTIONAL;
+	if (eppconfig(bc))
+		printk(KERN_INFO "%s: no FPGA detected, assuming conventional EPP modem\n", bc_drvname);
+	else
+		bc->modem = /*EPP_FPGA*/ EPP_FPGAEXTSTATUS;
+	parport_write_control(pp, LPTCTRL_PROGRAM); /* prepare EPP mode; we aren't using interrupts */
+	/* reset the modem */
+	parport_epp_write_addr(pp, 0);
+	parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE);
+	/* autoprobe baud rate */
+	tstart = jiffies;
+	i = 0;
+	while ((signed)(jiffies-tstart-HZ/3) < 0) {
+		stat = parport_epp_read_addr(pp);
+		if ((stat & (EPP_NRAEF|EPP_NRHF)) == EPP_NRHF) {
+			schedule();
+			continue;
+		}
+		for (j = 0; j < 256; j++)
+			parport_epp_read_data(pp);
+		i += 256;
+	}
+	for (j = 0; j < 256; j++) {
+		stat = parport_epp_read_addr(pp);
+		if (!(stat & EPP_NREF))
+			break;
+		parport_epp_read_data(pp);
+		i++;
+	}
+	tstart = jiffies - tstart;
+	bc->bitrate = i * (8 * HZ) / tstart;
+	j = 1;
+	i = bc->bitrate >> 3;
+	while (j < 7 && i > 150) {
+		j++;
+		i >>= 1;
+	}
+	printk(KERN_INFO "%s: autoprobed bitrate: %d  int divider: %d  int rate: %d\n", 
+	       bc_drvname, bc->bitrate, j, bc->bitrate >> (j+2));
+	parport_epp_write_addr(pp, EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE/*|j*/);
+	/*
+	 * initialise hdlc variables
+	 */
+	bc->hdlcrx.state = 0;
+	bc->hdlcrx.numbits = 0;
+	bc->hdlctx.state = tx_idle;
+	bc->hdlctx.bufcnt = 0;
+	bc->hdlctx.slotcnt = bc->ch_params.slottime;
+	bc->hdlctx.calibrate = 0;
+        dev->start = 1;
+       	dev->tbusy = 0;
+	dev->interrupt = 0;
+	/* start the bottom half stuff */
+	queue_task(&bc->run_bh, &tq_timer);
+	MOD_INC_USE_COUNT;
+	return 0;
+
+#if 0
+  errreturn:
+        parport_release(bc->pdev);
+        parport_unregister_device(bc->pdev);
+	return -EIO;
+#endif
+}
+
+/* --------------------------------------------------------------------- */
+
+static int epp_close(struct device *dev)
+{
+	struct baycom_state *bc;
+	struct parport *pp;
+	struct sk_buff *skb;
+
+	baycom_paranoia_check(dev, "epp_close", -EINVAL);
+	if (!dev->start)
+		return 0;
+	bc = (struct baycom_state *)dev->priv;
+	pp = bc->pdev->port;
+	bc->bh_running = 0;
+	dev->start = 0;
+	dev->tbusy = 1;
+	run_task_queue(&tq_timer);  /* dequeue bottom half */
+	bc->stat = EPP_DCDBIT;
+	parport_epp_write_addr(pp, 0);
+	parport_write_control(pp, 0); /* reset the adapter */
+        parport_release(bc->pdev);
+        parport_unregister_device(bc->pdev);
+        /* Free any buffers left in the hardware transmit queue */
+        while ((skb = skb_dequeue(&bc->send_queue)))
+			dev_kfree_skb(skb);
+	printk(KERN_INFO "%s: close epp at iobase 0x%lx irq %u\n",
+	       bc_drvname, dev->base_addr, dev->irq);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_setmode(struct baycom_state *bc, const char *modestr)
+{
+	const char *cp;
+
+	if (strstr(modestr,"intclk"))
+		bc->cfg.intclk = 1;
+	if (strstr(modestr,"extclk"))
+		bc->cfg.intclk = 0;
+	if (strstr(modestr,"intmodem"))
+		bc->cfg.extmodem = 0;
+	if (strstr(modestr,"extmodem"))
+		bc->cfg.extmodem = 1;
+	if (strstr(modestr,"noloopback"))
+		bc->cfg.loopback = 0;
+	if (strstr(modestr,"loopback"))
+		bc->cfg.loopback = 1;
+	if ((cp = strstr(modestr,"divider="))) {
+		bc->cfg.divider = simple_strtoul(cp+8, NULL, 0);
+		if (bc->cfg.divider < 1)
+			bc->cfg.divider = 1;
+		if (bc->cfg.divider > 1023)
+			bc->cfg.divider = 1023;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	struct baycom_state *bc;
+	struct baycom_ioctl bi;
+	struct hdlcdrv_ioctl hi;
+	struct sm_ioctl si;
+
+	baycom_paranoia_check(dev, "baycom_ioctl", -EINVAL);
+	bc = (struct baycom_state *)dev->priv;
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+	if (get_user(cmd, (int *)ifr->ifr_data))
+		return -EFAULT;
+#ifdef BAYCOM_DEBUG
+	if (cmd == BAYCOMCTL_GETDEBUG) {
+		bi.data.dbg.debug1 = bc->ptt_keyed;
+		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
+		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
+		bc->debug_vals.last_intcnt = 0;
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == SMCTL_GETDEBUG) {
+                si.data.dbg.int_rate = bc->debug_vals.last_intcnt;
+                si.data.dbg.mod_cycles = bc->debug_vals.mod_cycles;
+                si.data.dbg.demod_cycles = bc->debug_vals.demod_cycles;
+                si.data.dbg.dma_residue = 0;
+                bc->debug_vals.mod_cycles = bc->debug_vals.demod_cycles = 0;
+		bc->debug_vals.last_intcnt = 0;
+                if (copy_to_user(ifr->ifr_data, &si, sizeof(si)))
+                        return -EFAULT;
+                return 0;
+	}
+#endif /* BAYCOM_DEBUG */
+
+	if (copy_from_user(&hi, ifr->ifr_data, sizeof(hi)))
+		return -EFAULT;
+	switch (hi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+	case HDLCDRVCTL_GETCHANNELPAR:
+		hi.data.cp.tx_delay = bc->ch_params.tx_delay;
+		hi.data.cp.tx_tail = bc->ch_params.tx_tail;
+		hi.data.cp.slottime = bc->ch_params.slottime;
+		hi.data.cp.ppersist = bc->ch_params.ppersist;
+		hi.data.cp.fulldup = bc->ch_params.fulldup;
+		break;
+
+	case HDLCDRVCTL_SETCHANNELPAR:
+		if (!suser())
+			return -EACCES;
+		bc->ch_params.tx_delay = hi.data.cp.tx_delay;
+		bc->ch_params.tx_tail = hi.data.cp.tx_tail;
+		bc->ch_params.slottime = hi.data.cp.slottime;
+		bc->ch_params.ppersist = hi.data.cp.ppersist;
+		bc->ch_params.fulldup = hi.data.cp.fulldup;
+		bc->hdlctx.slotcnt = 1;
+		return 0;
+		
+	case HDLCDRVCTL_GETMODEMPAR:
+		hi.data.mp.iobase = dev->base_addr;
+		hi.data.mp.irq = dev->irq;
+		hi.data.mp.dma = dev->dma;
+		hi.data.mp.dma2 = 0;
+		hi.data.mp.seriobase = 0;
+		hi.data.mp.pariobase = 0;
+		hi.data.mp.midiiobase = 0;
+		break;
+
+	case HDLCDRVCTL_SETMODEMPAR:
+		if ((!suser()) || dev->start)
+			return -EACCES;
+		dev->base_addr = hi.data.mp.iobase;
+		dev->irq = /*hi.data.mp.irq*/0;
+		dev->dma = /*hi.data.mp.dma*/0;
+		return 0;	
+		
+	case HDLCDRVCTL_GETSTAT:
+		hi.data.cs.ptt = !!(bc->stat & EPP_PTTBIT);
+		hi.data.cs.dcd = !(bc->stat & EPP_DCDBIT);
+		hi.data.cs.ptt_keyed = bc->ptt_keyed;
+		hi.data.cs.tx_packets = bc->stats.tx_packets;
+		hi.data.cs.tx_errors = bc->stats.tx_errors;
+		hi.data.cs.rx_packets = bc->stats.rx_packets;
+		hi.data.cs.rx_errors = bc->stats.rx_errors;
+		break;		
+
+	case HDLCDRVCTL_OLDGETSTAT:
+		hi.data.ocs.ptt = !!(bc->stat & EPP_PTTBIT);
+		hi.data.ocs.dcd = !(bc->stat & EPP_DCDBIT);
+		hi.data.ocs.ptt_keyed = bc->ptt_keyed;
+		break;		
+
+	case HDLCDRVCTL_CALIBRATE:
+		bc->hdlctx.calibrate = hi.data.calibrate * bc->bitrate / 8;
+		return 0;
+
+	case HDLCDRVCTL_DRIVERNAME:
+		strncpy(hi.data.drivername, "baycom_epp", sizeof(hi.data.drivername));
+		break;
+		
+	case HDLCDRVCTL_GETMODE:
+		sprintf(hi.data.modename, "%sclk,%smodem,divider=%d%s", 
+			bc->cfg.intclk ? "int" : "ext",
+			bc->cfg.extmodem ? "ext" : "int", bc->cfg.divider,
+			bc->cfg.loopback ? ",loopback" : "");
+		break;
+
+	case HDLCDRVCTL_SETMODE:
+		if (!suser() || dev->start)
+			return -EACCES;
+		hi.data.modename[sizeof(hi.data.modename)-1] = '\0';
+		return baycom_setmode(bc, hi.data.modename);
+
+	case HDLCDRVCTL_MODELIST:
+		strncpy(hi.data.modename, "intclk,extclk,intmodem,extmodem,divider=x",
+			sizeof(hi.data.modename));
+		break;
+
+	case HDLCDRVCTL_MODEMPARMASK:
+		return HDLCDRV_PARMASK_IOBASE;
+
+	}
+	if (copy_to_user(ifr->ifr_data, &hi, sizeof(hi)))
+		return -EFAULT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+static int baycom_probe(struct device *dev)
+{
+	static char ax25_bcast[AX25_ADDR_LEN] = {
+		'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1
+	};
+	static char ax25_nocall[AX25_ADDR_LEN] = {
+		'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1
+	};
+	const struct hdlcdrv_channel_params dflt_ch_params = { 
+		20, 2, 10, 40, 0 
+	};
+	struct baycom_state *bc;
+
+	if (!dev)
+		return -ENXIO;
+	baycom_paranoia_check(dev, "baycom_probe", -ENXIO);
+	/*
+	 * not a real probe! only initialize data structures
+	 */
+	bc = (struct baycom_state *)dev->priv;
+	/*
+	 * initialize the baycom_state struct
+	 */
+	bc->ch_params = dflt_ch_params;
+	bc->ptt_keyed = 0;
+
+	/*
+	 * initialize the device struct
+	 */
+	dev->open = epp_open;
+	dev->stop = epp_close;
+	dev->do_ioctl = baycom_ioctl;
+	dev->hard_start_xmit = baycom_send_packet;
+	dev->get_stats = baycom_get_stats;
+
+	/* Fill in the fields of the device structure */
+	dev_init_buffers(dev);
+
+	skb_queue_head_init(&bc->send_queue);
+	
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+	dev->hard_header = ax25_encapsulate;
+	dev->rebuild_header = ax25_rebuild_header;
+#else /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+	dev->hard_header = NULL;
+	dev->rebuild_header = NULL;
+#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+	dev->set_mac_address = baycom_set_mac_address;
+	
+	dev->type = ARPHRD_AX25;           /* AF_AX25 device */
+	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+	dev->mtu = AX25_DEF_PACLEN;        /* eth_mtu is the default */
+	dev->addr_len = AX25_ADDR_LEN;     /* sizeof an ax.25 address */
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+	memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN);
+
+	/* New style flags */
+	dev->flags = 0;
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(int baycom_epp_init(void))
+{
+	struct device *dev;
+	int i, found = 0;
+	char set_hw = 1;
+	struct baycom_state *bc;
+
+	printk(bc_drvinfo);
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		dev = baycom_device+i;
+		if (!baycom_ports[i].mode)
+			set_hw = 0;
+		if (!set_hw)
+			baycom_ports[i].iobase = 0;
+		memset(dev, 0, sizeof(struct device));
+		if (!(bc = dev->priv = kmalloc(sizeof(struct baycom_state), GFP_KERNEL)))
+			return -ENOMEM;
+		/*
+		 * initialize part of the baycom_state struct
+		 */
+		memset(bc, 0, sizeof(struct baycom_state));
+		bc->magic = BAYCOM_MAGIC;
+		sprintf(bc->ifname, "bce%d", i);
+		/*
+		 * initialize part of the device struct
+		 */
+		dev->name = bc->ifname;
+		dev->if_port = 0;
+		dev->init = baycom_probe;
+		dev->start = 0;
+		dev->tbusy = 1;
+		dev->base_addr = baycom_ports[i].iobase;
+		dev->irq = 0;
+		dev->dma = 0;
+		if (register_netdev(dev)) {
+			printk(KERN_WARNING "%s: cannot register net device %s\n", bc_drvname, bc->ifname);
+			kfree(dev->priv);
+			return -ENXIO;
+		}
+		if (set_hw && baycom_setmode(bc, baycom_ports[i].mode))
+			set_hw = 0;
+		found++;
+	}
+	if (!found)
+		return -ENXIO;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+static const char *mode[NR_PORTS] = { "epp", };
+static int iobase[NR_PORTS] = { 0x378, };
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(mode, "s");
+MODULE_PARM_DESC(mode, "baycom operating mode; epp");
+MODULE_PARM(iobase, "i");
+MODULE_PARM_DESC(iobase, "baycom io base address");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Baycom epp amateur radio modem driver");
+
+#endif
+
+__initfunc(int init_module(void))
+{
+        int i;
+
+        for (i = 0; (i < NR_PORTS) && (mode[i]); i++) {
+                baycom_ports[i].mode = mode[i];
+                baycom_ports[i].iobase = iobase[i];
+        }
+        if (i < NR_PORTS-1)
+                baycom_ports[i+1].mode = NULL;
+        return baycom_epp_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+	struct device *dev;
+	struct baycom_state *bc;
+	int i;
+
+	for(i = 0; i < NR_PORTS; i++) {
+		dev = baycom_device+i;
+		bc = (struct baycom_state *)dev->priv;
+		if (bc) {
+			if (bc->magic == BAYCOM_MAGIC) {
+				unregister_netdev(dev);
+				kfree(dev->priv);
+			} else
+				printk(paranoia_str, "cleanup_module");
+		}
+	}
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: baycom=io,mode
+ * mode: epp
+ */
+
+__initfunc(void baycom_epp_setup(char *str, int *ints))
+{
+	int i;
+
+	for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++);
+	if ((i >= NR_PORTS) || (ints[0] < 1)) {
+		printk(KERN_INFO "%s: too many or invalid interface "
+		       "specifications\n", bc_drvname);
+		return;
+	}
+	baycom_ports[i].mode = str;
+	baycom_ports[i].irq = ints[1];
+	if (i < NR_PORTS-1)
+		baycom_ports[i+1].mode = NULL;
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.104/linux/drivers/net/hamradio/baycom_par.c linux/drivers/net/hamradio/baycom_par.c
--- v2.1.104/linux/drivers/net/hamradio/baycom_par.c	Wed Dec 31 11:55:54 1997
+++ linux/drivers/net/hamradio/baycom_par.c	Sun Jun  7 11:13:45 1998
@@ -329,9 +329,7 @@
 
 static void par96_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	struct parport *pp = (struct parport *)dev_id;
-	struct pardevice *pd = pp->cad;
-	struct device *dev = (struct device *)pd->private;
+	struct device *dev = (struct device *)dev_id;
 	struct baycom_state *bc = (struct baycom_state *)dev->priv;
 
 	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
@@ -347,13 +345,14 @@
 		par96_rx(dev, bc);
 		if (--bc->modem.arb_divider <= 0) {
 			bc->modem.arb_divider = 6;
-			sti();
+			__sti();
 			hdlcdrv_arbitrate(dev, &bc->hdrv);
 		}
 	}
-	sti();
+	__sti();
 	hdlcdrv_transmitter(dev, &bc->hdrv);
 	hdlcdrv_receiver(dev, &bc->hdrv);
+        __cli();
 }
 
 /* --------------------------------------------------------------------- */
@@ -409,6 +408,7 @@
 	}
 	dev->irq = pp->irq;
 	/* bc->pdev->port->ops->change_mode(bc->pdev->port, PARPORT_MODE_PCSPP);  not yet implemented */
+        bc->hdrv.par.bitrate = 9600;
 	/* switch off PTT */
 	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev));
 	/*bc->pdev->port->ops->enable_irq(bc->pdev->port);  not yet implemented */
diff -u --recursive --new-file v2.1.104/linux/drivers/net/hamradio/baycom_ser_fdx.c linux/drivers/net/hamradio/baycom_ser_fdx.c
--- v2.1.104/linux/drivers/net/hamradio/baycom_ser_fdx.c	Sun Dec 21 17:41:24 1997
+++ linux/drivers/net/hamradio/baycom_ser_fdx.c	Sun Jun  7 11:13:45 1998
@@ -3,7 +3,7 @@
 /*
  *	baycom_ser_fdx.c  -- baycom ser12 fullduplex radio modem driver.
  *
- *	Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *	Copyright (C) 1997-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
@@ -34,6 +34,14 @@
  *          port, the kernel driver for serial ports cannot be used, and this
  *          driver only supports standard serial hardware (8250, 16450, 16550A)
  *
+ *          This modem usually draws its supply current out of the otherwise unused
+ *          TXD pin of the serial port. Thus a contignuous stream of 0x00-bytes
+ *          is transmitted to achieve a positive supply voltage.
+ *
+ *  hsk:    This is a 4800 baud FSK modem, designed for TNC use. It works fine
+ *          in 'baycom-mode' :-)  In contrast to the TCM3105 modem, power is
+ *          externally supplied. So there's no need to provide the 0x00-byte-stream
+ *          when receiving or idle, which drastically reduces interrupt load.
  *
  *  Command line options (insmod command line)
  *
@@ -49,6 +57,9 @@
  *   0.3  26.04.97  init code/data tagged
  *   0.4  08.07.97  alternative ser12 decoding algorithm (uses delta CTS ints)
  *   0.5  11.11.97  ser12/par96 split into separate files
+ *   0.6  24.01.98  Thorsten Kranzkowski, dl8bcu and Thomas Sailer:
+ *                  reduced interrupt load in transmit case
+ *                  reworked receiver
  */
 
 /*****************************************************************************/
@@ -62,8 +73,10 @@
 #include <linux/ioport.h>
 #include <linux/in.h>
 #include <linux/string.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <asm/uaccess.h>
 #include <asm/system.h>
-#include <asm/bitops.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
@@ -73,56 +86,6 @@
 
 /* --------------------------------------------------------------------- */
 
-/*
- * currently this module is supposed to support both module styles, i.e.
- * the old one present up to about 2.1.9, and the new one functioning
- * starting with 2.1.21. The reason is I have a kit allowing to compile
- * this module also under 2.0.x which was requested by several people.
- * This will go in 2.2
- */
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE >= 0x20100
-#include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#include <linux/mm.h>
-
-#undef put_user
-#undef get_user
-
-#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
-#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
-
-extern inline int copy_from_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_READ, from, n);
-        if (i)
-                return i;
-        memcpy_fromfs(to, from, n);
-        return 0;
-}
-
-extern inline int copy_to_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_WRITE, to, n);
-        if (i)
-                return i;
-        memcpy_tofs(to, from, n);
-        return 0;
-}
-#endif
-
-#if LINUX_VERSION_CODE >= 0x20123
-#include <linux/init.h>
-#else
-#define __init
-#define __initdata
-#define __initfunc(x) x
-#endif
-
-/* --------------------------------------------------------------------- */
-
 #define BAYCOM_DEBUG
 
 /*
@@ -133,8 +96,8 @@
 /* --------------------------------------------------------------------- */
 
 static const char bc_drvname[] = "baycom_ser_fdx";
-static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "baycom_ser_fdx: version 0.5 compiled " __TIME__ " " __DATE__ "\n";
+static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1997-1998 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom_ser_fdx: version 0.6 compiled " __TIME__ " " __DATE__ "\n";
 
 /* --------------------------------------------------------------------- */
 
@@ -172,22 +135,18 @@
 struct baycom_state {
 	struct hdlcdrv_state hdrv;
 
-	unsigned int baud, baud_us8, baud_arbdiv;
+	unsigned int baud, baud_us, baud_arbdiv, baud_uartdiv, baud_dcdtimeout;
 	unsigned int options;
 
 	struct modem_state {
-		short arb_divider;
 		unsigned char flags;
+		unsigned char ptt;
 		unsigned int shreg;
 		struct modem_state_ser12 {
 			unsigned char tx_bit;
-			int dcd_sum0, dcd_sum1, dcd_sum2;
-			unsigned char last_sample;
 			unsigned char last_rxbit;
-			unsigned int dcd_shreg;
-			unsigned int dcd_time;
-			unsigned int bit_pll;
-			unsigned long last_jiffies;
+			int dcd_sum0, dcd_sum1, dcd_sum2;
+			int dcd_time;
 			unsigned int pll_time;
 			unsigned int txshreg;
 		} ser12;
@@ -236,12 +195,36 @@
 
 /* --------------------------------------------------------------------- */
 
-extern inline unsigned int hweight16(unsigned short w)
+static inline void ser12_set_divisor(struct device *dev,
+                                     unsigned int divisor)
+{
+        outb(0x81, LCR(dev->base_addr));        /* DLAB = 1 */
+        outb(divisor, DLL(dev->base_addr));
+        outb(divisor >> 8, DLM(dev->base_addr));
+        outb(0x01, LCR(dev->base_addr));        /* word length = 6 */
+        /*
+         * make sure the next interrupt is generated;
+         * 0 must be used to power the modem; the modem draws its
+         * power from the TxD line
+         */
+        outb(0x00, THR(dev->base_addr));
+        /*
+         * it is important not to set the divider while transmitting;
+         * this reportedly makes some UARTs generating interrupts
+         * in the hundredthousands per second region
+         * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
+         */
+}
+
+/* --------------------------------------------------------------------- */
+
+#if 0
+extern inline unsigned int hweight16(unsigned int w)
         __attribute__ ((unused));
-extern inline unsigned int hweight8(unsigned char w)
+extern inline unsigned int hweight8(unsigned int w)
         __attribute__ ((unused));
 
-extern inline unsigned int hweight16(unsigned short w)
+extern inline unsigned int hweight16(unsigned int w)
 {
         unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
         res = (res & 0x3333) + ((res >> 2) & 0x3333);
@@ -249,85 +232,67 @@
         return (res & 0x00FF) + ((res >> 8) & 0x00FF);
 }
 
-extern inline unsigned int hweight8(unsigned char w)
+extern inline unsigned int hweight8(unsigned int w)
 {
         unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
         res = (res & 0x33) + ((res >> 2) & 0x33);
         return (res & 0x0F) + ((res >> 4) & 0x0F);
 }
+#endif
 
 /* --------------------------------------------------------------------- */
 
-static __inline__ void ser12_rxsample(struct device *dev, struct baycom_state *bc, unsigned char news)
+static __inline__ void ser12_rx(struct device *dev, struct baycom_state *bc, struct timeval *tv, unsigned char curs)
 {
-	bc->modem.ser12.dcd_shreg <<= 1;
-	bc->modem.ser12.bit_pll += 0x2000;
-	if (bc->modem.ser12.last_sample != news) {
-		bc->modem.ser12.last_sample = news;
-		bc->modem.ser12.dcd_shreg |= 1;
-		if (bc->modem.ser12.bit_pll < 0x9000)
-			bc->modem.ser12.bit_pll += 0x1000;
-		else
-			bc->modem.ser12.bit_pll -= 0x1000;
-		bc->modem.ser12.dcd_sum0 += 4 * hweight8(bc->modem.ser12.dcd_shreg & 0x38)
-			- hweight16(bc->modem.ser12.dcd_shreg & 0x7c0);
-	}
-	hdlcdrv_channelbit(&bc->hdrv, !!bc->modem.ser12.last_sample);
-	if ((--bc->modem.ser12.dcd_time) <= 0) {
-		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + 
-					   bc->modem.ser12.dcd_sum1 + 
-					   bc->modem.ser12.dcd_sum2) < 0);
+	int timediff;
+	int bdus8 = bc->baud_us >> 3;
+	int bdus4 = bc->baud_us >> 2;
+	int bdus2 = bc->baud_us >> 1;
+
+	timediff = 1000000 + tv->tv_usec - bc->modem.ser12.pll_time;
+	while (timediff >= 500000)
+		timediff -= 1000000;
+	while (timediff >= bdus2) {
+		timediff -= bc->baud_us;
+		bc->modem.ser12.pll_time += bc->baud_us;
+		bc->modem.ser12.dcd_time--;
+		/* first check if there is room to add a bit */
+		if (bc->modem.shreg & 1) {
+			hdlcdrv_putbits(&bc->hdrv, (bc->modem.shreg >> 1) ^ 0xffff);
+			bc->modem.shreg = 0x10000;
+		}
+		/* add a one bit */
+		bc->modem.shreg >>= 1;
+	}
+	if (bc->modem.ser12.dcd_time <= 0) {
+		if (bc->options & BAYCOM_OPTIONS_SOFTDCD)
+			hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + 
+						   bc->modem.ser12.dcd_sum1 + 
+						   bc->modem.ser12.dcd_sum2) < 0);
 		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
 		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
 		bc->modem.ser12.dcd_sum0 = 2; /* slight bias */
-		bc->modem.ser12.dcd_time = 120;
+		bc->modem.ser12.dcd_time += 120;
 	}
-	if (bc->modem.ser12.bit_pll >= 0x10000) {
-		bc->modem.ser12.bit_pll &= 0xffff;
-		bc->modem.shreg >>= 1;
-		if (bc->modem.ser12.last_rxbit == bc->modem.ser12.last_sample)
-			bc->modem.shreg |=  0x10000;
-		bc->modem.ser12.last_rxbit = bc->modem.ser12.last_sample;
-		if (bc->modem.shreg & 1) {
-			hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
-			bc->modem.shreg = 0x10000;
-		}
+	if (bc->modem.ser12.last_rxbit != curs) {
+		bc->modem.ser12.last_rxbit = curs;
+		bc->modem.shreg |= 0x10000;
+		/* adjust the PLL */
+		if (timediff > 0)
+			bc->modem.ser12.pll_time += bdus8;
+		else
+			bc->modem.ser12.pll_time += 1000000 - bdus8;
+		/* update DCD */
+		if (abs(timediff) > bdus4)
+			bc->modem.ser12.dcd_sum0 += 4;
+		else
+			bc->modem.ser12.dcd_sum0--;
+#ifdef BAYCOM_DEBUG
+		bc->debug_vals.cur_pllcorr = timediff;
+#endif /* BAYCOM_DEBUG */
 	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static __inline__ void ser12_rx(struct device *dev, struct baycom_state *bc, unsigned char curs)
-{
-	unsigned long curjiff;
-	struct timeval tv;
-	unsigned int timediff;
-
-	/*
-	 * get current time
-	 */
-	curjiff = jiffies;
-	do_gettimeofday(&tv);
-	if ((signed)(curjiff - bc->modem.ser12.last_jiffies) >= HZ/4) {
-		/* long inactivity; clear HDLC and DCD */
-		bc->modem.ser12.dcd_sum1 = 0;
-		bc->modem.ser12.dcd_sum2 = 0;
-		bc->modem.ser12.dcd_sum0 = 2;
-		bc->modem.ser12.dcd_time = 120;
-		hdlcdrv_setdcd(&bc->hdrv, 0);
-		hdlcdrv_putbits(&bc->hdrv, 0xffff);
-		bc->modem.ser12.last_jiffies = curjiff;
-		bc->modem.ser12.pll_time = tv.tv_usec;
-	}
-	bc->modem.ser12.last_jiffies = curjiff;
-	timediff = tv.tv_usec + 1000000 - bc->modem.ser12.pll_time;
-	timediff %= 1000000;
-	timediff /= bc->baud_us8;
-	bc->modem.ser12.pll_time = (bc->modem.ser12.pll_time + timediff * (bc->baud_us8)) % 1000000;
-	for (; timediff > 1; timediff--)
-		ser12_rxsample(dev, bc, bc->modem.ser12.last_sample);
-	if (timediff >= 1)
-		ser12_rxsample(dev, bc, curs);
+	while (bc->modem.ser12.pll_time >= 1000000)
+		bc->modem.ser12.pll_time -= 1000000;
 }
 
 /* --------------------------------------------------------------------- */
@@ -336,25 +301,30 @@
 {
 	struct device *dev = (struct device *)dev_id;
 	struct baycom_state *bc = (struct baycom_state *)dev->priv;
-	unsigned char iir, msr = 0;
+	struct timeval tv;
+	unsigned char iir, msr;
 	unsigned int txcount = 0;
-	unsigned int rxcount = 0;
 
-	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+	if (!bc || bc->hdrv.magic != HDLCDRV_MAGIC)
 		return;
-	
-	for (;;) {
-		iir = inb(IIR(dev->base_addr));
-		if (iir & 1)
-			break;
+	/* fast way out for shared irq */
+	if ((iir = inb(IIR(dev->base_addr))) & 1) 	
+		return;
+	/* get current time */
+	do_gettimeofday(&tv);
+	msr = inb(MSR(dev->base_addr));
+	/* delta DCD */
+	if ((msr & 8) && !(bc->options & BAYCOM_OPTIONS_SOFTDCD)) 
+		hdlcdrv_setdcd(&bc->hdrv, !(msr & 0x80));
+	do {
 		switch (iir & 6) {
 		case 6:
 			inb(LSR(dev->base_addr));
-			continue;
+			break;
 			
 		case 4:
 			inb(RBR(dev->base_addr));
-			continue;
+			break;
 			
 		case 2:
 			/*
@@ -363,45 +333,53 @@
 			 * power from the TxD line
 			 */
 			outb(0x00, THR(dev->base_addr));
-			bc->modem.arb_divider--;
 			baycom_int_freq(bc);
-			if (hdlcdrv_ptt(&bc->hdrv)) {
-				/*
-				 * first output the last bit (!) then call HDLC transmitter,
-				 * since this may take quite long
-				 */
+			txcount++;
+			/*
+			 * first output the last bit (!) then call HDLC transmitter,
+			 * since this may take quite long
+			 */
+			if (bc->modem.ptt)
 				outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
-				txcount++;
-			} else
+			else
 				outb(0x0d, MCR(dev->base_addr));       /* transmitter off */
-			continue;
+			break;
 			
 		default:
 			msr = inb(MSR(dev->base_addr));
-			if (msr & 1)  /* delta CTS interrupt */
-				rxcount++;
-			continue;
+			/* delta DCD */
+			if ((msr & 8) && !(bc->options & BAYCOM_OPTIONS_SOFTDCD)) 
+				hdlcdrv_setdcd(&bc->hdrv, !(msr & 0x80));
+			break;
 		}
-	}
-	if (rxcount)
-		ser12_rx(dev, bc, msr & 0x10);
-	if (txcount) {
-#ifdef BAYCOM_DEBUG
-		if (bc->debug_vals.cur_pllcorr < txcount)
-			bc->debug_vals.cur_pllcorr = txcount;
-#endif /* BAYCOM_DEBUG */
-		if (bc->modem.ser12.txshreg <= 1)
+		iir = inb(IIR(dev->base_addr));
+	} while (!(iir & 1));
+	ser12_rx(dev, bc, &tv, msr & 0x10); /* CTS */
+	if (bc->modem.ptt && txcount) {
+		if (bc->modem.ser12.txshreg <= 1) {
 			bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
+			if (!hdlcdrv_ptt(&bc->hdrv)) {
+				ser12_set_divisor(dev, 115200/100/8);
+				bc->modem.ptt = 0;
+				goto end_transmit;
+			}
+		}
 		bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1));
 		bc->modem.ser12.txshreg >>= 1;
 	}
-	sti();
-	if (bc->modem.arb_divider <= 0) {
-		bc->modem.arb_divider = bc->baud_arbdiv;
+ end_transmit:
+	__sti();
+	if (!bc->modem.ptt && txcount) {
 		hdlcdrv_arbitrate(dev, &bc->hdrv);
+		if (hdlcdrv_ptt(&bc->hdrv)) {
+			ser12_set_divisor(dev, bc->baud_uartdiv);
+			bc->modem.ser12.txshreg = 1;
+			bc->modem.ptt = 1;
+		}
 	}
 	hdlcdrv_transmitter(dev, &bc->hdrv);
 	hdlcdrv_receiver(dev, &bc->hdrv);
+	__cli();
 }
 
 /* --------------------------------------------------------------------- */
@@ -461,26 +439,25 @@
 		return -EACCES;
 	memset(&bc->modem, 0, sizeof(bc->modem));
 	bc->hdrv.par.bitrate = bc->baud;
-	bc->baud_us8 = 125000/bc->baud;
-	bc->baud_arbdiv = bc->baud/100;
+	bc->baud_us = 1000000/bc->baud;
+	bc->baud_uartdiv = (115200/8)/bc->baud;
 	if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown)
 		return -EIO;
 	outb(0, FCR(dev->base_addr));  /* disable FIFOs */
 	outb(0x0d, MCR(dev->base_addr));
-	outb(0x0d, MCR(dev->base_addr));
 	outb(0, IER(dev->base_addr));
-	if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT,
+	if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT | SA_SHIRQ,
 			"baycom_ser_fdx", dev))
 		return -EBUSY;
 	request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx");
 	/*
-	 * set the SIO to 6 Bits/character and 19600 baud, so that
-	 * we get exactly (hopefully) one interrupt per radio symbol
+	 * set the SIO to 6 Bits/character; during receive,
+	 * the baud rate is set to produce 100 ints/sec
+	 * to feed the channel arbitration process,
+	 * during transmit to baud ints/sec to run
+	 * the transmitter
 	 */
-	outb(0x81, LCR(dev->base_addr));	/* DLAB = 1 */
-	outb(115200/8/bc->baud, DLL(dev->base_addr));
-	outb(0, DLM(dev->base_addr));
-	outb(0x01, LCR(dev->base_addr));	/* word length = 6 */
+	ser12_set_divisor(dev, 115200/100/8);
 	/*
 	 * enable transmitter empty interrupt and modem status interrupt
 	 */
@@ -491,6 +468,7 @@
 	 * power from the TxD line
 	 */
 	outb(0x00, THR(dev->base_addr));
+	hdlcdrv_setdcd(&bc->hdrv, 0);
 	printk(KERN_INFO "%s: ser_fdx at iobase 0x%lx irq %u options "
 	       "0x%x baud %u uart %s\n", bc_drvname, dev->base_addr, dev->irq,
 	       bc->options, bc->baud, uart_str[u]);
@@ -732,7 +710,7 @@
 #else /* MODULE */
 /* --------------------------------------------------------------------- */
 /*
- * format: baycom_ser_=io,irq,mode
+ * format: baycom_ser_fdx=io,irq,mode
  * mode: [*]
  * * indicates sofware DCD
  */
diff -u --recursive --new-file v2.1.104/linux/drivers/net/hamradio/baycom_ser_hdx.c linux/drivers/net/hamradio/baycom_ser_hdx.c
--- v2.1.104/linux/drivers/net/hamradio/baycom_ser_hdx.c	Sun Dec 21 17:41:24 1997
+++ linux/drivers/net/hamradio/baycom_ser_hdx.c	Sun Jun  7 11:13:45 1998
@@ -48,6 +48,7 @@
  *   0.3  26.04.97  init code/data tagged
  *   0.4  08.07.97  alternative ser12 decoding algorithm (uses delta CTS ints)
  *   0.5  11.11.97  ser12/par96 split into separate files
+ *   0.6  14.04.98  cleanups
  */
 
 /*****************************************************************************/
@@ -61,6 +62,8 @@
 #include <linux/ioport.h>
 #include <linux/in.h>
 #include <linux/string.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -72,56 +75,6 @@
 
 /* --------------------------------------------------------------------- */
 
-/*
- * currently this module is supposed to support both module styles, i.e.
- * the old one present up to about 2.1.9, and the new one functioning
- * starting with 2.1.21. The reason is I have a kit allowing to compile
- * this module also under 2.0.x which was requested by several people.
- * This will go in 2.2
- */
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE >= 0x20100
-#include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#include <linux/mm.h>
-
-#undef put_user
-#undef get_user
-
-#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
-#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
-
-extern inline int copy_from_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_READ, from, n);
-        if (i)
-                return i;
-        memcpy_fromfs(to, from, n);
-        return 0;
-}
-
-extern inline int copy_to_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_WRITE, to, n);
-        if (i)
-                return i;
-        memcpy_tofs(to, from, n);
-        return 0;
-}
-#endif
-
-#if LINUX_VERSION_CODE >= 0x20123
-#include <linux/init.h>
-#else
-#define __init
-#define __initdata
-#define __initfunc(x) x
-#endif
-
-/* --------------------------------------------------------------------- */
-
 #define BAYCOM_DEBUG
 
 /*
@@ -132,8 +85,8 @@
 /* --------------------------------------------------------------------- */
 
 static const char bc_drvname[] = "baycom_ser_hdx";
-static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "baycom_ser_hdx: version 0.5 compiled " __TIME__ " " __DATE__ "\n";
+static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1997-1998 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom_ser_hdx: version 0.6 compiled " __TIME__ " " __DATE__ "\n";
 
 /* --------------------------------------------------------------------- */
 
@@ -439,27 +392,52 @@
 {
 	struct device *dev = (struct device *)dev_id;
 	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+	unsigned char iir;
 
 	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
 		return;
-
+	/* fast way out */
+	if ((iir = inb(IIR(dev->base_addr))) & 1)
+		return;
 	baycom_int_freq(bc);
-	/*
-	 * check if transmitter active
-	 */
-	if (hdlcdrv_ptt(&bc->hdrv))
-		ser12_tx(dev, bc);
-	else {
-		ser12_rx(dev, bc);
-		if (--bc->modem.arb_divider <= 0) {
-			bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
-			sti();
-			hdlcdrv_arbitrate(dev, &bc->hdrv);
+	do {
+		switch (iir & 6) {
+		case 6:
+			inb(LSR(dev->base_addr));
+			break;
+			
+		case 4:
+			inb(RBR(dev->base_addr));
+			break;
+			
+		case 2:
+			/*
+			 * check if transmitter active
+			 */
+			if (hdlcdrv_ptt(&bc->hdrv))
+				ser12_tx(dev, bc);
+			else {
+				ser12_rx(dev, bc);
+				bc->modem.arb_divider--;
+			}
+			outb(0x00, THR(dev->base_addr));
+			break;
+			
+		default:
+			inb(MSR(dev->base_addr));
+			break;
 		}
+		iir = inb(IIR(dev->base_addr));
+	} while (!(iir & 1));
+	if (bc->modem.arb_divider <= 0) {
+		bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
+		__sti();
+		hdlcdrv_arbitrate(dev, &bc->hdrv);
 	}
-	sti();
+	__sti();
 	hdlcdrv_transmitter(dev, &bc->hdrv);
 	hdlcdrv_receiver(dev, &bc->hdrv);
+	__cli();
 }
 
 /* --------------------------------------------------------------------- */
@@ -521,9 +499,8 @@
 		return -EIO;
 	outb(0, FCR(dev->base_addr));  /* disable FIFOs */
 	outb(0x0d, MCR(dev->base_addr));
-	outb(0x0d, MCR(dev->base_addr));
 	outb(0, IER(dev->base_addr));
-	if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT,
+	if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT | SA_SHIRQ,
 			"baycom_ser12", dev))
 		return -EBUSY;
 	request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12");
diff -u --recursive --new-file v2.1.104/linux/drivers/net/hamradio/hdlcdrv.c linux/drivers/net/hamradio/hdlcdrv.c
--- v2.1.104/linux/drivers/net/hamradio/hdlcdrv.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/hamradio/hdlcdrv.c	Sun Jun  7 11:13:45 1998
@@ -3,7 +3,7 @@
 /*
  *	hdlcdrv.c  -- HDLC packet radio network driver.
  *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *	Copyright (C) 1996-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
@@ -35,6 +35,7 @@
  *   0.4  16.04.97  init code/data tagged
  *   0.5  30.07.97  made HDLC buffers bigger (solves a problem with the
  *                  soundmodem driver)
+ *   0.6  05.04.98  add spinlocks
  */
 
 /*****************************************************************************/
@@ -47,7 +48,9 @@
 #include <linux/if.h>
 #include <linux/malloc.h>
 #include <linux/errno.h>
+#include <linux/init.h>
 #include <asm/bitops.h>
+#include <asm/uaccess.h>
 
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
@@ -67,78 +70,6 @@
 /* --------------------------------------------------------------------- */
 
 /*
- * currently this module is supposed to support both module styles, i.e.
- * the old one present up to about 2.1.9, and the new one functioning
- * starting with 2.1.21. The reason is I have a kit allowing to compile
- * this module also under 2.0.x which was requested by several people.
- * This will go in 2.2
- */
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE >= 0x20100
-#include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#include <linux/mm.h>
-
-#undef put_user
-#undef get_user
-
-#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
-#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
-
-extern inline int copy_from_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_READ, from, n);
-        if (i)
-                return i;
-        memcpy_fromfs(to, from, n);
-        return 0;
-}
-
-extern inline int copy_to_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_WRITE, to, n);
-        if (i)
-                return i;
-        memcpy_tofs(to, from, n);
-        return 0;
-}
-#endif
-
-/* --------------------------------------------------------------------- */
-
-#if LINUX_VERSION_CODE < 0x20115
-extern __inline__ void dev_init_buffers(struct device *dev)
-{
-        int i;
-        for(i=0;i<DEV_NUMBUFFS;i++)
-        {
-                skb_queue_head_init(&dev->buffs[i]);
-        }
-}
-#endif
-
-/* --------------------------------------------------------------------- */
-
-#if LINUX_VERSION_CODE >= 0x20123
-#include <linux/init.h>
-#else
-#define __init
-#define __initdata
-#define __initfunc(x) x
-#endif
-
-/* --------------------------------------------------------------------- */
-
-#if LINUX_VERSION_CODE < 0x20125
-#define test_and_set_bit set_bit
-#define test_and_clear_bit clear_bit
-#endif
-
-/* --------------------------------------------------------------------- */
-
-/*
  * The name of the card. Is used for messages and in the requests for
  * io regions, irqs and dma channels
  */
@@ -844,10 +775,12 @@
 	s->ch_params = dflt_ch_params;
 	s->ptt_keyed = 0;
 
+	spin_lock_init(&s->hdlcrx.hbuf.lock);
 	s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
 	s->hdlcrx.in_hdlc_rx = 0;
 	s->hdlcrx.rx_state = 0;
 	
+	spin_lock_init(&s->hdlctx.hbuf.lock);
 	s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
 	s->hdlctx.in_hdlc_tx = 0;
 	s->hdlctx.tx_state = 1;
@@ -1006,7 +939,7 @@
 __initfunc(int init_module(void))
 {
 	printk(KERN_INFO "hdlcdrv: (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
-	printk(KERN_INFO "hdlcdrv: version 0.5 compiled " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "hdlcdrv: version 0.6 compiled " __TIME__ " " __DATE__ "\n");
 #if LINUX_VERSION_CODE < 0x20115
         register_symtab(&hdlcdrv_syms);
 #endif
diff -u --recursive --new-file v2.1.104/linux/drivers/net/hamradio/soundmodem/sm.c linux/drivers/net/hamradio/soundmodem/sm.c
--- v2.1.104/linux/drivers/net/hamradio/soundmodem/sm.c	Sun Dec 21 17:41:24 1997
+++ linux/drivers/net/hamradio/soundmodem/sm.c	Sun Jun  7 11:13:45 1998
@@ -3,7 +3,7 @@
 /*
  *	sm.c  -- soundcard radio modem driver.
  *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *	Copyright (C) 1996-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
@@ -40,6 +40,7 @@
  *   0.5  03.03.97  fixed LPT probing (check_lpt result was interpreted the wrong way round)
  *   0.6  16.04.97  init code/data tagged
  *   0.7  30.07.97  fixed halfduplex interrupt handlers/hotfix for CS423X
+ *   0.8  14.04.98  cleanups
  */
 
 /*****************************************************************************/
@@ -53,6 +54,8 @@
 #include <linux/net.h>
 #include <linux/in.h>
 #include <linux/string.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/bitops.h>
@@ -62,59 +65,9 @@
 
 /* --------------------------------------------------------------------- */
 
-/*
- * currently this module is supposed to support both module styles, i.e.
- * the old one present up to about 2.1.9, and the new one functioning
- * starting with 2.1.21. The reason is I have a kit allowing to compile
- * this module also under 2.0.x which was requested by several people.
- * This will go in 2.2
- */
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE >= 0x20100
-#include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#include <linux/mm.h>
-
-#undef put_user
-#undef get_user
-
-#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
-#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
-
-extern inline int copy_from_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_READ, from, n);
-        if (i)
-                return i;
-        memcpy_fromfs(to, from, n);
-        return 0;
-}
-
-extern inline int copy_to_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_WRITE, to, n);
-        if (i)
-                return i;
-        memcpy_tofs(to, from, n);
-        return 0;
-}
-#endif
-
-#if LINUX_VERSION_CODE >= 0x20123
-#include <linux/init.h>
-#else
-#define __init
-#define __initdata
-#define __initfunc(x) x
-#endif
-
-/* --------------------------------------------------------------------- */
-
 /*static*/ const char sm_drvname[] = "soundmodem";
-static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-1997 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "soundmodem: version 0.7 compiled " __TIME__ " " __DATE__ "\n";
+static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-1998 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "soundmodem: version 0.8 compiled " __TIME__ " " __DATE__ "\n";
 
 /* --------------------------------------------------------------------- */
 
@@ -685,51 +638,6 @@
 
 /* --------------------------------------------------------------------- */
 
-#ifdef __i386__
-
-int sm_x86_capability = 0;
-
-__initfunc(static void i386_capability(void))
-{
-	unsigned long flags;
-	unsigned long fl1;
-	union {
-		struct {
-			unsigned int ebx, edx, ecx;
-		} r;
-		unsigned char s[13];
-	} id;
-	unsigned int eax;
-
-	save_flags(flags);
-	flags |= 0x200000;
-	restore_flags(flags);
-	save_flags(flags);
-	fl1 = flags;
-	flags &= ~0x200000;
-	restore_flags(flags);
-	save_flags(flags);
-	if (!(fl1 & 0x200000) || (flags & 0x200000)) {
-		printk(KERN_WARNING "%s: cpu does not support CPUID\n", sm_drvname);
-		return;
-	}
-	__asm__ ("cpuid" : "=a" (eax), "=b" (id.r.ebx), "=c" (id.r.ecx), "=d" (id.r.edx) :
-		 "0" (0));
-	id.s[12] = 0;
-	if (eax < 1) {
-		printk(KERN_WARNING "%s: cpu (vendor string %s) does not support capability "
-		       "list\n", sm_drvname, id.s);
-		return;
-	}
-	printk(KERN_INFO "%s: cpu: vendor string %s ", sm_drvname, id.s);
-	__asm__ ("cpuid" : "=a" (eax), "=d" (sm_x86_capability) : "0" (1) : "ebx", "ecx");
-	printk("fam %d mdl %d step %d cap 0x%x\n", (eax >> 8) & 15, (eax >> 4) & 15,
-	       eax & 15, sm_x86_capability);
-}	
-#endif /* __i386__ */	
-
-/* --------------------------------------------------------------------- */
-
 #ifdef MODULE
 __initfunc(static int sm_init(void))
 #else /* MODULE */
@@ -742,9 +650,6 @@
 	char ifname[HDLCDRV_IFNAMELEN];
 
 	printk(sm_drvinfo);
-#ifdef __i386__
-	i386_capability();
-#endif /* __i386__ */	
 	/*
 	 * register net devices
 	 */
diff -u --recursive --new-file v2.1.104/linux/drivers/net/hamradio/soundmodem/sm.h linux/drivers/net/hamradio/soundmodem/sm.h
--- v2.1.104/linux/drivers/net/hamradio/soundmodem/sm.h	Mon Jun 16 16:35:56 1997
+++ linux/drivers/net/hamradio/soundmodem/sm.h	Sun Jun  7 11:13:45 1998
@@ -3,7 +3,7 @@
 /*
  *	sm.h  --  soundcard radio modem driver internal header.
  *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *	Copyright (C) 1996-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
@@ -32,6 +32,7 @@
 
 #include <linux/hdlcdrv.h>
 #include <linux/soundmodem.h>
+#include <linux/bitops.h>
 
 #define SM_DEBUG
 
@@ -71,7 +72,7 @@
 	 * state of the modem code
 	 */
 	union {
-		long m[32/sizeof(long)];
+		long m[48/sizeof(long)];
 	} m;
 	union {
 		long d[256/sizeof(long)];
@@ -228,6 +229,7 @@
  * ===================== utility functions ===============================
  */
 
+#if 0
 extern inline unsigned int hweight32(unsigned int w)
 	__attribute__ ((unused));
 extern inline unsigned int hweight16(unsigned short w)
@@ -259,6 +261,8 @@
         return (res & 0x0F) + ((res >> 4) & 0x0F);
 }
 
+#endif
+
 extern inline unsigned int gcd(unsigned int x, unsigned int y)
 	__attribute__ ((unused));
 extern inline unsigned int lcm(unsigned int x, unsigned int y)
@@ -291,13 +295,13 @@
 
 #ifdef __i386__
 
-extern int sm_x86_capability;
+#include <asm/processor.h>
 
-#define HAS_RDTSC (sm_x86_capability & 0x10)
+#define HAS_RDTSC (current_cpu_data.x86_capability & 0x10)
 
 /*
- * only do 32bit cycle counter arithmetic; we hope we won't overflow :-)
- * in fact, overflowing modems would require over 2THz clock speeds :-)
+ * only do 32bit cycle counter arithmetic; we hope we won't overflow.
+ * in fact, overflowing modems would require over 2THz CPU clock speeds :-)
  */
 
 #define time_exec(var,cmd)                                              \
diff -u --recursive --new-file v2.1.104/linux/drivers/net/hamradio/soundmodem/sm_afsk2666.c linux/drivers/net/hamradio/soundmodem/sm_afsk2666.c
--- v2.1.104/linux/drivers/net/hamradio/soundmodem/sm_afsk2666.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/sm_afsk2666.c	Sun Jun  7 11:13:45 1998
@@ -0,0 +1,356 @@
+/*****************************************************************************/
+
+/*
+ *	sm_afsk2666.c  -- soundcard radio modem driver, 2666 baud AFSK modem
+ *
+ *	Copyright (C) 1997  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.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include "sm.h"
+#include "sm_tbl_afsk2666.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_afsk26 {
+	unsigned int shreg;
+	unsigned long descram;
+	int dem_sum[8];
+	int dem_sum_mean;
+	int dem_cnt;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+};
+
+struct mod_state_afsk26 {
+	unsigned int shreg;
+	unsigned long scram;
+	unsigned int bit_pll;
+	unsigned int phinc;
+	unsigned int tx_seq;
+};
+
+/* --------------------------------------------------------------------- */
+
+#define DESCRAM_TAP1 0x20000
+#define DESCRAM_TAP2 0x01000
+#define DESCRAM_TAP3 0x00001
+
+#define DESCRAM_TAPSH1 17
+#define DESCRAM_TAPSH2 12
+#define DESCRAM_TAPSH3 0
+
+#define SCRAM_TAP1 0x20000 /* X^17 */
+#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_2666_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_afsk26 *st = (struct mod_state_afsk26 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->scram = ((st->scram << 1) | (st->scram & 1));
+			st->scram ^= (!(st->shreg & 1));
+			st->shreg >>= 1;
+			if (st->scram & (SCRAM_TAP1 << 1))
+				st->scram ^= SCRAM_TAPN << 1;
+			st->phinc = afsk26_carfreq[!(st->scram & (SCRAM_TAP1 << 2))];
+		}
+		if (st->tx_seq >= 6)
+			st->tx_seq = 0;
+		*buf = OFFSCOS(st->bit_pll);
+		st->bit_pll += st->phinc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_2666_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_afsk26 *st = (struct mod_state_afsk26 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->scram = ((st->scram << 1) | (st->scram & 1));
+			st->scram ^= (!(st->shreg & 1));
+			st->shreg >>= 1;
+			if (st->scram & (SCRAM_TAP1 << 1))
+				st->scram ^= SCRAM_TAPN << 1;
+			st->phinc = afsk26_carfreq[!(st->scram & (SCRAM_TAP1 << 2))];
+		}
+		if (st->tx_seq >= 6)
+			st->tx_seq = 0;
+		*buf = COS(st->bit_pll);
+		st->bit_pll += st->phinc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int convolution12_u8(const unsigned char *st, const int *coeff, int csum)
+{
+	int sum = -0x80 * csum;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+	sum += (st[-8] * coeff[8]);
+	sum += (st[-9] * coeff[9]);
+	sum += (st[-10] * coeff[10]);
+	sum += (st[-11] * coeff[11]);
+
+	return sum;
+}
+
+extern __inline__ int convolution12_s16(const short *st, const int *coeff, int csum)
+{
+	int sum = 0;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+	sum += (st[-8] * coeff[8]);
+	sum += (st[-9] * coeff[9]);
+	sum += (st[-10] * coeff[10]);
+	sum += (st[-11] * coeff[11]);
+
+	sum >>= 8;
+	return sum;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#if 0
+static int binexp(unsigned int i)
+{
+	int ret = 31;
+	
+	if (!i)
+		return 0;
+	if (i < 0x10000LU) {
+		i <<= 16;
+		ret -= 16;
+	}
+	if (i < 0x1000000LU) {
+		i <<= 8;
+		ret -= 8;
+	}
+	if (i < 0x10000000LU) {
+		i <<= 4;
+		ret -= 4;
+	}
+	if (i < 0x40000000LU) {
+		i <<= 2;
+		ret -= 2;
+	}
+	if (i < 0x80000000LU)
+		ret -= 1;
+	return ret;
+}
+
+static const sqrt_tab[16] = {
+	00000, 16384, 23170, 28378, 32768, 36636, 40132, 43348,
+	46341, 49152, 51811, 54340, 56756, 59073, 61303, 63455
+};
+
+
+static unsigned int int_sqrt_approx(unsigned int i)
+{
+	unsigned int j;
+
+	if (i < 16)
+		return sqrt_tab[i] >> 14;
+	j = binexp(i) >> 1;
+	i >>= (j * 2 - 2);
+      	return (sqrt_tab[i & 0xf] << j) >> 15;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+extern unsigned int est_pwr(int i, int q)
+{
+	unsigned int ui = abs(i);
+	unsigned int uq = abs(q);
+
+	if (uq > ui) {
+		unsigned int tmp;
+		tmp = ui;
+		ui = uq;
+		uq = tmp;
+	}
+	if (uq > (ui >> 1))
+		return 7*(ui>>3) + 9*(uq>>4);
+	else
+		return ui + (uq>>2);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_one_sample(struct sm_state *sm, struct demod_state_afsk26 *st, int curval,
+			     int loi, int loq, int hii, int hiq)
+{
+	static const int pll_corr[2] = { -0xa00, 0xa00 };
+	unsigned char curbit;
+	unsigned int descx;
+	int val;
+
+	/* 
+	 * estimate power
+	 */
+	val = est_pwr(hii, hiq) - est_pwr(loi, loq);
+	/*
+	 * estimate center value
+	 */
+	st->dem_sum[0] += val >> 8;
+	if ((++st->dem_cnt) >= 256) {
+		st->dem_cnt = 0;
+		st->dem_sum_mean = (st->dem_sum[0]+st->dem_sum[1]+
+				    st->dem_sum[2]+st->dem_sum[3]+
+				    st->dem_sum[4]+st->dem_sum[5]+
+				    st->dem_sum[6]+st->dem_sum[7]) >> 3;
+		memmove(st->dem_sum+1, st->dem_sum, 
+			sizeof(st->dem_sum)-sizeof(st->dem_sum[0]));
+		st->dem_sum[0] = 0;
+	}
+	/*
+	 * decision and bit clock regen
+	 */
+	val -= st->dem_sum_mean;
+	diag_add(sm, curval, val);
+	
+	st->dcd_shreg <<= 1;
+	st->bit_pll += 0x1555;
+	curbit = (val > 0);
+	if (st->last_sample ^ curbit) {
+		st->dcd_shreg |= 1;
+		st->bit_pll += pll_corr[st->bit_pll < (0x8000+0x1555)];
+		st->dcd_sum0 += 4*hweight8(st->dcd_shreg & 0x1e) -
+			hweight16(st->dcd_shreg & 0xfe00);
+	}
+	st->last_sample = curbit;
+	hdlcdrv_channelbit(&sm->hdrv, curbit);
+	if ((--st->dcd_time) <= 0) {
+		hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + st->dcd_sum1 + 
+					   st->dcd_sum2) < 0);
+		st->dcd_sum2 = st->dcd_sum1;
+		st->dcd_sum1 = st->dcd_sum0;
+		st->dcd_sum0 = 2; /* slight bias */
+		st->dcd_time = 400;
+	}
+	if (st->bit_pll >= 0x10000) {
+		st->bit_pll &= 0xffffu;
+		st->descram = (st->descram << 1) | curbit;
+		descx = st->descram ^ (st->descram >> 1);
+		descx ^= ((descx >> DESCRAM_TAPSH1) ^
+			  (descx >> DESCRAM_TAPSH2));
+		st->shreg >>= 1;
+		st->shreg |= (!(descx & 1)) << 16;
+		if (st->shreg & 1) {
+			hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+			st->shreg = 0x10000;
+		}
+		diag_trigger(sm);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2666_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d);
+
+	for (; buflen > 0; buflen--, buf++) {
+		demod_one_sample(sm, st, (*buf-0x80)<<8,
+				 convolution12_u8(buf, afsk26_dem_tables[0][0].i, AFSK26_DEM_SUM_I_0_0),
+				 convolution12_u8(buf, afsk26_dem_tables[0][0].q, AFSK26_DEM_SUM_Q_0_0),
+				 convolution12_u8(buf, afsk26_dem_tables[0][1].i, AFSK26_DEM_SUM_I_0_1),
+				 convolution12_u8(buf, afsk26_dem_tables[0][1].q, AFSK26_DEM_SUM_Q_0_1));
+		demod_one_sample(sm, st,  (*buf-0x80)<<8,
+				 convolution12_u8(buf, afsk26_dem_tables[1][0].i, AFSK26_DEM_SUM_I_1_0),
+				 convolution12_u8(buf, afsk26_dem_tables[1][0].q, AFSK26_DEM_SUM_Q_1_0),
+				 convolution12_u8(buf, afsk26_dem_tables[1][1].i, AFSK26_DEM_SUM_I_1_1),
+				 convolution12_u8(buf, afsk26_dem_tables[1][1].q, AFSK26_DEM_SUM_Q_1_1));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2666_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d);
+
+	for (; buflen > 0; buflen--, buf++) {
+		demod_one_sample(sm, st, *buf,
+				 convolution12_s16(buf, afsk26_dem_tables[0][0].i, AFSK26_DEM_SUM_I_0_0),
+				 convolution12_s16(buf, afsk26_dem_tables[0][0].q, AFSK26_DEM_SUM_Q_0_0),
+				 convolution12_s16(buf, afsk26_dem_tables[0][1].i, AFSK26_DEM_SUM_I_0_1),
+				 convolution12_s16(buf, afsk26_dem_tables[0][1].q, AFSK26_DEM_SUM_Q_0_1));
+		demod_one_sample(sm, st, *buf, 
+				 convolution12_s16(buf, afsk26_dem_tables[1][0].i, AFSK26_DEM_SUM_I_1_0),
+				 convolution12_s16(buf, afsk26_dem_tables[1][0].q, AFSK26_DEM_SUM_Q_1_0),
+				 convolution12_s16(buf, afsk26_dem_tables[1][1].i, AFSK26_DEM_SUM_I_1_1),
+				 convolution12_s16(buf, afsk26_dem_tables[1][1].q, AFSK26_DEM_SUM_Q_1_1));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_2666(struct sm_state *sm)
+{
+	struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d);
+
+	st->dcd_time = 400;
+	st->dcd_sum0 = 2;	
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_afsk2666_tx = {
+	"afsk2666", sizeof(struct mod_state_afsk26), AFSK26_SAMPLERATE, 2666, 
+	modulator_2666_u8, modulator_2666_s16, NULL
+};
+
+const struct modem_rx_info sm_afsk2666_rx = {
+	"afsk2666", sizeof(struct demod_state_afsk26), AFSK26_SAMPLERATE, 2666, 12, 6, 
+	demodulator_2666_u8, demodulator_2666_s16, demod_init_2666
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.104/linux/drivers/net/hamradio/soundmodem/sm_psk4800.c linux/drivers/net/hamradio/soundmodem/sm_psk4800.c
--- v2.1.104/linux/drivers/net/hamradio/soundmodem/sm_psk4800.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/sm_psk4800.c	Sun Jun  7 11:13:45 1998
@@ -0,0 +1,418 @@
+/*****************************************************************************/
+
+/*
+ *	sm_psk4800.c  -- soundcard radio modem driver, 4800 baud 8PSK modem
+ *
+ *	Copyright (C) 1997  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.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include "sm.h"
+#include "sm_tbl_psk4800.h"
+
+/* --------------------------------------------------------------------- */
+
+#define DESCRAM_TAP1 0x20000
+#define DESCRAM_TAP2 0x01000
+#define DESCRAM_TAP3 0x00001
+
+#define DESCRAM_TAPSH1 17
+#define DESCRAM_TAPSH2 12
+#define DESCRAM_TAPSH3 0
+
+#define SCRAM_TAP1 0x20000 /* X^17 */
+#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+#define SCRAM_SHIFT 17
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_psk48 {
+	/*
+	 * input mixer and lowpass
+	 */
+	short infi[PSK48_RXF_LEN/2], infq[PSK48_RXF_LEN/2];
+	unsigned int downmixer;
+	int ovrphase;
+	short magi, magq;
+	/*
+	 * sampling instant recovery
+	 */
+	int pwrhist[5];
+	unsigned int s_phase;
+	int cur_sync;
+	/*
+	 * phase recovery
+	 */
+	short cur_phase_dev;
+	short last_ph_err;
+	unsigned short pskph;
+	unsigned int phase;
+	unsigned short last_pskph;
+	unsigned char cur_raw, last_raw, rawbits;
+	/*
+	 * decoding
+	 */
+	unsigned int shreg;
+	unsigned long descram;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+};
+
+struct mod_state_psk48 {
+	unsigned char txbits[PSK48_TXF_NUMSAMPLES];
+	unsigned short txphase;
+	unsigned int shreg;
+	unsigned long scram;
+	const short *tbl;
+	unsigned int txseq;
+};
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_4800_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m);
+	int i, j;
+	int si, sq;
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->txseq++) {
+			memmove(st->txbits+1, st->txbits, 
+				sizeof(st->txbits)-sizeof(st->txbits[0]));
+			for (i = 0; i < 3; i++) {
+				if (st->shreg <= 1)
+					st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+				st->scram = (st->scram << 1) |
+					(st->shreg & 1);
+				st->shreg >>= 1;
+				if (st->scram & SCRAM_TAP1)
+					st->scram ^= SCRAM_TAPN;
+			}
+			j = (st->scram >> (SCRAM_SHIFT+3)) & 7;
+			st->txbits[0] -= (j ^ (j >> 1));
+			st->txbits[0] &= 7;
+			st->tbl = psk48_tx_table;
+		}
+		if (st->txseq >= PSK48_TXF_OVERSAMPLING)
+			st->txseq = 0;
+		for (j = si = sq = 0; j < PSK48_TXF_NUMSAMPLES; j++, st->tbl += 16) {
+			si += st->tbl[st->txbits[j]];
+			sq += st->tbl[st->txbits[j]+8];
+		}
+		*buf = ((si*COS(st->txphase)+ sq*SIN(st->txphase)) >> 23) + 0x80;
+		st->txphase = (st->txphase + PSK48_PHASEINC) & 0xffffu;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_4800_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m);
+	int i, j;
+	int si, sq;
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->txseq++) {
+			memmove(st->txbits+1, st->txbits, 
+				sizeof(st->txbits)-sizeof(st->txbits[0]));
+			for (i = 0; i < 3; i++) {
+				if (st->shreg <= 1)
+					st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+				st->scram = (st->scram << 1) |
+					(st->shreg & 1);
+				st->shreg >>= 1;
+				if (st->scram & SCRAM_TAP1)
+					st->scram ^= SCRAM_TAPN;
+			}
+			j = (st->scram >> (SCRAM_SHIFT+3)) & 7;
+			st->txbits[0] -= (j ^ (j >> 1));
+			st->txbits[0] &= 7;
+			st->tbl = psk48_tx_table;
+		}
+		if (st->txseq >= PSK48_TXF_OVERSAMPLING)
+			st->txseq = 0;
+		for (j = si = sq = 0; j < PSK48_TXF_NUMSAMPLES; j++, st->tbl += 16) {
+			si += st->tbl[st->txbits[j]];
+			sq += st->tbl[st->txbits[j]+8];
+		}
+		*buf = (si*COS(st->txphase)+ sq*SIN(st->txphase)) >> 15;
+		st->txphase = (st->txphase + PSK48_PHASEINC) & 0xffffu;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ unsigned short tbl_atan(short q, short i)
+{
+	short tmp;
+	unsigned short argoffs = 0;
+
+	if (i == 0 && q == 0)
+		return 0;
+	switch (((q < 0) << 1) | (i < 0)) {
+	case 0:
+		break;
+	case 1:
+		tmp = q;
+		q = -i;
+		i = tmp;
+		argoffs = 0x4000;
+		break;
+	case 3:
+		q = -q;
+		i = -i;
+		argoffs = 0x8000;
+		break;
+	case 2:
+		tmp = -q;
+		q = i;
+		i = tmp;
+		argoffs = 0xc000;
+		break;
+	}
+	if (q > i) {
+		tmp = i / q * ATAN_TABLEN;
+		return (argoffs+0x4000-atan_tab[((i<<15)/q*ATAN_TABLEN>>15)])
+			&0xffffu;
+	}
+	return (argoffs+atan_tab[((q<<15)/i*ATAN_TABLEN)>>15])&0xffffu;
+}
+
+#define ATAN(q,i) tbl_atan(q, i)
+
+/* --------------------------------------------------------------------- */
+
+static void demod_psk48_baseband(struct sm_state *sm, struct demod_state_psk48 *st,
+				 short vali, short valq)
+{
+	int i, j;
+
+	st->magi = vali;
+	st->magq = valq;
+	memmove(st->pwrhist+1, st->pwrhist, 
+		sizeof(st->pwrhist)-sizeof(st->pwrhist[0]));
+	st->pwrhist[0] = st->magi * st->magi +
+		st->magq * st->magq;
+	st->cur_sync = ((st->pwrhist[4] >> 2) > st->pwrhist[2] && 
+			(st->pwrhist[0] >> 2) > st->pwrhist[2] &&
+			st-> pwrhist[3] > st->pwrhist[2] && 
+			st->pwrhist[1] > st->pwrhist[2]);
+	st->s_phase &= 0xffff;
+	st->s_phase += PSK48_SPHASEINC;
+	st->dcd_shreg <<= 1;
+	if (st->cur_sync) {
+		if (st->s_phase >= (0x8000 + 5*PSK48_SPHASEINC/2))
+			st->s_phase -= PSK48_SPHASEINC/6;
+		else
+			st->s_phase += PSK48_SPHASEINC/6;
+		st->dcd_sum0 = 4*hweight8(st->dcd_shreg & 0xf8)-
+			hweight16(st->dcd_shreg & 0x1f00);
+	}
+	if ((--st->dcd_time) <= 0) {
+		hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + st->dcd_sum1 + 
+					   st->dcd_sum2) < 0);
+		st->dcd_sum2 = st->dcd_sum1;
+		st->dcd_sum1 = st->dcd_sum0;
+		st->dcd_sum0 = 2; /* slight bias */
+		st->dcd_time = 240;
+	}
+	if (st->s_phase < 0x10000)
+		return;
+	/*
+	 * sample one constellation
+	 */
+	st->last_pskph = st->pskph;
+	st->pskph = (ATAN(st->magq, st->magi)-
+			       st->phase) & 0xffffu;
+	st->last_ph_err = (st->pskph & 0x1fffu) - 0x1000;
+	st->phase += st->last_ph_err/16;
+	st->last_raw = st->cur_raw;
+	st->cur_raw = ((st->pskph >> 13) & 7);
+	i = (st->cur_raw - st->last_raw) & 7;
+	st->rawbits = i ^ (i >> 1) ^ (i >> 2);
+	st->descram = (st->descram << 3) | (st->rawbits);
+	hdlcdrv_channelbit(&sm->hdrv, st->descram & 4);
+	hdlcdrv_channelbit(&sm->hdrv, st->descram & 2);
+	hdlcdrv_channelbit(&sm->hdrv, st->descram & 1);
+	i = (((st->descram >> DESCRAM_TAPSH1) & 7) ^
+	     ((st->descram >> DESCRAM_TAPSH2) & 7) ^
+	     ((st->descram >> DESCRAM_TAPSH3) & 7));
+ 	for (j = 4; j; j >>= 1) {
+		st->shreg >>= 1;
+		st->shreg |= (!!(i & j)) << 16;
+		if (st->shreg & 1) {
+			hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+			st->shreg = 0x10000;
+		}
+	}
+
+#if 0
+	st->dcd_shreg <<= 1;
+	st->bit_pll += 0x4000;
+	curbit = (*buf >= 0x80);
+	if (st->last_sample ^ curbit) {
+		st->dcd_shreg |= 1;
+		st->bit_pll += pll_corr
+			[st->bit_pll < 0xa000];
+		st->dcd_sum0 += 8 * 
+			hweight8(st->dcd_shreg & 0x0c) - 
+				!!(st->dcd_shreg & 0x10);
+	}
+	st->last_sample = curbit;
+	hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+	if ((--st->dcd_time) <= 0) {
+		hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+					   st->dcd_sum1 + 
+					   st->dcd_sum2) < 0);
+		st->dcd_sum2 = st->dcd_sum1;
+		st->dcd_sum1 = st->dcd_sum0;
+		st->dcd_sum0 = 2; /* slight bias */
+		st->dcd_time = 240;
+	}
+	if (st->bit_pll >= 0x10000) {
+		st->bit_pll &= 0xffffu;
+		st->descram = (st->descram << 1) | curbit;
+		descx = st->descram ^ (st->descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			st->shreg >>= 1;
+			st->shreg |= (!(descx & 1)) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+	}
+	diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+#endif
+	
+	diag_trigger(sm);
+	diag_add_constellation(sm, (vali*COS(st->phase)+ valq*SIN(st->phase)) >> 13, 
+			       (valq*COS(st->phase) - vali*SIN(st->phase)) >> 13);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_4800_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d);
+	int i, si, sq;
+	const short *coeff;
+
+	for (; buflen > 0; buflen--, buf++) {
+		memmove(st->infi+1, st->infi, 
+			sizeof(st->infi)-sizeof(st->infi[0]));
+		memmove(st->infq+1, st->infq, 
+			sizeof(st->infq)-sizeof(st->infq[0]));
+		si = *buf;
+		si &= 0xff;
+		si -= 128;
+		diag_add_one(sm, si << 8);
+		st->infi[0] = (si * COS(st->downmixer))>>7;
+		st->infq[0] = (si * SIN(st->downmixer))>>7;
+		st->downmixer = (st->downmixer-PSK48_PHASEINC)&0xffffu;
+		for (i = si = sq = 0, coeff = psk48_rx_coeff; i < (PSK48_RXF_LEN/2); 
+		     i++, coeff += 2) {
+			si += st->infi[i] * (*coeff);
+			sq += st->infq[i] * (*coeff);
+		}
+		demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
+		for (i = si = sq = 0, coeff = psk48_rx_coeff + 1; i < (PSK48_RXF_LEN/2); 
+		     i++, coeff += 2) {
+			si += st->infi[i] * (*coeff);
+			sq += st->infq[i] * (*coeff);
+		}
+		demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_4800_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d);
+	int i, si, sq;
+	const short *coeff;
+
+	for (; buflen > 0; buflen--, buf++) {
+		memmove(st->infi+1, st->infi, 
+			sizeof(st->infi)-sizeof(st->infi[0]));
+		memmove(st->infq+1, st->infq, 
+			sizeof(st->infq)-sizeof(st->infq[0]));
+		si = *buf;
+		diag_add_one(sm, si);
+		st->infi[0] = (si * COS(st->downmixer))>>15;
+		st->infq[0] = (si * SIN(st->downmixer))>>15;
+		st->downmixer = (st->downmixer-PSK48_PHASEINC)&0xffffu;
+		for (i = si = sq = 0, coeff = psk48_rx_coeff; i < (PSK48_RXF_LEN/2); 
+		     i++, coeff += 2) {
+			si += st->infi[i] * (*coeff);
+			sq += st->infq[i] * (*coeff);
+		}
+		demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
+		for (i = si = sq = 0, coeff = psk48_rx_coeff + 1; i < (PSK48_RXF_LEN/2); 
+		     i++, coeff += 2) {
+			si += st->infi[i] * (*coeff);
+			sq += st->infq[i] * (*coeff);
+		}
+		demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void mod_init_4800(struct sm_state *sm)
+{
+	struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m);
+
+	st->scram = 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_4800(struct sm_state *sm)
+{
+	struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d);
+
+	st->dcd_time = 120;
+	st->dcd_sum0 = 2;	
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_psk4800_tx = {
+	"psk4800", sizeof(struct mod_state_psk48), 
+	PSK48_SAMPLERATE, 4800,
+	modulator_4800_u8, modulator_4800_s16, mod_init_4800
+};
+
+const struct modem_rx_info sm_psk4800_rx = {
+	"psk4800", sizeof(struct demod_state_psk48), 
+	PSK48_SAMPLERATE, 4800, 1, PSK48_TXF_OVERSAMPLING, 
+	demodulator_4800_u8, demodulator_4800_s16, demod_init_4800
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.104/linux/drivers/net/hp-plus.c linux/drivers/net/hp-plus.c
--- v2.1.104/linux/drivers/net/hp-plus.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/hp-plus.c	Sun Jun  7 10:37:41 1998
@@ -159,6 +159,9 @@
 		|| (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300)
 		return ENODEV;
 
+	if (load_8390_module("hp-plus.c"))
+		return -ENOSYS;
+
 	/* We should have a "dev" from Space.c or the static module table. */
 	if (dev == NULL) {
 		printk("hp-plus.c: Passed a NULL device.\n");
@@ -260,7 +263,7 @@
 	int ioaddr = dev->base_addr - NIC_OFFSET;
 	int option_reg;
 
-	if (request_irq(dev->irq, &ei_interrupt, 0, "hp-plus", dev)) {
+	if (request_irq(dev->irq, ei_interrupt, 0, "hp-plus", dev)) {
 	    return -EAGAIN;
 	}
 
@@ -447,12 +450,15 @@
 		}
 		if (register_netdev(dev) != 0) {
 			printk(KERN_WARNING "hp-plus.c: No HP-Plus card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0) return 0;	/* Got at least one. */
+			if (found != 0) {	/* Got at least one. */
+				lock_8390_module();
+				return 0;
+			}
 			return -ENXIO;
 		}
 		found++;
 	}
-
+	lock_8390_module();
 	return 0;
 }
 
@@ -464,14 +470,16 @@
 	for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
 		struct device *dev = &dev_hpp[this_dev];
 		if (dev->priv != NULL) {
-			/* NB: hpp_close() handles free_irq */
 			int ioaddr = dev->base_addr - NIC_OFFSET;
-			unregister_netdev(dev);
-			kfree(dev->priv);
-			dev->priv = NULL;
+			void *priv = dev->priv;
+			/* NB: hpp_close() handles free_irq */
 			release_region(ioaddr, HP_IO_EXTENT);
+			dev->priv = NULL;
+			unregister_netdev(dev);
+			kfree(priv);
 		}
 	}
+	unlock_8390_module();
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v2.1.104/linux/drivers/net/hp.c linux/drivers/net/hp.c
--- v2.1.104/linux/drivers/net/hp.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/hp.c	Sun Jun  7 10:37:41 1998
@@ -131,6 +131,9 @@
 		wordmode = 0;
 	}
 
+	if (load_8390_module("hp.c"))
+		return -ENOSYS;
+
 	/* We should have a "dev" from Space.c or the static module table. */
 	if (dev == NULL) {
 		printk("hp.c: Passed a NULL device.\n");
@@ -140,6 +143,12 @@
 	if (ei_debug  &&  version_printed++ == 0)
 		printk(version);
 
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk (" unable to get memory for dev->priv.\n");
+		return -ENOMEM;
+	}
+
 	printk("%s: %s (ID %02x) at %#3x,", dev->name, name, board_id, ioaddr);
 
 	for(i = 0; i < ETHER_ADDR_LEN; i++)
@@ -158,7 +167,7 @@
 				outb_p(irqmap[irq] | HP_RUN, ioaddr + HP_CONFIGURE);
 				outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE);
 				if (irq == autoirq_report(0)		 /* It's a good IRQ line! */
-					&& request_irq (irq, &ei_interrupt, 0, "hp", dev) == 0) {
+					&& request_irq (irq, ei_interrupt, 0, "hp", dev) == 0) {
 					printk(" selecting IRQ %d.\n", irq);
 					dev->irq = *irqp;
 					break;
@@ -167,6 +176,8 @@
 		} while (*++irqp);
 		if (*irqp == 0) {
 			printk(" no free IRQ lines.\n");
+			kfree(dev->priv);
+			dev->priv = NULL;
 			return EBUSY;
 		}
 	} else {
@@ -174,17 +185,12 @@
 			dev->irq = 9;
 		if (request_irq(dev->irq, ei_interrupt, 0, "hp", dev)) {
 			printk (" unable to get IRQ %d.\n", dev->irq);
+			kfree(dev->priv);
+			dev->priv = NULL;
 			return EBUSY;
 		}
 	}
 
-	/* Allocate dev->priv and fill in 8390 specific dev fields. */
-	if (ethdev_init(dev)) {
-		printk (" unable to get memory for dev->priv.\n");
-		free_irq(dev->irq, dev);
-		return -ENOMEM;
-	}
-
 	/* Grab the region so we can find another board if something fails. */
 	request_region(ioaddr, HP_IO_EXTENT,"hp");
 
@@ -415,12 +421,15 @@
 		}
 		if (register_netdev(dev) != 0) {
 			printk(KERN_WARNING "hp.c: No HP card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0) return 0;	/* Got at least one. */
+			if (found != 0) {	/* Got at least one. */
+				lock_8390_module();
+				return 0;
+			}
 			return -ENXIO;
 		}
 		found++;
 	}
-
+	lock_8390_module();
 	return 0;
 }
 
@@ -433,13 +442,15 @@
 		struct device *dev = &dev_hp[this_dev];
 		if (dev->priv != NULL) {
 			int ioaddr = dev->base_addr - NIC_OFFSET;
-			unregister_netdev(dev);
-			kfree(dev->priv);
-			dev->priv = NULL;
+			void *priv = dev->priv;
 			free_irq(dev->irq, dev);
 			release_region(ioaddr, HP_IO_EXTENT);
+			dev->priv = NULL;
+			unregister_netdev(dev);
+			kfree(priv);
 		}
 	}
+	unlock_8390_module();
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v2.1.104/linux/drivers/net/lance.c linux/drivers/net/lance.c
--- v2.1.104/linux/drivers/net/lance.c	Mon Apr  6 17:41:00 1998
+++ linux/drivers/net/lance.c	Sun Jun  7 10:16:57 1998
@@ -276,6 +276,12 @@
         {0x2621, "PCnet/PCI-II 79C970A",        /* 79C970A PCInetPCI II. */
                 LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
                         LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
+        {0x2623, "PCnet/FAST 79C971",        /* 79C971 PCInetFAST. */
+                LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+                        LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
+        {0x2624, "PCnet/FAST+ 79C972",       /* 79C972 PCInetFAST+. */
+                LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+                        LANCE_HAS_MISSED_FRAME + PCNET32_POSSIBLE},
 	{0x0, 	 "PCnet (unknown)",
 		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
 			LANCE_HAS_MISSED_FRAME},
@@ -315,7 +321,7 @@
 	if (virt_to_bus(high_memory) <= 16*1024*1024)
 		lance_need_isa_bounce_buffers = 0;
 
-#if defined(CONFIG_PCI) && !defined(CONFIG_PCNET32)
+#if defined(CONFIG_PCI) && !(defined(CONFIG_PCNET32) || defined(CONFIG_PCNET32_MODULE))
     if (pci_present()) {
 	    struct pci_dev *pdev = NULL;
 		if (lance_debug > 1)
@@ -421,7 +427,7 @@
 		}
 	}
     
-#ifdef CONFIG_PCNET32
+#if defined(CONFIG_PCNET32) || defined (CONFIG_PCNET32_MODULE)
         /*
 	 * if pcnet32 is configured and the chip is capable of 32bit mode
 	 * leave the card alone
diff -u --recursive --new-file v2.1.104/linux/drivers/net/lne390.c linux/drivers/net/lne390.c
--- v2.1.104/linux/drivers/net/lne390.c	Sun Jun  7 11:16:32 1998
+++ linux/drivers/net/lne390.c	Sun Jun  7 10:37:41 1998
@@ -162,12 +162,21 @@
 	}
 #endif
 
+	if (load_8390_module("lne390.c"))
+		return -ENOSYS;
+
 	/* We should have a "dev" from Space.c or the static module table. */
 	if (dev == NULL) {
 		printk("lne390.c: Passed a NULL device.\n");
 		dev = init_etherdev(0, 0);
 	}
 
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk ("lne390.c: unable to allocate memory for dev->priv!\n");
+		return -ENOMEM;
+	}
+
 	printk("lne390.c: LNE390%X in EISA slot %d, address", 0xa+revision, ioaddr/0x1000);
 	for(i = 0; i < ETHER_ADDR_LEN; i++)
 		printk(" %02x", (dev->dev_addr[i] = inb(ioaddr + LNE390_SA_PROM + i)));
@@ -187,6 +196,8 @@
 
 	if (request_irq(dev->irq, ei_interrupt, 0, "lne390", NULL)) {
 		printk (" unable to get IRQ %d.\n", dev->irq);
+		kfree(dev->priv);
+		dev->priv = NULL;
 		return EAGAIN;
 	}
 
@@ -218,6 +229,8 @@
 			printk(KERN_CRIT "lne390.c: or to an address above %p.\n", high_memory);
 			printk(KERN_CRIT "lne390.c: Driver NOT installed.\n");
 			free_irq(dev->irq, dev);
+			kfree(dev->priv);
+			dev->priv = NULL;
 			return EINVAL;
 		}
 		dev->mem_start = (unsigned long)ioremap(dev->mem_start, LNE390_STOP_PG*0x100);
@@ -226,6 +239,8 @@
 			printk(KERN_ERR "lne390.c: Try using EISA SCU to set memory below 1MB.\n");
 			printk(KERN_ERR "lne390.c: Driver NOT installed.\n");
 			free_irq(dev->irq, dev);
+			kfree(dev->priv);
+			dev->priv = NULL;
 			return EAGAIN;
 		}
 		printk("lne390.c: remapped %dkB card memory to virtual address %#lx\n",
@@ -236,13 +251,6 @@
 		+ (LNE390_STOP_PG - LNE390_START_PG)*256;
 	dev->rmem_start = dev->mem_start + TX_PAGES*256;
 
-	/* Allocate dev->priv and fill in 8390 specific dev fields. */
-	if (ethdev_init(dev)) {
-		printk ("lne390.c: unable to allocate memory for dev->priv!\n");
-		free_irq(dev->irq, dev);
-		return -ENOMEM;
-	}
-
 	/* The 8390 offset is zero for the LNE390 */
 	dev->base_addr = ioaddr;
 	request_region(dev->base_addr, LNE390_IO_EXTENT, "lne390");
@@ -397,12 +405,15 @@
 		if (io[this_dev] == 0 && this_dev != 0) break;
 		if (register_netdev(dev) != 0) {
 			printk(KERN_WARNING "lne390.c: No LNE390 card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0) return 0;	/* Got at least one. */
+			if (found != 0) {	/* Got at least one. */
+				lock_8390_module();
+				return 0;
+			}
 			return -ENXIO;
 		}
 		found++;
 	}
-
+	lock_8390_module();
 	return 0;
 }
 
@@ -413,13 +424,15 @@
 	for (this_dev = 0; this_dev < MAX_LNE_CARDS; this_dev++) {
 		struct device *dev = &dev_lne[this_dev];
 		if (dev->priv != NULL) {
-			kfree(dev->priv);
-			dev->priv = NULL;
+			void *priv = dev->priv;
 			free_irq(dev->irq, dev);
 			release_region(dev->base_addr, LNE390_IO_EXTENT);
+			dev->priv = NULL;
 			unregister_netdev(dev);
+			kfree(priv);
 		}
 	}
+	unlock_8390_module();
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v2.1.104/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v2.1.104/linux/drivers/net/ne.c	Sun Jun  7 11:16:32 1998
+++ linux/drivers/net/ne.c	Sun Jun  7 10:37:41 1998
@@ -225,6 +225,7 @@
 		printk("ne.c: PCI BIOS reports %s at i/o %#x, irq %d.\n",
 				pci_clone_list[i].name,
 				pci_ioaddr, pci_irq_line);
+		printk("*\n* Use of the PCI-NE2000 driver with this card is recommended!\n*\n");
 		if (ne_probe1(dev, pci_ioaddr) != 0) {	/* Shouldn't happen. */
 			printk(KERN_ERR "ne.c: Probe of PCI card at %#x failed.\n", pci_ioaddr);
 			pci_irq_line = 0;
@@ -265,6 +266,9 @@
 	}
     }
 
+    if (load_8390_module("ne.c"))
+	return -ENOSYS;
+
     /* We should have a "dev" from Space.c or the static module table. */
     if (dev == NULL) {
 	printk(KERN_ERR "ne.c: Passed a NULL device.\n");
@@ -434,6 +438,7 @@
 	    printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
 
             kfree(dev->priv);
+	    dev->priv = NULL;
 	    return EAGAIN;
 	}
     }
@@ -755,15 +760,17 @@
 			found++;
 			continue;
 		}
-		if (found != 0) 	/* Got at least one. */
+		if (found != 0) { 	/* Got at least one. */
+			lock_8390_module();
 			return 0;
+		}
 		if (io[this_dev] != 0)
 			printk(KERN_WARNING "ne.c: No NE*000 card found at i/o = %#x\n", io[this_dev]);
 		else
 			printk(KERN_NOTICE "ne.c: No PCI cards found. Use \"io=0xNNN\" value(s) for ISA cards.\n");
 		return -ENXIO;
 	}
-
+	lock_8390_module();
 	return 0;
 }
 
@@ -775,13 +782,15 @@
 	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
 		struct device *dev = &dev_ne[this_dev];
 		if (dev->priv != NULL) {
+			void *priv = dev->priv;
 			free_irq(dev->irq, dev);
 			release_region(dev->base_addr, NE_IO_EXTENT);
-			unregister_netdev(dev);
-			kfree(dev->priv);
 			dev->priv = NULL;
+			unregister_netdev(dev);
+			kfree(priv);
 		}
 	}
+	unlock_8390_module();
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v2.1.104/linux/drivers/net/ne2k-pci.c linux/drivers/net/ne2k-pci.c
--- v2.1.104/linux/drivers/net/ne2k-pci.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/ne2k-pci.c	Sun Jun  7 10:37:41 1998
@@ -0,0 +1,617 @@
+/* ne2k-pci.c: A NE2000 clone on PCI bus driver for Linux. */
+/*
+	A Linux device driver for PCI NE2000 clones.
+
+	Authorship and other copyrights:
+	1992-1998 by Donald Becker, NE2000 core and various modifications.
+	1995-1998 by Paul Gortmaker, core modifications and PCI support.
+
+	Copyright 1993 assigned to the United States Government as represented
+	by the Director, National Security Agency.
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License, incorporated herein by reference.
+
+	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+	Center of Excellence in Space Data and Information Sciences
+	Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+	People are making PCI ne2000 clones! Oh the horror, the horror...
+
+	Issues remaining:
+	No full-duplex support.
+*/
+
+/* Our copyright info must remain in the binary. */
+static const char *version =
+"ne2k-pci.c:v0.99L 2/7/98 D. Becker/P. Gortmaker http://cesdis.gsfc.nasa.gov/linux/drivers/ne2k-pci.html\n";
+
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+/* Set statically or when loading the driver module. */
+static debug = 1;
+
+/* Some defines that people can play with if so inclined. */
+
+/* Use 32 bit data-movement operations instead of 16 bit. */
+#define USE_LONGIO
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
+/* #define PACKETBUF_MEMSIZE	0x40 */
+
+static struct {
+	unsigned short vendor, dev_id;
+	char *name;
+}
+pci_clone_list[] __initdata = {
+	{0x10ec, 0x8029, "RealTek RTL-8029"},
+	{0x1050, 0x0940, "Winbond 89C940"},
+	{0x11f6, 0x1401, "Compex RL2000"},
+	{0x8e2e, 0x3000, "KTI ET32P2"},
+	{0x4a14, 0x5000, "NetVin NV5000SC"},
+	{0x1106, 0x0926, "Via 82C926"},
+	{0,}
+};
+
+/* ---- No user-serviceable parts below ---- */
+
+#define NE_BASE	 (dev->base_addr)
+#define NE_CMD	 	0x00
+#define NE_DATAPORT	0x10	/* NatSemi-defined port window offset. */
+#define NE_RESET	0x1f	/* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT	0x20
+
+#define NESM_START_PG	0x40	/* First page of TX buffer */
+#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+int ne2k_pci_probe(struct device *dev);
+static struct device *ne2k_pci_probe1(struct device *dev, int ioaddr, int irq);
+
+static int ne2k_pci_open(struct device *dev);
+static int ne2k_pci_close(struct device *dev);
+
+static void ne2k_pci_reset_8390(struct device *dev);
+static void ne2k_pci_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+			  int ring_page);
+static void ne2k_pci_block_input(struct device *dev, int count,
+			  struct sk_buff *skb, int ring_offset);
+static void ne2k_pci_block_output(struct device *dev, const int count,
+		const unsigned char *buf, const int start_page);
+
+
+
+/* No room in the standard 8390 structure for extra info we need. */
+struct ne2k_pci_card {
+	struct ne2k_pci_card *next;
+	struct device *dev;
+	unsigned char pci_bus, pci_device_fn;
+};
+/* A list of all installed devices, for removing the driver module. */
+static struct ne2k_pci_card *ne2k_card_list = NULL;
+
+#ifdef MODULE
+
+int
+init_module(void)
+{
+	int retval;
+
+	/* We must emit version information. */
+	if (debug)
+		printk(KERN_INFO "%s", version);
+
+	retval = ne2k_pci_probe(0);
+
+	if (retval) {
+		printk(KERN_NOTICE "ne2k-pci.c: no (useable) cards found, driver NOT installed.\n");
+		return retval;
+	}
+	lock_8390_module();
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	struct device *dev;
+	struct ne2k_pci_card *this_card;
+
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	while (ne2k_card_list) {
+		dev = ne2k_card_list->dev;
+		unregister_netdev(dev);
+		release_region(dev->base_addr, NE_IO_EXTENT);
+		kfree(dev);
+		this_card = ne2k_card_list;
+		ne2k_card_list = ne2k_card_list->next;
+		kfree(this_card);
+	}
+	unlock_8390_module();
+}
+
+#endif  /* MODULE */
+
+/*
+  NEx000-clone boards have a Station Address (SA) PROM (SAPROM) in the packet
+  buffer memory space.  By-the-spec NE2000 clones have 0x57,0x57 in bytes
+  0x0e,0x0f of the SAPROM, while other supposed NE2000 clones must be
+  detected by their SA prefix.
+
+  Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+  mode results in doubled values, which can be detected and compensated for.
+
+  The probe is also responsible for initializing the card and filling
+  in the 'dev' and 'ei_status' structures.
+*/
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry netcard_drv =
+{"ne2k_pci", ne2k_pci_probe1, NE_IO_EXTENT, 0};
+#endif
+
+__initfunc (int ne2k_pci_probe(struct device *dev))
+{
+	static int pci_index = 0;	/* Static, for multiple calls. */
+	int cards_found = 0;
+	int i;
+
+	if ( ! pcibios_present())
+		return -ENODEV;
+
+	for (;pci_index < 0xff; pci_index++) {
+		unsigned char pci_bus, pci_device_fn;
+		u8 pci_irq_line;
+		u16 pci_command, new_command, vendor, device;
+		u32 pci_ioaddr;
+
+		if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
+								&pci_bus, &pci_device_fn)
+			!= PCIBIOS_SUCCESSFUL)
+			break;
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_VENDOR_ID, &vendor);
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_DEVICE_ID, &device);
+
+		/* Note: some vendor IDs (RealTek) have non-NE2k cards as well. */
+		for (i = 0; pci_clone_list[i].vendor != 0; i++)
+			if (pci_clone_list[i].vendor == vendor
+				&& pci_clone_list[i].dev_id == device)
+				break;
+		if (pci_clone_list[i].vendor == 0)
+			continue;
+
+		pcibios_read_config_dword(pci_bus, pci_device_fn,
+								  PCI_BASE_ADDRESS_0, &pci_ioaddr);
+		pcibios_read_config_byte(pci_bus, pci_device_fn,
+								 PCI_INTERRUPT_LINE, &pci_irq_line);
+		pcibios_read_config_word(pci_bus, pci_device_fn,
+								 PCI_COMMAND, &pci_command);
+
+		/* Remove I/O space marker in bit 0. */
+		pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
+
+		/* Avoid already found cards from previous calls */
+		if (check_region(pci_ioaddr, NE_IO_EXTENT))
+			continue;
+
+#ifndef MODULE
+		{
+			static unsigned version_printed = 0;
+			if (version_printed++ == 0)
+				printk(KERN_INFO "%s", version);
+		}
+#endif
+
+		/* Activate the card: fix for brain-damaged Win98 BIOSes. */
+		new_command = pci_command | PCI_COMMAND_IO;
+		if (pci_command != new_command) {
+			printk(KERN_INFO "  The PCI BIOS has not enabled this"
+				   " NE2k clone!  Updating PCI command %4.4x->%4.4x.\n",
+				   pci_command, new_command);
+			pcibios_write_config_word(pci_bus, pci_device_fn,
+									  PCI_COMMAND, new_command);
+		}
+
+		if (pci_irq_line <= 0 || pci_irq_line >= NR_IRQS)
+			printk(KERN_WARNING "  WARNING: The PCI BIOS assigned this PCI NE2k"
+				   " card to IRQ %d, which is unlikely to work!.\n"
+				   KERN_WARNING " You should use the PCI BIOS setup to assign"
+				   " a valid IRQ line.\n", pci_irq_line);
+
+		printk("ne2k-pci.c: PCI NE2000 clone '%s' at I/O %#x, IRQ %d.\n",
+			   pci_clone_list[i].name, pci_ioaddr, pci_irq_line);
+		dev = ne2k_pci_probe1(dev, pci_ioaddr, pci_irq_line);
+		if (dev == 0) {
+			/* Should not happen. */
+			printk(KERN_ERR "ne2k-pci: Probe of PCI card at %#x failed.\n",
+				   pci_ioaddr);
+			continue;
+		} else {
+			struct ne2k_pci_card *ne2k_card =
+				kmalloc(sizeof(struct ne2k_pci_card), GFP_KERNEL);
+			ne2k_card->next = ne2k_card_list;
+			ne2k_card_list = ne2k_card;
+			ne2k_card->dev = dev;
+			ne2k_card->pci_bus = pci_bus;
+			ne2k_card->pci_device_fn = pci_device_fn;
+		}
+		dev = 0;
+
+		cards_found++;
+	}
+
+	return cards_found ? 0 : -ENODEV;
+}
+
+__initfunc (static struct device *ne2k_pci_probe1(struct device *dev, int ioaddr, int irq))
+{
+	int i;
+	unsigned char SA_prom[32];
+	const char *name = NULL;
+	int start_page, stop_page;
+	int reg0 = inb(ioaddr);
+
+	if (reg0 == 0xFF)
+		return 0;
+
+	/* Do a preliminary verification that we have a 8390. */
+	{
+		int regd;
+		outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+		regd = inb(ioaddr + 0x0d);
+		outb(0xff, ioaddr + 0x0d);
+		outb(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+		inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+		if (inb(ioaddr + EN0_COUNTER0) != 0) {
+			outb(reg0, ioaddr);
+			outb(regd, ioaddr + 0x0d);	/* Restore the old values. */
+			return 0;
+		}
+	}
+
+	/* Reset card. Who knows what dain-bramaged state it was left in. */
+	{
+		unsigned long reset_start_time = jiffies;
+
+		outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+		/* This looks like a horrible timing loop, but it should never take
+		   more than a few cycles.
+		*/
+		while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
+			/* Limit wait: '2' avoids jiffy roll-over. */
+			if (jiffies - reset_start_time > 2) {
+				printk("ne2k-pci: Card failure (no reset ack).\n");
+				return 0;
+			}
+		
+		outb(0xff, ioaddr + EN0_ISR);		/* Ack all intr. */
+	}
+
+	if (load_8390_module("ne2k-pci.c")) {
+		return 0;
+	}
+
+	/* Read the 16 bytes of station address PROM.
+	   We must first initialize registers, similar to NS8390_init(eifdev, 0).
+	   We can't reliably read the SAPROM address without this.
+	   (I learned the hard way!). */
+	{
+		struct {unsigned char value, offset; } program_seq[] = {
+			{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+			{0x49,	EN0_DCFG},	/* Set word-wide access. */
+			{0x00,	EN0_RCNTLO},	/* Clear the count regs. */
+			{0x00,	EN0_RCNTHI},
+			{0x00,	EN0_IMR},	/* Mask completion irq. */
+			{0xFF,	EN0_ISR},
+			{E8390_RXOFF, EN0_RXCR},	/* 0x20  Set to monitor */
+			{E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */
+			{32,	EN0_RCNTLO},
+			{0x00,	EN0_RCNTHI},
+			{0x00,	EN0_RSARLO},	/* DMA starting at 0x0000. */
+			{0x00,	EN0_RSARHI},
+			{E8390_RREAD+E8390_START, E8390_CMD},
+		};
+		for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
+			outb(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+	}
+
+#ifdef notdef
+	/* Some broken PCI cards don't respect the byte-wide
+	   request in program_seq above, and hence don't have doubled up values.
+	*/
+	for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
+		SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+		SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
+		if (SA_prom[i] != SA_prom[i+1])
+			sa_prom_doubled = 0;
+	}
+
+	if (sa_prom_doubled)
+		for (i = 0; i < 16; i++)
+			SA_prom[i] = SA_prom[i+i];
+#else
+	for(i = 0; i < 32 /*sizeof(SA_prom)*/; i++)
+		SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+
+#endif
+
+	/* We always set the 8390 registers for word mode. */
+	outb(0x49, ioaddr + EN0_DCFG);
+	start_page = NESM_START_PG;
+	stop_page = NESM_STOP_PG;
+
+	/* Set up the rest of the parameters. */
+	name = "PCI NE2000";
+
+	dev = init_etherdev(dev, 0);
+
+	dev->irq = irq;
+	dev->base_addr = ioaddr;
+
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk ("%s: unable to get memory for dev->priv.\n", dev->name);
+		kfree(dev);
+		return 0;
+	}
+
+	request_region(ioaddr, NE_IO_EXTENT, dev->name);
+
+	printk("%s: %s found at %#x, IRQ %d, ",
+		   dev->name, name, ioaddr, dev->irq);
+	for(i = 0; i < 6; i++) {
+		printk("%2.2X%s", SA_prom[i], i == 5 ? ".\n": ":");
+		dev->dev_addr[i] = SA_prom[i];
+	}
+
+	ei_status.name = name;
+	ei_status.tx_start_page = start_page;
+	ei_status.stop_page = stop_page;
+	ei_status.word16 = 1;
+
+	ei_status.rx_start_page = start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+	/* Allow the packet buffer size to be overridden by know-it-alls. */
+	ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+	ei_status.reset_8390 = &ne2k_pci_reset_8390;
+	ei_status.block_input = &ne2k_pci_block_input;
+	ei_status.block_output = &ne2k_pci_block_output;
+	ei_status.get_8390_hdr = &ne2k_pci_get_8390_hdr;
+	dev->open = &ne2k_pci_open;
+	dev->stop = &ne2k_pci_close;
+	NS8390_init(dev, 0);
+	return dev;
+}
+
+static int
+ne2k_pci_open(struct device *dev)
+{
+	if (request_irq(dev->irq, ei_interrupt, SA_SHIRQ, dev->name, dev))
+		return -EAGAIN;
+	ei_open(dev);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int
+ne2k_pci_close(struct device *dev)
+{
+	ei_close(dev);
+	free_irq(dev->irq, dev);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* Hard reset the card.  This used to pause for the same period that a
+   8390 reset command required, but that shouldn't be necessary. */
+static void
+ne2k_pci_reset_8390(struct device *dev)
+{
+	unsigned long reset_start_time = jiffies;
+
+	if (debug > 1) printk("%s: Resetting the 8390 t=%ld...",
+						  dev->name, jiffies);
+
+	outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+	ei_status.txing = 0;
+	ei_status.dmaing = 0;
+
+	/* This check _should_not_ be necessary, omit eventually. */
+	while ((inb(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
+		if (jiffies - reset_start_time > 2) {
+			printk("%s: ne2k_pci_reset_8390() did not complete.\n", dev->name);
+			break;
+		}
+	outb(ENISR_RESET, NE_BASE + EN0_ISR);	/* Ack intr. */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void
+ne2k_pci_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+	int nic_base = dev->base_addr;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne2k_pci_get_8390_hdr "
+			   "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+			   dev->name, ei_status.dmaing, ei_status.irqlock,
+			   dev->interrupt);
+		return;
+	}
+
+	ei_status.dmaing |= 0x01;
+	outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+	outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+	outb(0, nic_base + EN0_RCNTHI);
+	outb(0, nic_base + EN0_RSARLO);		/* On page boundary */
+	outb(ring_page, nic_base + EN0_RSARHI);
+	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+#if defined(USE_LONGIO)
+	*(u32*)hdr = inl(NE_BASE + NE_DATAPORT);
+#else
+	insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+#endif
+
+	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+/* Block input and output, similar to the Crynwr packet driver.  If you
+   are porting to a new ethercard, look at the packet driver source for hints.
+   The NEx000 doesn't share the on-board packet memory -- you have to put
+   the packet out through the "remote DMA" dataport using outb. */
+
+static void
+ne2k_pci_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	int nic_base = dev->base_addr;
+	char *buf = skb->data;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne2k_pci_block_input "
+			   "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+			   dev->name, ei_status.dmaing, ei_status.irqlock,
+			   dev->interrupt);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+	outb(count & 0xff, nic_base + EN0_RCNTLO);
+	outb(count >> 8, nic_base + EN0_RCNTHI);
+	outb(ring_offset & 0xff, nic_base + EN0_RSARLO);
+	outb(ring_offset >> 8, nic_base + EN0_RSARHI);
+	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+#if defined(USE_LONGIO)
+	insl(NE_BASE + NE_DATAPORT, buf, count>>2);
+	if (count & 3) {
+		buf += count & ~3;
+		if (count & 2)
+			*((u16*)buf)++ = inw(NE_BASE + NE_DATAPORT);
+		if (count & 1)
+			*buf = inb(NE_BASE + NE_DATAPORT);
+	}
+#else
+	insw(NE_BASE + NE_DATAPORT,buf,count>>1);
+	if (count & 0x01) {
+		buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+	}
+#endif
+
+	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+}
+
+static void
+ne2k_pci_block_output(struct device *dev, int count,
+		const unsigned char *buf, const int start_page)
+{
+	int nic_base = NE_BASE;
+	unsigned long dma_start;
+
+	/* On little-endian it's always safe to round the count up for
+	   word writes. */
+	if (count & 0x01)
+		count++;
+
+	/* This *shouldn't* happen. If it does, it's the last thing you'll see */
+	if (ei_status.dmaing) {
+		printk("%s: DMAing conflict in ne2k_pci_block_output."
+			   "[DMAstat:%d][irqlock:%d][intr:%d]\n",
+			   dev->name, ei_status.dmaing, ei_status.irqlock,
+			   dev->interrupt);
+		return;
+	}
+	ei_status.dmaing |= 0x01;
+	/* We should already be in page 0, but to be safe... */
+	outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+#ifdef NE8390_RW_BUGFIX
+	/* Handle the read-before-write bug the same way as the
+	   Crynwr packet driver -- the NatSemi method doesn't work.
+	   Actually this doesn't always work either, but if you have
+	   problems with your NEx000 this is better than nothing! */
+	outb(0x42, nic_base + EN0_RCNTLO);
+	outb(0x00, nic_base + EN0_RCNTHI);
+	outb(0x42, nic_base + EN0_RSARLO);
+	outb(0x00, nic_base + EN0_RSARHI);
+	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+#endif
+	outb(ENISR_RDC, nic_base + EN0_ISR);
+
+   /* Now the normal output. */
+	outb(count & 0xff, nic_base + EN0_RCNTLO);
+	outb(count >> 8,   nic_base + EN0_RCNTHI);
+	outb(0x00, nic_base + EN0_RSARLO);
+	outb(start_page, nic_base + EN0_RSARHI);
+	outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+#if defined(USE_LONGIO)
+	outsl(NE_BASE + NE_DATAPORT, buf, count>>2);
+	if (count & 3) {
+		buf += count & ~3;
+		if (count & 2)
+			outw(*((u16*)buf)++, NE_BASE + NE_DATAPORT);
+	}
+#else
+	outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
+#endif
+
+	dma_start = jiffies;
+
+	while ((inb(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+		if (jiffies - dma_start > 2) { 			/* Avoid clock roll-over. */
+			printk("%s: timeout waiting for Tx RDC.\n", dev->name);
+			ne2k_pci_reset_8390(dev);
+			NS8390_init(dev,1);
+			break;
+		}
+
+	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+	ei_status.dmaing &= ~0x01;
+	return;
+}
+
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -DMODVERSIONS  -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer -I/usr/src/linux/drivers/net/ -c ne2k-pci.c"
+ *  alt-compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer -I/usr/src/linux/drivers/net/ -c ne2k-pci.c"
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ *  version-control: t
+ *  kept-new-versions: 5
+ * End:
+ */
diff -u --recursive --new-file v2.1.104/linux/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c
--- v2.1.104/linux/drivers/net/pcnet32.c	Mon Apr  6 17:41:00 1998
+++ linux/drivers/net/pcnet32.c	Sun Jun  7 10:16:57 1998
@@ -1,6 +1,6 @@
 /* pcnet32.c: An AMD PCnet32 ethernet driver for linux. */
 /*
- *      Copyright 1996,97 Thomas Bogendoerfer
+ *      Copyright 1996,97,98 Thomas Bogendoerfer
  * 
  * 	Derived from the lance driver written 1993,1994,1995 by Donald Becker.
  * 
@@ -13,9 +13,14 @@
  * 	This driver is for PCnet32 and PCnetPCI based ethercards
  */
 
-static const char *version = "pcnet32.c:v0.23 8.2.97 tsbogend@alpha.franken.de\n";
+static const char *version = "pcnet32.c:v1.00 30.5.98 tsbogend@alpha.franken.de\n";
 
 #include <linux/config.h>
+#include <linux/module.h>
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
@@ -25,6 +30,7 @@
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
+#include <linux/delay.h>
 #include <linux/init.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -36,12 +42,15 @@
 
 static unsigned int pcnet32_portlist[] __initdata = {0x300, 0x320, 0x340, 0x360, 0};
 
-#ifdef PCNET32_DEBUG
-static int pcnet32_debug = PCNET32_DEBUG;
-#else
 static int pcnet32_debug = 1;
+
+#ifdef MODULE
+static struct device *pcnet32_dev = NULL;
 #endif
 
+static const int max_interrupt_work = 20;
+static const int rx_copybreak = 200;
+
 /*
  * 				Theory of Operation
  * 
@@ -58,7 +67,7 @@
  *         only tested on Alpha Noname Board
  * v0.02:  changed IRQ handling for new interrupt scheme (dev_id)
  *         tested on a ASUS SP3G
- * v0.10:  fixed an odd problem with the 79C794 in a Compaq Deskpro XL
+ * v0.10:  fixed an odd problem with the 79C974 in a Compaq Deskpro XL
  *         looks like the 974 doesn't like stopping and restarting in a
  *         short period of time; now we do a reinit of the lance; the
  *         bug was triggered by doing ifconfig eth0 <ip> broadcast <addr>
@@ -77,7 +86,18 @@
  *	   in arch/i386/bios32.c
  * v0.21:  added endian conversion for ppc, from work by cort@cs.nmt.edu
  * v0.22:  added printing of status to ring dump
- * v0.23:  changed enet_statistics to net_device_stats
+ * v0.23:  changed enet_statistics to net_devive_stats
+ * v0.90:  added multicast filter
+ *         added module support
+ *         changed irq probe to new style
+ *         added PCnetFast chip id
+ *         added fix for receive stalls with Intel saturn chipsets
+ *         added in-place rx skbs like in the tulip driver
+ *         minor cleanups
+ * v0.91:  added PCnetFast+ chip id
+ *         back port to 2.0.x
+ * v1.00:  added some stuff from Donald Becker's 2.0.34 version
+ *         added support for byte counters in net_dev_stats
  */
 
 
@@ -108,6 +128,8 @@
 #define PCNET32_BUS_IF 0x16
 #define PCNET32_TOTAL_SIZE 0x18
 
+#define CRC_POLYNOMIAL_LE 0xedb88320UL  /* Ethernet CRC, little endian */
+
 /* The PCNET32 Rx and Tx ring descriptors. */
 struct pcnet32_rx_head {
 	u32 base;
@@ -145,14 +167,17 @@
 	struct pcnet32_init_block	init_block;
 	const char *name;
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
-	struct sk_buff* tx_skbuff[TX_RING_SIZE];
-	unsigned long rx_buffs;			/* Address of Rx and Tx buffers. */
+	struct sk_buff *tx_skbuff[TX_RING_SIZE];
+        struct sk_buff *rx_skbuff[RX_RING_SIZE];
 	int cur_rx, cur_tx;			/* The next free ring entry */
 	int dirty_rx, dirty_tx;		        /* The ring entries to be free()ed. */
 	struct net_device_stats stats;
 	char tx_full;
 	unsigned long lock;
         char shared_irq;                         /* shared irq possible */
+#ifdef MODULE
+        struct device *next;
+#endif    
 };
 
 int  pcnet32_probe(struct device *dev);
@@ -204,9 +229,6 @@
 		pci_command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO;
 		pci_write_config_word(pdev, PCI_COMMAND, pci_command);
 	    }
-#ifdef __powerpc__
-	    irq_line = 15;
-#endif
 	    
 	    printk("Found PCnet/PCI at %#x, irq %d.\n",
 		   ioaddr, irq_line);
@@ -275,17 +297,20 @@
 	 case 0x2621:
 	    chipname = "PCnet/PCI II 79C970A";
 	    break;
+	 case 0x2623:
+	    chipname = "PCnet/FAST 79C971";
+	    break;
+	 case 0x2624:
+	    chipname = "PCnet/FAST+ 79C972";
+	    break;
 	 default:
 	    printk("pcnet32: PCnet version %#x, no PCnet32 chip.\n",chip_version);
 	    return ENODEV;
 	}
     }
     
-    /* We should have a "dev" from Space.c or the static module table. */
-    if (dev == NULL) {
-	printk(KERN_ERR "pcnet32.c: Passed a NULL device.\n");
+    if (dev == NULL)
 	dev = init_etherdev(0, 0);
-    }
 
     printk("%s: %s at %#3x,", dev->name, chipname, ioaddr);
 
@@ -304,7 +329,6 @@
     dev->priv = lp;
     lp->name = chipname;
     lp->shared_irq = shared;
-    lp->rx_buffs = (unsigned long) kmalloc(PKT_BUF_SZ*RX_RING_SIZE, GFP_DMA | GFP_KERNEL);
 
     lp->init_block.mode = le16_to_cpu(0x0003);	/* Disable Rx and Tx. */
     lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); 
@@ -335,17 +359,18 @@
     if (dev->irq >= 2)
       printk(" assigned IRQ %d.\n", dev->irq);
     else {
+	unsigned long irq_mask = probe_irq_on();
+	
 	/*
 	 * To auto-IRQ we enable the initialization-done and DMA error
 	 * interrupts. For ISA boards we get a DMA error, but VLB and PCI
 	 * boards will work.
 	 */
-	autoirq_setup(0);
-	
 	/* Trigger an initialization just for the interrupt. */
 	outw(0x0041, ioaddr+PCNET32_DATA);
-
-	dev->irq = autoirq_report(1);
+	mdelay (1);
+	
+	dev->irq = probe_irq_off (irq_mask);
 	if (dev->irq)
 	  printk(", probed IRQ %d.\n", dev->irq);
 	else {
@@ -368,6 +393,11 @@
     dev->stop = &pcnet32_close;
     dev->get_stats = &pcnet32_get_stats;
     dev->set_multicast_list = &pcnet32_set_multicast_list;
+    
+#ifdef MODULE
+    lp->next = pcnet32_dev;
+    pcnet32_dev = dev;
+#endif	
 
     /* Fill in the generic fields of the device structure. */
     ether_setup(dev);
@@ -441,6 +471,8 @@
 		printk("%s: PCNET32 open after %d ticks, init block %#x csr0 %4.4x.\n",
 			   dev->name, i, (u32) virt_to_bus(&lp->init_block), inw(ioaddr+PCNET32_DATA));
 
+        MOD_INC_USE_COUNT;
+    
 	return 0;					/* Always succeed */
 }
 
@@ -478,15 +510,23 @@
 {
 	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
 	int i;
+        struct sk_buff *skb;
 
 	lp->lock = 0, lp->tx_full = 0;
 	lp->cur_rx = lp->cur_tx = 0;
 	lp->dirty_rx = lp->dirty_tx = 0;
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
-		lp->rx_ring[i].base = (u32)le32_to_cpu(virt_to_bus((char *)lp->rx_buffs + i*PKT_BUF_SZ));
+	    skb = dev_alloc_skb (PKT_BUF_SZ);
+	    if (skb) {
+		lp->rx_skbuff[i] = skb;
+		skb_reserve (skb, 2);
+		lp->rx_ring[i].base = (u32)le32_to_cpu(virt_to_bus(skb->tail));
 		lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ);
-	        lp->rx_ring[i].status = le16_to_cpu(0x8000);	    
+	        lp->rx_ring[i].status = le16_to_cpu(0x8000);
+	    }
+	    else
+		break;
 	}
 	/* The Tx buffer address is filled in as needed, but we do need to clear
 	   the upper ownership bit. */
@@ -503,7 +543,7 @@
 }
 
 static void
-pcnet32_restart(struct device *dev, unsigned int csr0_bits, int must_reinit)
+pcnet32_restart(struct device *dev, unsigned int csr0_bits)
 {
         int i;
 	unsigned int ioaddr = dev->base_addr;
@@ -557,9 +597,9 @@
 			printk("\n");
 		}
 #endif
-		pcnet32_restart(dev, 0x0042, 1);
+		pcnet32_restart(dev, 0x0042);
 
-		dev->tbusy=0;
+		dev->tbusy = 0;
 		dev->trans_start = jiffies;
 
 		return 0;
@@ -603,6 +643,7 @@
         lp->tx_ring[entry].status = le16_to_cpu(0x8300);
 
 	lp->cur_tx++;
+        lp->stats.tx_bytes += skb->len;
 
 	/* Trigger an immediate send poll. */
 	outw(0x0000, ioaddr+PCNET32_ADDR);
@@ -614,7 +655,7 @@
 	cli();
 	lp->lock = 0;
 	if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
-		dev->tbusy=0;
+		clear_bit (0, (void *)&dev->tbusy);
 	else
 		lp->tx_full = 1;
 	restore_flags(flags);
@@ -629,8 +670,7 @@
 	struct device *dev = (struct device *)dev_id;
 	struct pcnet32_private *lp;
 	unsigned int csr0, ioaddr;
-	int boguscnt=10;
-	int must_restart;
+	int boguscnt =  max_interrupt_work;
 
 	if (dev == NULL) {
 		printk ("pcnet32_interrupt(): irq %d for unknown device.\n", irq);
@@ -645,13 +685,10 @@
 	dev->interrupt = 1;
 
 	outw(0x00, dev->base_addr + PCNET32_ADDR);
-	while ((csr0 = inw(dev->base_addr + PCNET32_DATA)) & 0x8600
-		   && --boguscnt >= 0) {
+	while ((csr0 = inw(dev->base_addr + PCNET32_DATA)) & 0x8600 && --boguscnt >= 0) {
 		/* Acknowledge all of the current interrupt sources ASAP. */
 		outw(csr0 & ~0x004f, dev->base_addr + PCNET32_DATA);
 
-		must_restart = 0;
-
 		if (pcnet32_debug > 5)
 			printk("%s: interrupt  csr0=%#2.2x new csr=%#2.2x.\n",
 				   dev->name, csr0, inw(dev->base_addr + PCNET32_DATA));
@@ -684,8 +721,10 @@
 						/* Remove this verbosity later! */
 						printk("%s: Tx FIFO error! Status %4.4x.\n",
 							   dev->name, csr0);
-						/* Restart the chip. */
-						must_restart = 1;
+					        /* stop the chip to clear the error condition, then restart */
+					        outw(0x0000, dev->base_addr + PCNET32_ADDR);
+					        outw(0x0004, dev->base_addr + PCNET32_DATA);
+					        pcnet32_restart(dev, 0x0002);
 					}
 				} else {
 					if (status & 0x1800)
@@ -713,28 +752,31 @@
 				&& dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
 				/* The ring is no longer full, clear tbusy. */
 				lp->tx_full = 0;
-				dev->tbusy = 0;
+				clear_bit(0, (void *)&dev->tbusy);
 				mark_bh(NET_BH);
 			}
-
 			lp->dirty_tx = dirty_tx;
 		}
 
 		/* Log misc errors. */
 		if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */
-		if (csr0 & 0x1000) lp->stats.rx_errors++; /* Missed a Rx frame. */
+		if (csr0 & 0x1000) {
+		    /*
+		     * this happens when our receive ring is full. This shouldn't
+		     * be a problem as we will see normal rx interrupts for the frames
+		     * in the receive ring. But there are some PCI chipsets (I can reproduce
+		     * this on SP3G with Intel saturn chipset) which have sometimes problems
+		     * and will fill up the receive ring with error descriptors. In this
+		     * situation we don't get a rx interrupt, but a missed frame interrupt sooner
+		     * or later. So we try to clean up our receive ring here.
+		     */
+		    pcnet32_rx(dev);
+		    lp->stats.rx_errors++; /* Missed a Rx frame. */
+		}
 		if (csr0 & 0x0800) {
 			printk("%s: Bus master arbitration failure, status %4.4x.\n",
 				   dev->name, csr0);
-			/* Restart the chip. */
-			must_restart = 1;
-		}
-
-		if (must_restart) {
-			/* stop the chip to clear the error condition, then restart */
-			outw(0x0000, dev->base_addr + PCNET32_ADDR);
-			outw(0x0004, dev->base_addr + PCNET32_DATA);
-			pcnet32_restart(dev, 0x0002, 0);
+		        /* unlike for the lance, there is no restart needed */
 		}
 	}
 
@@ -757,7 +799,7 @@
 	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
 	int entry = lp->cur_rx & RX_RING_MOD_MASK;
 	int i;
-		
+
 	/* If we own the next entry, it's a new packet. Send it up. */
 	while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) {
 		int status = (short)le16_to_cpu(lp->rx_ring[entry].status) >> 8;
@@ -781,38 +823,53 @@
 			short pkt_len = (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)-4;
 			struct sk_buff *skb;
 			
-			if(pkt_len<60)
-			{
+			if(pkt_len < 60) {
 				printk("%s: Runt packet!\n",dev->name);
 				lp->stats.rx_errors++;
-			}
-			else
-			{
+			} else {
+			    int rx_in_place = 0;
+			    
+			    if (pkt_len > rx_copybreak) {
+				struct sk_buff *newskb;
+				
+				if ((newskb = dev_alloc_skb (PKT_BUF_SZ))) {
+				    skb_reserve (newskb, 2);
+				    skb = lp->rx_skbuff[entry];
+				    skb_put (skb, pkt_len);
+				    lp->rx_skbuff[entry] = newskb;
+				    newskb->dev = dev;
+				    lp->rx_ring[entry].base = le32_to_cpu(virt_to_bus(newskb->tail));
+				    rx_in_place = 1;
+				} else
+				    skb = NULL;
+			    } else
 				skb = dev_alloc_skb(pkt_len+2);
-				if (skb == NULL) 
-				{
-					printk("%s: Memory squeeze, deferring packet.\n", dev->name);
-					for (i=0; i < RX_RING_SIZE; i++)
-						if ((short)le16_to_cpu(lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status) < 0)
-							break;
-
-					if (i > RX_RING_SIZE -2) 
-					{
-						lp->stats.rx_dropped++;
-						lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
-						lp->cur_rx++;
-					}
+			    
+			    if (skb == NULL) {
+				printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+				for (i=0; i < RX_RING_SIZE; i++)
+				    if ((short)le16_to_cpu(lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status) < 0)
 					break;
+
+				if (i > RX_RING_SIZE -2) {
+				    lp->stats.rx_dropped++;
+				    lp->rx_ring[entry].status |= le16_to_cpu(0x8000);
+				    lp->cur_rx++;
 				}
-				skb->dev = dev;
+				break;
+			    }
+			    skb->dev = dev;
+			    if (!rx_in_place) {
 				skb_reserve(skb,2);	/* 16 byte align */
 				skb_put(skb,pkt_len);	/* Make room */
 				eth_copy_and_sum(skb,
 					(unsigned char *)bus_to_virt(le32_to_cpu(lp->rx_ring[entry].base)),
 					pkt_len,0);
-				skb->protocol=eth_type_trans(skb,dev);
-				netif_rx(skb);
-				lp->stats.rx_packets++;
+			    }
+			    lp->stats.rx_bytes += skb->len;
+			    skb->protocol=eth_type_trans(skb,dev);
+			    netif_rx(skb);
+			    lp->stats.rx_packets++;
 			}
 		}
 		/* The docs say that the buffer length isn't touched, but Andrew Boyd
@@ -833,9 +890,10 @@
 {
 	unsigned int ioaddr = dev->base_addr;
 	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv;
+        int i;
 
 	dev->start = 0;
-	dev->tbusy = 1;
+	set_bit (0, (void *)&dev->tbusy);
 
 	outw(112, ioaddr+PCNET32_ADDR);
 	lp->stats.rx_missed_errors = inw(ioaddr+PCNET32_DATA);
@@ -851,6 +909,22 @@
 	outw(0x0004, ioaddr+PCNET32_DATA);
 
 	free_irq(dev->irq, dev);
+    
+        /* free all allocated skbuffs */
+        for (i = 0; i < RX_RING_SIZE; i++) {
+	    lp->rx_ring[i].status = 0;	    	    	    
+	    if (lp->rx_skbuff[i])
+		dev_kfree_skb(lp->rx_skbuff[i]);
+	    lp->rx_skbuff[i] = NULL;
+	}
+    
+        for (i = 0; i < TX_RING_SIZE; i++) {
+	    if (lp->tx_skbuff[i])
+		dev_kfree_skb(lp->tx_skbuff[i]);
+	    lp->rx_skbuff[i] = NULL;
+	}
+    
+        MOD_DEC_USE_COUNT;
 
 	return 0;
 }
@@ -874,6 +948,57 @@
 	return &lp->stats;
 }
 
+
+/* taken from the sunlance driver, which it took from the depca driver */
+static void pcnet32_load_multicast (struct device *dev)
+{
+    struct pcnet32_private *lp = (struct pcnet32_private *) dev->priv;
+    volatile struct pcnet32_init_block *ib = &lp->init_block;
+    volatile u16 *mcast_table = (u16 *)&ib->filter;
+    struct dev_mc_list *dmi=dev->mc_list;
+    char *addrs;
+    int i, j, bit, byte;
+    u32 crc, poly = CRC_POLYNOMIAL_LE;
+	
+    /* set all multicast bits */
+    if (dev->flags & IFF_ALLMULTI){ 
+	ib->filter [0] = 0xffffffff;
+	ib->filter [1] = 0xffffffff;
+	return;
+    }
+    /* clear the multicast filter */
+    ib->filter [0] = 0;
+    ib->filter [1] = 0;
+
+    /* Add addresses */
+    for (i = 0; i < dev->mc_count; i++){
+	addrs = dmi->dmi_addr;
+	dmi   = dmi->next;
+	
+	/* multicast address? */
+	if (!(*addrs & 1))
+	    continue;
+	
+	crc = 0xffffffff;
+	for (byte = 0; byte < 6; byte++)
+	    for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
+		int test;
+		
+		test = ((bit ^ crc) & 0x01);
+		crc >>= 1;
+		
+		if (test) {
+		    crc = crc ^ poly;
+		}
+	    }
+	
+	crc = crc >> 26;
+	mcast_table [crc >> 4] |= 1 << (crc & 0xf);
+    }
+    return;
+}
+
+
 /* Set or clear the multicast filter for this adaptor.
  */
 
@@ -885,22 +1010,53 @@
 	if (dev->flags&IFF_PROMISC) {
 		/* Log any net taps. */
 		printk("%s: Promiscuous mode enabled.\n", dev->name);
-	        lp->init_block.mode = 0x8000;
+	        lp->init_block.mode = le16_to_cpu(0x8000);
 	} else {
-		int num_addrs=dev->mc_count;
-		if(dev->flags&IFF_ALLMULTI)
-			num_addrs=1;
-		/* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */
-		memset(lp->init_block.filter , (num_addrs == 0) ? 0 : -1, sizeof(lp->init_block.filter));
-	        lp->init_block.mode = 0x0000;	        
+	        lp->init_block.mode = 0x0000;
+	        pcnet32_load_multicast (dev);
 	}
     
 	outw(0, ioaddr+PCNET32_ADDR);
 	outw(0x0004, ioaddr+PCNET32_DATA); /* Temporarily stop the lance.	 */
 
-	pcnet32_restart(dev, 0x0042, 0); /*  Resume normal operation */
+	pcnet32_restart(dev, 0x0042); /*  Resume normal operation */
+}
+
+#ifdef MODULE
+MODULE_PARM(debug, "i");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(rx_copybreak, "i");
 
+/* An additional parameter that may be passed in... */
+static int debug = -1;
+
+int
+init_module(void)
+{
+        if (debug > 0)
+	        pcnet32_debug = debug;
+    
+	pcnet32_dev = NULL;
+	return pcnet32_probe(NULL);
 }
+
+void
+cleanup_module(void)
+{
+	struct device *next_dev;
+
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	while (pcnet32_dev) {
+		next_dev = ((struct pcnet32_private *) pcnet32_dev->priv)->next;
+		unregister_netdev(pcnet32_dev);
+		release_region(pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE);
+	        kfree(pcnet32_dev->priv);
+		kfree(pcnet32_dev);
+		pcnet32_dev = next_dev;
+	}
+}
+#endif /* MODULE */
+
 
 
 /*
diff -u --recursive --new-file v2.1.104/linux/drivers/net/shaper.c linux/drivers/net/shaper.c
--- v2.1.104/linux/drivers/net/shaper.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/net/shaper.c	Sun Jun  7 10:37:41 1998
@@ -326,7 +326,8 @@
 			 */
 			 
 			skb_unlink(skb);
-			shaper->recovery=jiffies+skb->shapelen;
+			if (shaper->recovery < skb->shapeclock + skb->shapelen)
+				shaper->recovery = skb->shapeclock + skb->shapelen;
 			restore_flags(flags);
 
 			/*
diff -u --recursive --new-file v2.1.104/linux/drivers/net/smc-mca.c linux/drivers/net/smc-mca.c
--- v2.1.104/linux/drivers/net/smc-mca.c	Sun Jun  7 11:16:32 1998
+++ linux/drivers/net/smc-mca.c	Sun Jun  7 10:37:41 1998
@@ -118,6 +118,9 @@
 	reg4 = inb(ioaddr + 4) & 0x7f;
 	outb(reg4, ioaddr + 4);
 
+	if (load_8390_module("wd.c"))
+		return -ENOSYS;
+
 	printk("%s: SMC Ultra MCA at %#3x,", dev->name, ioaddr);
 
 	for (i = 0; i < 6; i++)
@@ -347,12 +350,15 @@
 		if (register_netdev(dev) != 0)
 		{
 			printk(KERN_WARNING "smc-mca.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0)
-				return 0;   /* Got at least one. */
+			if (found != 0) {	/* Got at least one. */
+				lock_8390_module();
+				return 0;
+			}
 			return -ENXIO;
 		}
 		found++;
 	}
+	lock_8390_module();
 	return 0;
 }
 
@@ -365,14 +371,16 @@
 		struct device *dev = &dev_ultra[this_dev];
         	if (dev->priv != NULL)
         	{
+			void *priv = dev->priv;
 			/* NB: ultra_close_card() does free_irq */
 			int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
-			unregister_netdev(dev);
-			kfree(dev->priv);
-			dev->priv = NULL;
 			release_region(ioaddr, ULTRA_IO_EXTENT);
+			dev->priv = NULL;
+			unregister_netdev(dev);
+			kfree(priv);
 		}
 	}
+	unlock_8390_module();
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v2.1.104/linux/drivers/net/smc-ultra.c linux/drivers/net/smc-ultra.c
--- v2.1.104/linux/drivers/net/smc-ultra.c	Mon Feb 23 18:12:06 1998
+++ linux/drivers/net/smc-ultra.c	Sun Jun  7 10:37:41 1998
@@ -152,6 +152,9 @@
 	if ((checksum & 0xff) != 0xFF)
 		return ENODEV;
 
+	if (load_8390_module("smc-ultra.c"))
+		return -ENOSYS;
+
 	/* We should have a "dev" from Space.c or the static module table. */
 	if (dev == NULL) {
 		printk("smc-ultra.c: Passed a NULL device.\n");
@@ -456,12 +459,15 @@
 		}
 		if (register_netdev(dev) != 0) {
 			printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0) return 0;	/* Got at least one. */
+			if (found != 0) {	/* Got at least one. */
+				lock_8390_module();
+				return 0;
+			}
 			return -ENXIO;
 		}
 		found++;
 	}
-
+	lock_8390_module();
 	return 0;
 }
 
@@ -473,14 +479,16 @@
 	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
 		struct device *dev = &dev_ultra[this_dev];
 		if (dev->priv != NULL) {
-			/* NB: ultra_close_card() does free_irq */
 			int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
-			unregister_netdev(dev);
-			kfree(dev->priv);
-			dev->priv = NULL;
+			void *priv = dev->priv;
+			/* NB: ultra_close_card() does free_irq */
 			release_region(ioaddr, ULTRA_IO_EXTENT);
+			dev->priv = NULL;
+			unregister_netdev(dev);
+			kfree(priv);
 		}
 	}
+	unlock_8390_module();
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v2.1.104/linux/drivers/net/smc-ultra32.c linux/drivers/net/smc-ultra32.c
--- v2.1.104/linux/drivers/net/smc-ultra32.c	Sun Jun  7 11:16:32 1998
+++ linux/drivers/net/smc-ultra32.c	Sun Jun  7 10:37:41 1998
@@ -148,6 +148,9 @@
 	if ((checksum & 0xff) != 0xff)
 		return ENODEV;
 
+	if (load_8390_module("smc-ultra32.c"))
+		return -ENOSYS;
+
 	/* We should have a "dev" from Space.c or the static module table. */
 	if (dev == NULL) {
 		printk("smc-ultra32.c: Passed a NULL device.\n");
@@ -386,13 +389,16 @@
 		dev->name = namelist+(NAMELEN*this_dev);
 		dev->init = ultra32_probe;
 		if (register_netdev(dev) != 0) {
-			if (found > 0) return 0; /* Got at least one. */
+			if (found > 0) { /* Got at least one. */
+				lock_8390_module();
+				return 0;
+			}
 			printk(KERN_WARNING "smc-ultra32.c: No SMC Ultra32 found.\n");
 			return -ENXIO;
 		}
 		found++;
 	}
-
+	lock_8390_module();
 	return 0;
 }
 
@@ -403,13 +409,15 @@
 	for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) {
 		struct device *dev = &dev_ultra[this_dev];
 		if (dev->priv != NULL) {
-			/* NB: ultra32_close_card() does free_irq + irq2dev */
 			int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET;
-			kfree(dev->priv);
-			dev->priv = NULL;
+			void *priv = dev->priv;
+			/* NB: ultra32_close_card() does free_irq */
 			release_region(ioaddr, ULTRA32_IO_EXTENT);
+			dev->priv = NULL;
 			unregister_netdev(dev);
+			kfree(priv);
 		}
 	}
+	unlock_8390_module();
 }
 #endif /* MODULE */
diff -u --recursive --new-file v2.1.104/linux/drivers/net/wd.c linux/drivers/net/wd.c
--- v2.1.104/linux/drivers/net/wd.c	Mon Feb 23 18:12:06 1998
+++ linux/drivers/net/wd.c	Sun Jun  7 10:37:41 1998
@@ -56,7 +56,7 @@
 						  struct sk_buff *skb, int ring_offset);
 static void wd_block_output(struct device *dev, int count,
 							const unsigned char *buf, int start_page);
-static int wd_close_card(struct device *dev);
+static int wd_close(struct device *dev);
 
 
 #define WD_START_PG		0x00	/* First page of TX buffer */
@@ -124,6 +124,10 @@
 		|| (checksum & 0xff) != 0xFF)
 		return ENODEV;
 
+	/* Looks like we have a card. Make sure 8390 support is available. */
+        if (load_8390_module("wd.c"))
+                return -ENOSYS;
+
 	/* We should have a "dev" from Space.c or the static module table. */
 	if (dev == NULL) {
 		printk("wd.c: Passed a NULL device.\n");
@@ -140,7 +144,7 @@
 	if (ei_debug  &&  version_printed++ == 0)
 		printk(version);
 
-	printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr);
+	printk("%s: WD80x3 at %#3x,", dev->name, ioaddr);
 	for (i = 0; i < 6; i++)
 		printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
 
@@ -252,20 +256,21 @@
 	} else if (dev->irq == 2)		/* Fixup bogosity: IRQ2 is really IRQ9 */
 		dev->irq = 9;
 
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk (" unable to get memory for dev->priv.\n");
+		return -ENOMEM;
+	}
+
 	/* Snarf the interrupt now.  There's no point in waiting since we cannot
 	   share and the board will usually be enabled. */
 	if (request_irq(dev->irq, ei_interrupt, 0, model_name, dev)) {
 		printk (" unable to get IRQ %d.\n", dev->irq);
+		kfree(dev->priv);
+		dev->priv = NULL;
 		return EAGAIN;
 	}
 
-	/* Allocate dev->priv and fill in 8390 specific dev fields. */
-	if (ethdev_init(dev)) {
-		printk (" unable to get memory for dev->priv.\n");
-		free_irq(dev->irq, dev);
-		return -ENOMEM;
-	}
-
 	/* OK, were are certain this is going to work.  Setup the device. */
 	request_region(ioaddr, WD_IO_EXTENT, model_name);
 
@@ -294,7 +299,7 @@
 	ei_status.block_output = &wd_block_output;
 	ei_status.get_8390_hdr = &wd_get_8390_hdr;
 	dev->open = &wd_open;
-	dev->stop = &wd_close_card;
+	dev->stop = &wd_close;
 	NS8390_init(dev, 0);
 
 #if 1
@@ -414,7 +419,7 @@
 
 
 static int
-wd_close_card(struct device *dev)
+wd_close(struct device *dev)
 {
 	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
 
@@ -479,12 +484,15 @@
 		}
 		if (register_netdev(dev) != 0) {
 			printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0) return 0;	/* Got at least one. */
+			if (found != 0) {	/* Got at least one. */
+				lock_8390_module();
+				return 0;
+			}
 			return -ENXIO;
 		}
 		found++;
 	}
-
+	lock_8390_module();
 	return 0;
 }
 
@@ -496,12 +504,14 @@
 	for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
 		struct device *dev = &dev_wd[this_dev];
 		if (dev->priv != NULL) {
+			void *priv = dev->priv;
 			int ioaddr = dev->base_addr - WD_NIC_OFFSET;
-			unregister_netdev(dev);
-			kfree(dev->priv);
-			dev->priv = NULL;
 			free_irq(dev->irq, dev);
 			release_region(ioaddr, WD_IO_EXTENT);
+			dev->priv = NULL;
+			unregister_netdev(dev);
+			kfree(priv);
+			unlock_8390_module();
 		}
 	}
 }
diff -u --recursive --new-file v2.1.104/linux/drivers/scsi/NCR5380.c linux/drivers/scsi/NCR5380.c
--- v2.1.104/linux/drivers/scsi/NCR5380.c	Thu May 14 19:47:40 1998
+++ linux/drivers/scsi/NCR5380.c	Sun Jun  7 10:37:41 1998
@@ -31,6 +31,18 @@
 
 /*
  * $Log: NCR5380.c,v $
+
+ * Revision 1.9  1997/7/27	Ronald van Cuijlenborg
+ *				(ronald.van.cuijlenborg@tip.nl or nutty@dds.nl)
+ * (hopefully) fixed and enhanced USLEEP
+ * added support for DTC3181E card (for Mustek scanner)
+ *
+
+ * Revision 1.8			Ingmar Baumgart
+ *				(ingmar@gonzo.schwaben.de)
+ * added support for NCR53C400a card
+ *
+
  * Revision 1.7  1996/3/2       Ray Van Tassle (rayvt@comm.mot.com)
  * added proc_info
  * added support needed for DTC 3180/3280
@@ -84,7 +96,6 @@
 
 #ifndef notyet
 #undef LINKED
-#undef USLEEP
 #undef REAL_DMA
 #endif
 
@@ -239,9 +250,6 @@
  *      bus, we will go to sleep so that the CPU can get real work done 
  *      when we run a command that won't complete immediately.
  *
- * Note that if USLEEP is defined, NCR5380_TIMER *must* also be
- * defined.
- *
  * Defaults for these will be provided if USLEEP is defined, although
  * the user may want to adjust these to allocate CPU resources to 
  * the SCSI driver or "real" code.
@@ -309,6 +317,10 @@
 static struct Scsi_Host *first_instance = NULL;
 static Scsi_Host_Template *the_template = NULL;
 
+#ifdef USLEEP
+struct timer_list usleep_timer;
+#endif
+
 /*
  * Function : void initialize_SCp(Scsi_Cmnd *cmd)
  *
@@ -560,9 +572,6 @@
 }
 
 #ifdef USLEEP
-#ifndef NCR5380_TIMER
-#error "NCR5380_TIMER must be defined so that this type of NCR5380 driver gets a unique timer."
-#endif
 
 /*
  * These need tweaking, and would probably work best as per-device 
@@ -582,6 +591,10 @@
 #ifndef USLEEP_POLL
 #define USLEEP_POLL (200*HZ/1000)
 #endif
+#ifndef USLEEP_WAITLONG
+/* RvC: (reasonable time to wait on select error) */
+#define USLEEP_WAITLONG USLEEP_SLEEP
+#endif
 
 static struct Scsi_Host *expires_first = NULL;
 
@@ -638,48 +651,48 @@
 
 	save_flags(flags);
 	cli();
-	if (((struct NCR5380_hostdata *) (instance->host_data))->next_timer) {
+	if (((struct NCR5380_hostdata *) (instance->hostdata))->next_timer) {
 		restore_flags(flags);
 		return -1;
 	}
 	for (prev = &expires_first, tmp = expires_first; tmp;
-	     prev = &(((struct NCR5380_hostdata *) tmp->host_data)->next_timer),
-	  tmp = ((struct NCR5380_hostdata *) tmp->host_data)->next_timer)
-		if (instance->time_expires < tmp->time_expires)
+		prev = &(((struct NCR5380_hostdata *) tmp->hostdata)->next_timer), 
+		tmp = ((struct NCR5380_hostdata *) tmp->hostdata)->next_timer)
+		if (((struct NCR5380_hostdata *)instance->hostdata)->time_expires < 
+			((struct NCR5380_hostdata *)tmp->hostdata)->time_expires) 
 			break;
 
-	instance->next_timer = tmp;
+	((struct NCR5380_hostdata *) instance->hostdata)->next_timer = tmp;
 	*prev = instance;
-	timer_table[NCR5380_TIMER].expires = expires_first->time_expires;
-	timer_active |= 1 << NCR5380_TIMER;
+   
+	del_timer(&usleep_timer);
+	usleep_timer.expires = ((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires;
+	add_timer(&usleep_timer);
 	restore_flags(flags);
 	return 0;
 }
 
 /* Doing something about unwanted reentrancy here might be useful */
-void NCR5380_timer_fn(void)
+void NCR5380_timer_fn(unsigned long surplus_to_requirements)
 {
 	unsigned long flags;
 	struct Scsi_Host *instance;
 	save_flags(flags);
 	cli();
-	for (; expires_first && expires_first->time_expires >= jiffies;) {
-		instance = ((NCR5380_hostdata *) expires_first->host_data)->
-		    expires_next;
-		((NCR5380_hostdata *) expires_first->host_data)->expires_next =
-		    NULL;
-		((NCR5380_hostdata *) expires_first->host_data)->time_expires =
-		    0;
+	for (; expires_first &&
+		((struct NCR5380_hostdata *)expires_first->hostdata)->time_expires <= jiffies; ) 
+	{
+		instance = ((struct NCR5380_hostdata *) expires_first->hostdata)->next_timer;
+		((struct NCR5380_hostdata *) expires_first->hostdata)->next_timer = NULL;
+		((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires = 0;
 		expires_first = instance;
 	}
 
-	if (expires_first) {
-		timer_table[NCR5380_TIMER].expires = ((NCR5380_hostdata *)
-				 expires_first->host_data)->time_expires;
-		timer_active |= (1 << NCR5380_TIMER);
-	} else {
-		timer_table[NCR5380_TIMER].expires = 0;
-		timer_active &= ~(1 << MCR5380_TIMER);
+	del_timer(&usleep_timer);
+	if (expires_first) 
+	{
+		usleep_timer.expires = ((struct NCR5380_hostdata *)expires_first->hostdata)->time_expires;
+		add_timer(&usleep_timer);
 	}
 	restore_flags(flags);
 
@@ -696,8 +709,8 @@
 #endif
 		done = 1;
 #ifdef USLEEP
-		timer_table[NCR5380_TIMER].expires = 0;
-		timer_table[NCR5380_TIMER].fn = NCR5380_timer_fn;
+		init_timer(&usleep_timer);
+		usleep_timer.function = NCR5380_timer_fn;
 #endif
 	}
 }
@@ -1269,7 +1282,11 @@
 			 hostdata = (struct NCR5380_hostdata *) instance->hostdata;
 			 save_flags(flags);
 			 cli();
+#ifdef USLEEP
+			if (!hostdata->connected && !hostdata->selecting) {
+#else
 			if (!hostdata->connected) {
+#endif			
 #if (NDEBUG & NDEBUG_MAIN)
 				printk("scsi%d : not connected\n", instance->host_no);
 #endif
@@ -1326,6 +1343,10 @@
 						 * and see if we can do an information transfer,
 						 * with failures we will restart.
 						 */
+#ifdef USLEEP
+						hostdata->selecting = 0; /* RvC: have to preset this
+							to indicate a new command is being performed */
+#endif
 
 						if (!NCR5380_select(instance, tmp,
 						/* 
@@ -1355,12 +1376,42 @@
 					}	/* if target/lun is not busy */
 				}	/* for */
 			}	/* if (!hostdata->connected) */
+#ifdef USLEEP
+			if (hostdata->selecting) 
+			{
+				tmp = (Scsi_Cmnd *)hostdata->selecting;
+				if (!NCR5380_select(instance, tmp, 
+					(tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : TAG_NEXT)) 
+				{
+					/* Ok ?? */
+				}
+				else
+				{
+					unsigned long flags;
+					/* RvC: device failed, so we wait a long time
+					this is needed for Mustek scanners, that
+					do not respond to commands immediately
+					after a scan */
+					printk(KERN_DEBUG "scsi%d: device %d did not respond in time\n",
+						instance->host_no, tmp->target);
+					save_flags(flags);
+					cli();
+					LIST(tmp, hostdata->issue_queue);
+					tmp->host_scribble = (unsigned char *) hostdata->issue_queue;
+					hostdata->issue_queue = tmp;
+					restore_flags(flags);
+
+					hostdata->time_expires = jiffies + USLEEP_WAITLONG;
+					NCR5380_set_timer (instance);
+				}
+			} /* if hostdata->selecting */
+#endif
 			if (hostdata->connected
 #ifdef REAL_DMA
 			    && !hostdata->dmalen
 #endif
 #ifdef USLEEP
-			    && (!hostdata->time_expires || hostdata->time_expires >= jiffies)
+			    && (!hostdata->time_expires || hostdata->time_expires <= jiffies)
 #endif
 			    ) {
 				restore_flags(flags);
@@ -1377,6 +1428,7 @@
 				break;
 		}		/* for instance */
 	} while (!done);
+	cli();
 	main_running = 0;
 }
 
@@ -1567,13 +1619,21 @@
 	NCR5380_local_declare();
 	struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
 	unsigned char tmp[3], phase;
-	unsigned char *data;
+	unsigned char *data, value;
 	int len;
 	unsigned long timeout;
 	unsigned long flags;
 
 	 NCR5380_setup(instance);
 
+#ifdef USLEEP
+	if (hostdata->selecting) 
+	{
+		goto part2;	/* RvC: sorry prof. Dijkstra, but it keeps the
+				   rest of the code nearly the same */
+	}
+#endif
+
 	 hostdata->restart_select = 0;
 #if defined (NDEBUG) && (NDEBUG & NDEBUG_ARBITRATION)
 	 NCR5380_print(instance);
@@ -1646,7 +1706,13 @@
 	}
 	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL);
 
-	if (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) {
+	if (!(hostdata->flags & FLAG_DTC3181E) &&
+		/* RvC: DTC3181E has some trouble with this
+		 *	so we simply removed it. Seems to work with
+		 *	only Mustek scanner attached
+		 */
+		(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) 
+	{
 		NCR5380_write(MODE_REG, MR_BASE);
 		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
 #if (NDEBUG & NDEBUG_ARBITRATION)
@@ -1736,9 +1802,32 @@
 	 * and it's detecting as true.  Sigh.
 	 */
 
+#ifdef USLEEP
+	hostdata->select_time = 0; /* we count the clock ticks at which we polled */
+	hostdata->selecting = cmd;
+
+part2:
+    	/* RvC: here we enter after a sleeping period, or immediately after
+		execution of part 1
+		we poll only once ech clock tick */
+	value = NCR5380_read(STATUS_REG) & (SR_BSY | SR_IO);
+
+	if (!value && (hostdata->select_time < 25)) 
+	{
+		/* RvC: we still must wait for a device response */
+		hostdata->select_time++; /* after 25 ticks the device has failed */
+		hostdata->time_expires = jiffies + 1;
+		NCR5380_set_timer(instance);
+		return 0;	/* RvC: we return here with hostdata->selecting set,
+				   to go to sleep */
+	}
+
+	hostdata->selecting = 0; /* clear this pointer, because we passed the
+				waiting period */
+#else
 	while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) &
 					(SR_BSY | SR_IO)));
-
+#endif
 	if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) ==
 	    (SR_SEL | SR_IO)) {
 		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
@@ -1764,7 +1853,7 @@
 			printk("scsi%d : weirdness\n", instance->host_no);
 			if (hostdata->restart_select)
 				printk("\trestart select\n");
-#ifdef NDEBUG
+#if (NDEBUG & NDEBUG_SELECTION)
 			NCR5380_print(instance);
 #endif
 			NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
@@ -1896,7 +1985,14 @@
 	register unsigned char p = *phase, tmp;
 	register int c = *count;
 	register unsigned char *d = *data;
-	 NCR5380_setup(instance);
+#ifdef USLEEP
+	/*
+	 *	RvC: some administrative data to process polling time
+	 */
+	int break_allowed = 0;
+	struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+#endif
+	NCR5380_setup(instance);
 
 #if (NDEBUG & NDEBUG_PIO)
 	if (!(p & SR_IO))
@@ -1913,12 +2009,41 @@
 
 	 NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
 
+#ifdef USLEEP
+	/* RvC: don't know if this is necessary, but other SCSI I/O is short
+	 *	so breaks are not necessary there
+	 */
+	if ((p == PHASE_DATAIN) || (p == PHASE_DATAOUT)) 
+	{
+		break_allowed = 1;
+	}
+#endif
+
+
 	do {
 		/* 
 		 * Wait for assertion of REQ, after which the phase bits will be 
 		 * valid 
 		 */
-		while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ));
+
+#ifdef USLEEP
+		/* RvC: we simply poll once, after that we stop temporarily
+		 *	and let the device buffer fill up
+		 *	if breaking is not allowed, we keep polling as long as needed
+		 */
+
+		while ( !((tmp = NCR5380_read(STATUS_REG)) & SR_REQ) &&
+			!break_allowed );
+		if (!(tmp & SR_REQ)) 
+		{
+			/* timeout condition */
+			hostdata->time_expires = jiffies + USLEEP_SLEEP;
+			NCR5380_set_timer (instance);
+			break;
+		}
+#else
+		while ( !((tmp = NCR5380_read(STATUS_REG)) & SR_REQ) ); 
+#endif
 
 #if (NDEBUG & NDEBUG_HANDSHAKE)
 		printk("scsi%d : REQ detected\n", instance->host_no);
@@ -2468,7 +2593,12 @@
 	unsigned char *data;
 	unsigned char phase, tmp, extended_msg[10], old_phase = 0xff;
 	Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
-	 NCR5380_setup(instance);
+#ifdef USLEEP
+	/* RvC: we need to set the end of the polling time */
+	unsigned long poll_time = jiffies + USLEEP_POLL;
+#endif
+
+	NCR5380_setup(instance);
 
 	while (1) {
 		tmp = NCR5380_read(STATUS_REG);
@@ -2899,7 +3029,9 @@
 				NCR5380_transfer_pio(instance, &phase, &len,
 						     &data);
 #ifdef USLEEP
-				if (!disconnect && should_disconnect(cmd->cmnd[0])) {
+				if (!cmd->device->disconnect &&
+					should_disconnect(cmd->cmnd[0])) 
+				{
 					hostdata->time_expires = jiffies + USLEEP_SLEEP;
 #if (NDEBUG & NDEBUG_USLEEP)
 					printk("scsi%d : issued command, sleeping until %ul\n", instance->host_no,
@@ -2924,9 +3056,12 @@
 			}	/* switch(phase) */
 		}		/* if (tmp * SR_REQ) */
 #ifdef USLEEP
-		else {
-			if (!disconnect && hostdata->time_expires && jiffies >
-			    hostdata->time_expires) {
+		else 
+		{
+			/* RvC: go to sleep if polling time expired
+			 */
+			if (!cmd->device->disconnect && jiffies >= poll_time) 
+			{
 				hostdata->time_expires = jiffies + USLEEP_SLEEP;
 #if (NDEBUG & NDEBUG_USLEEP)
 				printk("scsi%d : poll timed out, sleeping until %ul\n", instance->host_no,
diff -u --recursive --new-file v2.1.104/linux/drivers/scsi/NCR5380.h linux/drivers/scsi/NCR5380.h
--- v2.1.104/linux/drivers/scsi/NCR5380.h	Tue Apr 14 14:29:21 1998
+++ linux/drivers/scsi/NCR5380.h	Sun Jun  7 10:37:41 1998
@@ -235,11 +235,13 @@
 #define DMA_NONE	255
 #define IRQ_AUTO	254
 #define DMA_AUTO	254
+#define PORT_AUTO	0xffff		/* autoprobe io port for 53c400a */
 
 #define FLAG_HAS_LAST_BYTE_SENT		1	/* NCR53c81 or better */
 #define FLAG_CHECK_LAST_BYTE_SENT	2	/* Only test once */
 #define FLAG_NCR53C400			4	/* NCR53c400 */
 #define FLAG_NO_PSEUDO_DMA		8	/* Inhibit DMA */
+#define FLAG_DTC3181E			16	/* DTC3181E */
 
 #ifndef ASM
 struct NCR5380_hostdata {
@@ -264,6 +266,8 @@
 #ifdef USLEEP
     unsigned long time_expires;			/* in jiffies, set prior to sleeping */
     struct Scsi_Host *next_timer;
+    int select_time;				/* timer in select for target response */
+    volatile Scsi_Cmnd *selecting;
 #endif
 #ifdef NCR5380_STATS
     unsigned timebase;				/* Base for time calcs */
diff -u --recursive --new-file v2.1.104/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c
--- v2.1.104/linux/drivers/scsi/aha1542.c	Sun Jun  7 11:16:34 1998
+++ linux/drivers/scsi/aha1542.c	Sun Jun  7 10:37:41 1998
@@ -223,7 +223,7 @@
 
 /* Similar to aha1542_in, except that we wait a very short period of time.
    We use this if we know the board is alive and awake, but we are not sure
-   if the board will respond to the command we are about to send or not */
+   if the board will respond the command we are about to send or not */
 static int aha1542_in1(unsigned int base, unchar *cmdp, int len)
 {
     unsigned long flags;
@@ -300,7 +300,6 @@
 
 static int aha1542_test_port(int bse, struct Scsi_Host * shpnt)
 {
-    int i;
     unchar inquiry_cmd[] = {CMD_INQUIRY };
     unchar inquiry_result[4];
     unchar *cmdp;
diff -u --recursive --new-file v2.1.104/linux/drivers/scsi/g_NCR5380.c linux/drivers/scsi/g_NCR5380.c
--- v2.1.104/linux/drivers/scsi/g_NCR5380.c	Sun Jun  7 11:16:34 1998
+++ linux/drivers/scsi/g_NCR5380.c	Sun Jun  7 10:37:41 1998
@@ -10,6 +10,12 @@
  * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
  *    K.Lentin@cs.monash.edu.au
  *
+ * NCR53C400A extensions (c) 1996, Ingmar Baumgart
+ *    ingmar@gonzo.schwaben.de
+ *
+ * DTC3181E extensions (c) 1997, Ronald van Cuijlenborg
+ * ronald.van.cuijlenborg@tip.nl or nutty@dds.nl
+ *
  * ALPHA RELEASE 1. 
  *
  * For more information, please consult 
@@ -73,6 +79,12 @@
  * $Log: generic_NCR5380.c,v $
  */
 
+/* settings for DTC3181E card with only Mustek scanner attached */
+#define USLEEP
+#define USLEEP_POLL	1
+#define USLEEP_SLEEP	20
+#define USLEEP_WAITLONG	500
+
 #define AUTOPROBE_IRQ
 #define AUTOSENSE
 
@@ -105,6 +117,7 @@
 #include "sd.h"
 #include <linux/stat.h>
 #include <linux/init.h>
+#include<linux/ioport.h>
 
 struct proc_dir_entry proc_scsi_g_ncr5380 = {
     PROC_SCSI_GENERIC_NCR5380, 9, "g_NCR5380",
@@ -117,6 +130,8 @@
 static int ncr_addr=NCR_NOT_SET;
 static int ncr_5380=NCR_NOT_SET;
 static int ncr_53c400=NCR_NOT_SET;
+static int ncr_53c400a=NCR_NOT_SET;
+static int dtc_3181e=NCR_NOT_SET;
 
 static struct override {
 	NCR5380_implementation_fields;
@@ -156,6 +171,16 @@
 	    printk("generic_NCR53C400_setup : usage ncr53c400=" STRVAL(NCR5380_map_name) ",irq\n");
 	    return;
 	}
+    case BOARD_NCR53C400A:
+	if (ints[0] != 2) {
+	    printk("generic_NCR53C400A_setup : usage ncr53c400a=" STRVAL(NCR5380_map_name) ",irq\n");
+	    return;
+	}
+    case BOARD_DTC3181E:
+	if (ints[0] != 2) {
+	    printk("generic_DTC3181E_setup : usage dtc3181e=" STRVAL(NCR5380_map_name) ",irq\n");
+	    return;
+	}
     }
 
     if (commandline_current < NO_OVERRIDES) {
@@ -196,6 +221,32 @@
     internal_setup (BOARD_NCR53C400, str, ints);
 }
 
+/*
+ * Function : generic_NCR53C400A_setup (char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ * 
+ * Inputs : str - unused, ints - array of integer parameters with ints[0] 
+ * 	equal to the number of ints.
+ */
+
+void generic_NCR53C400A_setup (char *str, int *ints) {
+    internal_setup (BOARD_NCR53C400A, str, ints);
+}
+
+/*
+ * Function : generic_DTC3181E_setup (char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ * 
+ * Inputs : str - unused, ints - array of integer parameters with ints[0] 
+ * 	equal to the number of ints.
+ */
+
+void generic_DTC3181E_setup (char *str, int *ints) {
+    internal_setup (BOARD_DTC3181E, str, ints);
+}
+
 /* 
  * Function : int generic_NCR5380_detect(Scsi_Host_Template * tpnt)
  *
@@ -210,7 +261,12 @@
 
 __initfunc(int generic_NCR5380_detect(Scsi_Host_Template * tpnt)) {
     static int current_override = 0;
-    int count;
+    int count, i;
+    u_int *ports;
+    u_int ncr_53c400a_ports[] = {0x280, 0x290, 0x300, 0x310, 0x330,
+    		     		0x340, 0x348, 0x350, 0};
+    u_int dtc_3181e_ports[] = {0x220, 0x240, 0x280, 0x2a0, 0x2c0,
+    			     0x300, 0x320, 0x340, 0};
     int flags = 0;
     struct Scsi_Host *instance;
 
@@ -224,6 +280,10 @@
 	overrides[0].board=BOARD_NCR5380;
     else if (ncr_53c400 != NCR_NOT_SET)
 	overrides[0].board=BOARD_NCR53C400;
+    else if (ncr_53c400a != NCR_NOT_SET)
+        overrides[0].board=BOARD_NCR53C400A;
+    else if (dtc_3181e != NCR_NOT_SET)
+        overrides[0].board=BOARD_DTC3181E;
 
     tpnt->proc_dir = &proc_scsi_g_ncr5380;
 
@@ -231,6 +291,7 @@
 	if (!(overrides[current_override].NCR5380_map_name))
 	    continue;
 
+	ports = 0;
 	switch (overrides[current_override].board) {
 	case BOARD_NCR5380:
 	    flags = FLAG_NO_PSEUDO_DMA;
@@ -238,8 +299,56 @@
 	case BOARD_NCR53C400:
 	    flags = FLAG_NCR53C400;
 	    break;
+	case BOARD_NCR53C400A:
+	    flags = FLAG_NO_PSEUDO_DMA;
+            ports = ncr_53c400a_ports;
+	    break;
+	case BOARD_DTC3181E:
+	    flags = FLAG_NO_PSEUDO_DMA | FLAG_DTC3181E;
+	    ports = dtc_3181e_ports;
+	    break;
 	}
 
+	if (ports) {
+	    /* wakeup sequence for the NCR53C400A and DTC3181E*/
+
+	    /* Disable the adapter and look for a free io port */
+	    outb(0x59, 0x779);
+	    outb(0xb9, 0x379);
+	    outb(0xc5, 0x379);
+	    outb(0xae, 0x379);
+	    outb(0xa6, 0x379);
+	    outb(0x00, 0x379);
+
+	    if (overrides[current_override].NCR5380_map_name != PORT_AUTO)
+	        for(i=0; ports[i]; i++) {
+	            if (overrides[current_override].NCR5380_map_name == ports[i])
+	                break;
+	        }
+	    else
+	        for(i=0; ports[i]; i++) {
+	            if ((!check_region(ports[i], 16)) && (inb(ports[i]) == 0xff))
+	                break;
+		}
+	    if (ports[i]) {
+	        outb(0x59, 0x779);
+	        outb(0xb9, 0x379);
+	        outb(0xc5, 0x379);
+	        outb(0xae, 0x379);
+	        outb(0xa6, 0x379);
+	        outb(0x80 | i, 0x379);          /* set io port to be used */
+        	outb(0xc0, ports[i] + 9);
+	        if (inb(ports[i] + 9) != 0x80)
+	            continue;
+	        else
+	            overrides[current_override].NCR5380_map_name=ports[i];
+	    } else
+	        continue;
+	}
+
+	request_region(overrides[current_override].NCR5380_map_name,
+					NCR5380_region_size, "ncr5380");
+
 	instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
 	instance->NCR5380_instance_name = overrides[current_override].NCR5380_map_name;
 
@@ -289,6 +398,8 @@
 
     NCR5380_setup(instance);
 
+    release_region(instance->NCR5380_instance_name, NCR5380_region_size);
+
     if (instance->irq != IRQ_NONE)
 	free_irq(instance->irq, NULL);
 
@@ -742,5 +853,7 @@
 MODULE_PARM(ncr_addr, "i");
 MODULE_PARM(ncr_5380, "i");
 MODULE_PARM(ncr_53c400, "i");
+MODULE_PARM(ncr_53c400a, "i");
+MODULE_PARM(dtc_3181e, "i");
 
 #endif
diff -u --recursive --new-file v2.1.104/linux/drivers/scsi/g_NCR5380.h linux/drivers/scsi/g_NCR5380.h
--- v2.1.104/linux/drivers/scsi/g_NCR5380.h	Tue Apr 14 14:29:22 1998
+++ linux/drivers/scsi/g_NCR5380.h	Sun Jun  7 10:37:41 1998
@@ -165,6 +165,8 @@
 
 #define BOARD_NCR5380	0
 #define BOARD_NCR53C400	1
+#define BOARD_NCR53C400A 2
+#define BOARD_DTC3181E	3
 
 #endif /* else def HOSTS_C */
 #endif /* ndef ASM */
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.1.104/linux/drivers/sound/Config.in	Sun Jun  7 11:16:35 1998
+++ linux/drivers/sound/Config.in	Sun Jun  7 10:37:41 1998
@@ -9,179 +9,184 @@
 
 # Prompt user for primary drivers.
 
-dep_tristate 'ProAudioSpectrum 16 support' CONFIG_SOUND_PAS $CONFIG_SOUND
-if [ "$CONFIG_SOUND_PAS" = "y" ]; then
-    int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' CONFIG_PAS_IRQ 10
-    int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' CONFIG_PAS_DMA 3
-    bool 'Enable PAS16 joystick port' CONFIG_PAS_JOYSTICK
-fi
-
-dep_tristate '100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SOUND_SB $CONFIG_SOUND
-if [ "$CONFIG_SOUND_SB" = "y" ]; then
-    hex 'I/O base for SB Check from manual of the card' CONFIG_SB_BASE 220
-    int 'Sound Blaster IRQ Check from manual of the card' CONFIG_SB_IRQ 7
-    int 'Sound Blaster DMA 0, 1 or 3' CONFIG_SB_DMA 1
-    int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' CONFIG_SB_DMA2 5
-    hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' CONFIG_SB_MPU_BASE 330
-    comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.'
-    comment 'Enter -1 to the following question if you have something else such as SB16/32.'
-    int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' CONFIG_SB_MPU_IRQ -1
-fi
-
-dep_tristate 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_SOUND_ADLIB $CONFIG_SOUND
-
-dep_tristate 'Gravis Ultrasound support' CONFIG_SOUND_GUS $CONFIG_SOUND
-if [ "$CONFIG_SOUND_GUS" = "y" -o "$CONFIG_SOUND_GUS" = "m" ]; then
-    bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16 
-    bool 'GUS MAX support' CONFIG_GUSMAX
-fi
-if [ "$CONFIG_SOUND_GUS" = "y" ]; then
-    hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' CONFIG_GUS_BASE 220
-    int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' CONFIG_GUS_IRQ 15
-    int 'GUS DMA 1, 3, 5, 6 or 7' CONFIG_GUS_DMA 6
-    int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' CONFIG_GUS_DMA2 -1
-    if [ "$CONFIG_GUS16" = "y" ]; then
-	hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' CONFIG_GUS16_BASE 530
-	int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' CONFIG_GUS16_IRQ 7
-	int 'GUS DMA 0, 1 or 3' CONFIG_GUS16_DMA 3
-    fi
-fi
-
-dep_tristate 'MPU-401 support (NOT for SB16)' CONFIG_SOUND_MPU401 $CONFIG_SOUND
-if [ "$CONFIG_SOUND_MPU401" = "y" ]; then
-    hex 'I/O base for MPU401 Check from manual of the card' CONFIG_MPU_BASE 330
-    int 'MPU401 IRQ Check from manual of the card' CONFIG_MPU_IRQ 9
-fi
-
-dep_tristate 'PSS (AD1848, ADSP-2115, ESC614) support' CONFIG_SOUND_PSS $CONFIG_SOUND
-if [ "$CONFIG_SOUND_PSS" = "y" ]; then
-    hex 'PSS I/O base 220 or 240' CONFIG_PSS_BASE 220
-    hex 'PSS audio I/O base 530, 604, E80 or F40' CONFIG_PSS_MSS_BASE 530
-    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
-    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
-    fi
-fi
-if [ "$CONFIG_SOUND_PSS" = "y" -o "$CONFIG_SOUND_PSS" = "m" ]; then
-    bool '  Enable PSS mixer (Beethoven ADSP-16 and other compatibile)' CONFIG_PSS_MIXER
-fi
-
-dep_tristate 'Microsoft Sound System support' CONFIG_SOUND_MSS $CONFIG_SOUND
-if [ "$CONFIG_SOUND_MSS" = "y" ]; then
-    hex 'MSS/WSS I/O base 530, 604, E80 or F40' CONFIG_MSS_BASE 530
-    int 'MSS/WSS IRQ 7, 9, 10 or 11' CONFIG_MSS_IRQ 11
-    int 'MSS/WSS DMA 0, 1 or 3' CONFIG_MSS_DMA 3
-    int 'MSS/WSS second DMA (if possible) 0, 1 or 3' CONFIG_MSS_DMA2 -1
-fi
-
-dep_tristate 'Ensoniq SoundScape support' CONFIG_SOUND_SSCAPE $CONFIG_SOUND
-if [ "$CONFIG_SOUND_SSCAPE" = "y" ]; then
-    hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' CONFIG_SSCAPE_BASE 330
-    int 'SoundScape MIDI IRQ ' CONFIG_SSCAPE_IRQ 9
-    int 'SoundScape initialization DMA 0, 1 or 3' CONFIG_SSCAPE_DMA 3
-    hex 'SoundScape audio I/O base 534, 608, E84 or F44' CONFIG_SSCAPE_MSS_BASE 534
-    int 'SoundScape audio IRQ 7, 9, 10 or 11' CONFIG_SSCAPE_MSS_IRQ 11
-fi
-
-dep_tristate 'MediaTrix AudioTrix Pro support' CONFIG_SOUND_TRIX $CONFIG_SOUND
-if [ "$CONFIG_SOUND_TRIX" = "y" ]; then
-    hex 'OPL3-SA1 audio I/O base 530, 604, E80 or F40' CONFIG_TRIX_BASE 530
-    int 'OPL3-SA1 audio IRQ 7, 9, 10 or 11' CONFIG_TRIX_IRQ 11
-    int 'OPL3-SA1 audio DMA 0, 1 or 3' CONFIG_TRIX_DMA 0
-    int 'OPL3-SA1 second (duplex) DMA 0, 1 or 3' CONFIG_TRIX_DMA2 3
-    hex 'OPL3-SA1 MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_TRIX_MPU_BASE 330
-    int 'OPL3-SA1 MIDI IRQ 3, 4, 5, 7 or 9' CONFIG_TRIX_MPU_IRQ 9
-    hex 'OPL3-SA1 SB I/O base 220, 210, 230, 240, 250, 260 or 270' CONFIG_TRIX_SB_BASE 220
-    int 'OPL3-SA1 SB IRQ 3, 4, 5 or 7' CONFIG_TRIX_SB_IRQ 7
-    int 'OPL3-SA1 SB DMA 1 or 3' CONFIG_TRIX_SB_DMA 1
-    bool '  Have TRXPRO.HEX firmware file' CONFIG_TRIX_HAVE_BOOT
-    if [ "$CONFIG_TRIX_HAVE_BOOT" = "y" ]; then
-	string '  Full pathname of TRXPRO.HEX firmware file' CONFIG_TRIX_BOOT_FILE
-    fi
-fi
-
-dep_tristate 'Support for OPTi MAD16 and/or Mozart based cards' CONFIG_SOUND_MAD16 $CONFIG_SOUND
-dep_tristate 'Support MIDI in older MAD16 based cards (requires SB)' CONFIG_MAD16_OLDCARD $CONFIG_SOUND_MAD16
-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
-    int 'MAD16 audio DMA 0, 1 or 3' CONFIG_MAD16_DMA 3
-    int 'MAD16 second (duplex) DMA 0, 1 or 3' CONFIG_MAD16_DMA2 0
-    hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' CONFIG_MAD16_MPU_BASE 330
-    int 'MAD16 MIDI IRQ 5, 7, 9 or 10' CONFIG_MAD16_MPU_IRQ 9
-fi
-
-dep_tristate 'Support for Crystal CS4232 based (PnP) cards' CONFIG_SOUND_CS4232 $CONFIG_SOUND
-if [ "$CONFIG_SOUND_CS4232" = "y" ]; then
-    hex 'CS4232 audio I/O base 530, 604, E80 or F40' CONFIG_CS4232_BASE 530
-    int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CONFIG_CS4232_IRQ 11
-    int 'CS4232 audio DMA 0, 1 or 3' CONFIG_CS4232_DMA 0
-    int 'CS4232 second (duplex) DMA 0, 1 or 3' CONFIG_CS4232_DMA2 3
-    hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_CS4232_MPU_BASE 330
-    int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CONFIG_CS4232_MPU_IRQ 9
-fi
-
-dep_tristate 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_SOUND_MAUI $CONFIG_SOUND
-if [ "$CONFIG_SOUND_MAUI" = "y" ]; then
-    hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' CONFIG_MAUI_BASE 330
-    int 'Maui IRQ 5, 9, 12 or 15' CONFIG_MAUI_IRQ 9
-    bool '  Have OSWF.MOT firmware file' CONFIG_MAUI_HAVE_BOOT
-    if [ "$CONFIG_MAUI_HAVE_BOOT" = "y" ]; then
-	string '  Full pathname of OSWF.MOT firmware file' CONFIG_MAUI_BOOT_FILE
-    fi
-fi
+dep_tristate 'OSS sound modules' CONFIG_SOUND_OSS $CONFIG_SOUND
 
-dep_tristate 'Support for Aztech Sound Galaxy (non-PnP) cards' CONFIG_SGALAXY $CONFIG_SOUND
-if [ "$CONFIG_SGALAXY" = "y" ]; then
-    hex 'SGALAXY audio I/O base 530, 604, E80 or F40' CONFIG_SGALAXY_BASE 530
-    int 'SGALAXY audio IRQ 5, 7, 9, 11, 12 or 15' CONFIG_SGALAXY_IRQ 11
-    int 'SGALAXY audio DMA 0, 1 or 3' CONFIG_SGALAXY_DMA 0
-    int 'SGALAXY second (duplex) DMA 0, 1 or 3' CONFIG_SGALAXY_DMA2 3
-    hex 'SGALAXY SB I/O base 220 or 240' CONFIG_SGALAXY_SGBASE 220
+if [ "$CONFIG_SOUND_OSS" != "n" ]; 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
+      int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' CONFIG_PAS_DMA 3
+      bool 'Enable PAS16 joystick port' CONFIG_PAS_JOYSTICK
+  fi
+  
+  dep_tristate '100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SOUND_SB $CONFIG_SOUND_OSS
+  if [ "$CONFIG_SOUND_SB" = "y" ]; then
+      hex 'I/O base for SB Check from manual of the card' CONFIG_SB_BASE 220
+      int 'Sound Blaster IRQ Check from manual of the card' CONFIG_SB_IRQ 7
+      int 'Sound Blaster DMA 0, 1 or 3' CONFIG_SB_DMA 1
+      int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' CONFIG_SB_DMA2 5
+      hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' CONFIG_SB_MPU_BASE 330
+      comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.'
+      comment 'Enter -1 to the following question if you have something else such as SB16/32.'
+      int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' CONFIG_SB_MPU_IRQ -1
+  fi
+  
+  dep_tristate 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_SOUND_ADLIB $CONFIG_SOUND_OSS
+  
+  dep_tristate 'Gravis Ultrasound support' CONFIG_SOUND_GUS $CONFIG_SOUND_OSS
+  if [ "$CONFIG_SOUND_GUS" = "y" -o "$CONFIG_SOUND_GUS" = "m" ]; then
+      bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16 
+      bool 'GUS MAX support' CONFIG_GUSMAX
+  fi
+  if [ "$CONFIG_SOUND_GUS" = "y" ]; then
+      hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' CONFIG_GUS_BASE 220
+      int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' CONFIG_GUS_IRQ 15
+      int 'GUS DMA 1, 3, 5, 6 or 7' CONFIG_GUS_DMA 6
+      int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' CONFIG_GUS_DMA2 -1
+      if [ "$CONFIG_GUS16" = "y" ]; then
+  	hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' CONFIG_GUS16_BASE 530
+  	int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' CONFIG_GUS16_IRQ 7
+  	int 'GUS DMA 0, 1 or 3' CONFIG_GUS16_DMA 3
+      fi
+  fi
+  
+  dep_tristate 'MPU-401 support (NOT for SB16)' CONFIG_SOUND_MPU401 $CONFIG_SOUND_OSS
+  if [ "$CONFIG_SOUND_MPU401" = "y" ]; then
+      hex 'I/O base for MPU401 Check from manual of the card' CONFIG_MPU_BASE 330
+      int 'MPU401 IRQ Check from manual of the card' CONFIG_MPU_IRQ 9
+  fi
+  
+  dep_tristate 'PSS (AD1848, ADSP-2115, ESC614) support' CONFIG_SOUND_PSS $CONFIG_SOUND_OSS
+  if [ "$CONFIG_SOUND_PSS" = "y" ]; then
+      hex 'PSS I/O base 220 or 240' CONFIG_PSS_BASE 220
+      hex 'PSS audio I/O base 530, 604, E80 or F40' CONFIG_PSS_MSS_BASE 530
+      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
+      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
+      fi
+  fi
+  if [ "$CONFIG_SOUND_PSS" = "y" -o "$CONFIG_SOUND_PSS" = "m" ]; then
+      bool '  Enable PSS mixer (Beethoven ADSP-16 and other compatibile)' CONFIG_PSS_MIXER
+  fi
+  
+  dep_tristate 'Microsoft Sound System support' CONFIG_SOUND_MSS $CONFIG_SOUND_OSS
+  if [ "$CONFIG_SOUND_MSS" = "y" ]; then
+      hex 'MSS/WSS I/O base 530, 604, E80 or F40' CONFIG_MSS_BASE 530
+      int 'MSS/WSS IRQ 7, 9, 10 or 11' CONFIG_MSS_IRQ 11
+      int 'MSS/WSS DMA 0, 1 or 3' CONFIG_MSS_DMA 3
+      int 'MSS/WSS second DMA (if possible) 0, 1 or 3' CONFIG_MSS_DMA2 -1
+  fi
+  
+  dep_tristate 'Ensoniq SoundScape support' CONFIG_SOUND_SSCAPE $CONFIG_SOUND_OSS
+  if [ "$CONFIG_SOUND_SSCAPE" = "y" ]; then
+      hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' CONFIG_SSCAPE_BASE 330
+      int 'SoundScape MIDI IRQ ' CONFIG_SSCAPE_IRQ 9
+      int 'SoundScape initialization DMA 0, 1 or 3' CONFIG_SSCAPE_DMA 3
+      hex 'SoundScape audio I/O base 534, 608, E84 or F44' CONFIG_SSCAPE_MSS_BASE 534
+      int 'SoundScape audio IRQ 7, 9, 10 or 11' CONFIG_SSCAPE_MSS_IRQ 11
+  fi
+  
+  dep_tristate 'MediaTrix AudioTrix Pro support' CONFIG_SOUND_TRIX $CONFIG_SOUND_OSS
+  if [ "$CONFIG_SOUND_TRIX" = "y" ]; then
+      hex 'TRIX audio I/O base 530, 604, E80 or F40' CONFIG_TRIX_BASE 530
+      int 'TRIX audio IRQ 7, 9, 10 or 11' CONFIG_TRIX_IRQ 11
+      int 'TRIX audio DMA 0, 1 or 3' CONFIG_TRIX_DMA 0
+      int 'TRIX second (duplex) DMA 0, 1 or 3' CONFIG_TRIX_DMA2 3
+      hex 'TRIX MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_TRIX_MPU_BASE 330
+      int 'TRIX MIDI IRQ 3, 4, 5, 7 or 9' CONFIG_TRIX_MPU_IRQ 9
+      hex 'TRIX SB I/O base 220, 210, 230, 240, 250, 260 or 270' CONFIG_TRIX_SB_BASE 220
+      int 'TRIX SB IRQ 3, 4, 5 or 7' CONFIG_TRIX_SB_IRQ 7
+      int 'TRIX SB DMA 1 or 3' CONFIG_TRIX_SB_DMA 1
+      bool '  Have TRXPRO.HEX firmware file' CONFIG_TRIX_HAVE_BOOT
+      if [ "$CONFIG_TRIX_HAVE_BOOT" = "y" ]; then
+  	string '  Full pathname of TRXPRO.HEX firmware file' CONFIG_TRIX_BOOT_FILE
+      fi
+  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" ]; 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
+      int 'MAD16 audio DMA 0, 1 or 3' CONFIG_MAD16_DMA 3
+      int 'MAD16 second (duplex) DMA 0, 1 or 3' CONFIG_MAD16_DMA2 0
+      hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' CONFIG_MAD16_MPU_BASE 330
+      int 'MAD16 MIDI IRQ 5, 7, 9 or 10' CONFIG_MAD16_MPU_IRQ 9
+  fi
+  
+  dep_tristate 'Support for Crystal CS4232 based (PnP) cards' CONFIG_SOUND_CS4232 $CONFIG_SOUND_OSS
+  if [ "$CONFIG_SOUND_CS4232" = "y" ]; then
+      hex 'CS4232 audio I/O base 530, 604, E80 or F40' CONFIG_CS4232_BASE 530
+      int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CONFIG_CS4232_IRQ 11
+      int 'CS4232 audio DMA 0, 1 or 3' CONFIG_CS4232_DMA 0
+      int 'CS4232 second (duplex) DMA 0, 1 or 3' CONFIG_CS4232_DMA2 3
+      hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_CS4232_MPU_BASE 330
+      int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CONFIG_CS4232_MPU_IRQ 9
+  fi
+  
+  dep_tristate 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_SOUND_MAUI $CONFIG_SOUND_OSS
+  if [ "$CONFIG_SOUND_MAUI" = "y" ]; then
+      hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' CONFIG_MAUI_BASE 330
+      int 'Maui IRQ 5, 9, 12 or 15' CONFIG_MAUI_IRQ 9
+      bool '  Have OSWF.MOT firmware file' CONFIG_MAUI_HAVE_BOOT
+      if [ "$CONFIG_MAUI_HAVE_BOOT" = "y" ]; then
+  	string '  Full pathname of OSWF.MOT firmware file' CONFIG_MAUI_BOOT_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
+      hex 'SGALAXY audio I/O base 530, 604, E80 or F40' CONFIG_SGALAXY_BASE 530
+      int 'SGALAXY audio IRQ 5, 7, 9, 11, 12 or 15' CONFIG_SGALAXY_IRQ 11
+      int 'SGALAXY audio DMA 0, 1 or 3' CONFIG_SGALAXY_DMA 0
+      int 'SGALAXY second (duplex) DMA 0, 1 or 3' CONFIG_SGALAXY_DMA2 3
+      hex 'SGALAXY SB I/O base 220 or 240' CONFIG_SGALAXY_SGBASE 220
+  fi
+  
+  dep_tristate 'Yamaha OPL3-SA1 audio controller' CONFIG_SOUND_OPL3SA1 $CONFIG_SOUND_OSS
+  if [ "$CONFIG_SOUND_OPL3SA1" = "y" ]; then
+      hex 'OPL3-SA1 audio I/O base 530, 604, E80 or F40' CONFIG_OPL3SA1_BASE 530
+      int 'OPL3-SA1 audio IRQ 7, 9, 10 or 11' CONFIG_OPL3SA1_IRQ 11
+      int 'OPL3-SA1 audio DMA 0, 1 or 3' CONFIG_OPL3SA1_DMA 0
+      int 'OPL3-SA1 second (duplex) DMA 0, 1 or 3' CONFIG_OPL3SA1_DMA2 3
+      hex 'OPL3-SA1 MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_OPL3SA1_MPU_BASE 330
+      int 'OPL3-SA1 MIDI IRQ 3, 4, 5, 7 or 9' CONFIG_OPL3SA1_MPU_IRQ 9
+  fi
+  
+  dep_tristate 'SoftOSS software wave table engine' CONFIG_SOUND_SOFTOSS $CONFIG_SOUND_OSS
+  if [ "$CONFIG_SOUND_SOFTOSS" = "y" ]; then
+      int 'Sampling rate for SoftOSS 8000 to 48000' CONFIG_SOFTOSS_RATE 22050
+      int 'Max # of concurrent voices for SoftOSS 4 to 32' CONFIG_SOFTOSS_VOICES 32
+  fi
+  
+  dep_tristate 'FM synthesizer (YM3812/OPL-3) support' CONFIG_SOUND_YM3812 $CONFIG_SOUND_OSS
+  
+  dep_tristate 'Loopback MIDI device support' CONFIG_SOUND_VMIDI $CONFIG_SOUND_OSS
+  
+  dep_tristate '6850 UART support' CONFIG_SOUND_UART6850 $CONFIG_SOUND_OSS
+  if [ "$CONFIG_UART6850" = "y" ]; then
+      hex 'I/O base for UART 6850 MIDI port (Unknown)' CONFIG_U6850_BASE 0
+      int 'UART6850 IRQ (Unknown)' CONFIG_U6850_IRQ -1
+  fi
+  
+  if [ "$CONFIG_ARM" = "y" ]; then
+      bool 'VIDC 16-bit sound' CONFIG_VIDC_SOUND
+  fi
+  
+  
+  
+  # Additional low level drivers.
+  
+  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
+      source drivers/sound/lowlevel/Config.in
+  fi
+  endmenu
 fi
 
-dep_tristate 'Yamaha OPL3-SA1 audio controller' CONFIG_SOUND_OPL3SA1 $CONFIG_SOUND
-if [ "$CONFIG_SOUND_OPL3SA1" = "y" ]; then
-    hex 'OPL3-SA1 audio I/O base 530, 604, E80 or F40' CONFIG_OPL3SA1_BASE 530
-    int 'OPL3-SA1 audio IRQ 7, 9, 10 or 11' CONFIG_OPL3SA1_IRQ 11
-    int 'OPL3-SA1 audio DMA 0, 1 or 3' CONFIG_OPL3SA1_DMA 0
-    int 'OPL3-SA1 second (duplex) DMA 0, 1 or 3' CONFIG_OPL3SA1_DMA2 3
-    hex 'OPL3-SA1 MIDI I/O base 330, 370, 3B0 or 3F0' CONFIG_OPL3SA1_MPU_BASE 330
-    int 'OPL3-SA1 MIDI IRQ 3, 4, 5, 7 or 9' CONFIG_OPL3SA1_MPU_IRQ 9
-fi
-
-dep_tristate 'SoftOSS software wave table engine' CONFIG_SOUND_SOFTOSS $CONFIG_SOUND
-if [ "$CONFIG_SOUND_SOFTOSS" = "y" ]; then
-    int 'Sampling rate for SoftOSS 8000 to 48000' CONFIG_SOFTOSS_RATE 22050
-    int 'Max # of concurrent voices for SoftOSS 4 to 32' CONFIG_SOFTOSS_VOICES 32
-fi
-
-dep_tristate 'FM synthesizer (YM3812/OPL-3) support' CONFIG_SOUND_YM3812 $CONFIG_SOUND
-
-dep_tristate 'Loopback MIDI device support' CONFIG_SOUND_VMIDI $CONFIG_SOUND
-
-dep_tristate '6850 UART support' CONFIG_SOUND_UART6850 $CONFIG_SOUND
-if [ "$CONFIG_UART6850" = "y" ]; then
-    hex 'I/O base for UART 6850 MIDI port (Unknown)' CONFIG_U6850_BASE 0
-    int 'UART6850 IRQ (Unknown)' CONFIG_U6850_IRQ -1
-fi
-
-if [ "$CONFIG_ARM" = "y" ]; then
-    bool 'VIDC 16-bit sound' CONFIG_VIDC_SOUND
-fi
-
-
-
-# Additional low level drivers.
-
-mainmenu_option next_comment
-comment 'Additional low level sound drivers'
-bool 'Additional low level sound drivers' CONFIG_LOWLEVEL_SOUND $CONFIG_SOUND
-if [ "$CONFIG_LOWLEVEL_SOUND" != "n" ]; then
-    source drivers/sound/lowlevel/Config.in
-fi
-endmenu
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v2.1.104/linux/drivers/sound/Makefile	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/Makefile	Sun Jun  7 10:37:41 1998
@@ -22,8 +22,9 @@
 # All of the (potential) objects that export symbols.
 # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
 
-export-objs	:= ad1848.o mpu401.o opl3.o sb_card.o uart401.o		\
-		   audio_syms.o midi_syms.o sequencer_syms.o sound_syms.o
+export-objs	:=  ad1848.o audio_syms.o midi_syms.o mpu401.o \
+		    opl3.o sb_card.o sequencer_syms.o sound_core.o \
+		    sound_firmware.o sound_syms.o uart401.o
 
 
 
@@ -44,7 +45,8 @@
 
 else
 
-obj-$(CONFIG_SOUND)		+= sound.o
+obj-$(CONFIG_SOUND)		+= soundcore.o
+obj-$(CONFIG_SOUND_OSS)		+= sound.o
 obj-$(CONFIG_SOUND_ADLIB)	+= adlib_card.o opl3.o
 obj-$(CONFIG_SOUND_CS4232)	+= cs4232.o ad1848.o uart401.o
 obj-$(CONFIG_SOUND_GUS)		+= gus.o ad1848.o
@@ -71,14 +73,16 @@
 
 # Declare multi-part drivers.
 
-list-multi	:= sound.o gus.o pas2.o sb.o softoss2.o vidc_mod.o
+list-multi	:= sound.o gus.o pas2.o sb.o softoss2.o vidc_mod.o soundcore.o
 
 sound-objs	:= 							\
-    dev_table.o soundcard.o sound_firmware.o sound_syms.o		\
+    dev_table.o soundcard.o sound_syms.o		\
     audio.o audio_syms.o dmabuf.o					\
     midi_syms.o midi_synth.o midibuf.o					\
     sequencer.o sequencer_syms.o sound_timer.o sys_timer.o
 
+soundcore-objs	:= sound_core.o sound_firmware.o
+
 gus-objs	:= gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o
 pas2-objs	:= pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o
 sb-objs		:= sb_audio.o sb_card.o sb_common.o sb_midi.o sb_mixer.o
@@ -126,7 +130,8 @@
 # Translate to Rules.make lists.
 
 L_TARGET	:= sound.a
-MOD_LIST_NAME	:= SOUND_MODULES
+# This is a nice idea but needs depmod altering
+#MOD_LIST_NAME	:= SOUND_MODULES
 
 L_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-y)))
 LX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-y)))
@@ -147,6 +152,9 @@
 
 sound.o: $(sound-objs)
 	$(LD) -r -o $@ $(sound-objs)
+
+soundcore.o: $(soundcore-objs)
+	$(LD) -r -o $@ $(soundcore-objs)
 
 gus.o: $(gus-objs)
 	$(LD) -r -o $@ $(gus-objs)
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/dev_table.c linux/drivers/sound/dev_table.c
--- v2.1.104/linux/drivers/sound/dev_table.c	Wed Apr  8 19:36:27 1998
+++ linux/drivers/sound/dev_table.c	Sun Jun  7 10:37:41 1998
@@ -515,39 +515,32 @@
 void sound_unload_audiodev(int dev)
 {
 	if (dev != -1)
+	{
 		audio_devs[dev] = NULL;
+		unregister_sound_dsp((dev<<4)+3);
+	}
 }
 
 int sound_alloc_audiodev(void)
-{
-	int i;
-
-	for (i = 0; i < MAX_AUDIO_DEV; i++)
-	{
-		if (audio_devs[i] == NULL)
-		{
-			if (i >= num_audiodevs)
-				num_audiodevs = i + 1;
-			return i;
-		}
-	}
-	return -1;
+{ 
+	int i = register_sound_dsp(&oss_sound_fops);
+	if(i==-1)
+		return i;
+	i>>=4;
+	if(i>=num_audiodevs)
+		num_audiodevs = i + 1;
+	return i;
 }
 
 int sound_alloc_mididev(void)
 {
-	int i;
-
-	for (i = 0; i < MAX_MIDI_DEV; i++)
-	{
-		if (midi_devs[i] == NULL)
-		{
-			if (i >= num_midis)
-				num_midis++;
-			return i;
-		}
-	}
-	return -1;
+	int i = register_sound_midi(&oss_sound_fops);
+	if(i==-1)
+		return i;
+	i>>=4;
+	if(i>=num_midis)
+		num_midis = i + 1;
+	return i;
 }
 
 int sound_alloc_synthdev(void)
@@ -568,18 +561,13 @@
 
 int sound_alloc_mixerdev(void)
 {
-	int i;
-
-	for (i = 0; i < MAX_MIXER_DEV; i++)
-	{
-		if (mixer_devs[i] == NULL)
-		{
-			if (i >= num_mixers)
-				num_mixers++;
-			return i;
-		}
-	}
-	return -1;
+	int i = register_sound_mixer(&oss_sound_fops);
+	if(i==-1)
+		return -1;
+	i>>=4;
+	if(i>=num_mixers)
+		num_mixers = i + 1;
+	return i;
 }
 
 int sound_alloc_timerdev(void)
@@ -601,14 +589,20 @@
 void sound_unload_mixerdev(int dev)
 {
 	if (dev != -1)
+	{
 		mixer_devs[dev] = NULL;
+		unregister_sound_mixer(dev<<4);
+	}
 }
 
 void sound_unload_mididev(int dev)
 {
 #ifdef CONFIG_MIDI
 	if (dev != -1)
+	{
 		midi_devs[dev] = NULL;
+		unregister_sound_midi((dev<<4)+2);
+	}
 #endif
 }
 
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/lowlevel/Config.in linux/drivers/sound/lowlevel/Config.in
--- v2.1.104/linux/drivers/sound/lowlevel/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/lowlevel/Config.in	Sun Jun  7 10:37:41 1998
@@ -0,0 +1,56 @@
+dep_tristate 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER $CONFIG_SOUND_OSS
+
+dep_tristate 'AWE32 synth' CONFIG_AWE32_SYNTH $CONFIG_SOUND_OSS
+
+if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND" = "m" ]; then
+    dep_tristate 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16 $CONFIG_SOUND_OSS
+    if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then
+	hex '  I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220
+    fi
+
+    if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then
+	comment 'SC-6600 Audio Cards have no jumper switches at all'
+	bool 'SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600
+	if [ "$CONFIG_SC6600" = "y" ]; then
+	    comment 'SC-6600 specific configuration'
+	    bool 'Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY
+	    int 'SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' CONFIG_SC6600_CDROM 4
+	    hex 'SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0
+	fi
+
+	if [ "$CONFIG_SOUND_SB" = "y" -o "$CONFIG_SOUND_SB" = "m" ]; then
+	    if [ "$CONFIG_AEDSP16_MSS" != "y" ]; then
+		bool 'Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO
+		if [ "$CONFIG_AEDSP16_SBPRO" = "y" ]; then
+		    comment 'Audio Excel DSP 16 [Sound Blaster Pro]'
+		    hex 'I/O base for Audio Excel DSP 16 220, 240' CONFIG_AEDSP16_BASE $CONFIG_SB_BASE
+		    int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' CONFIG_AEDSP16_SB_IRQ $CONFIG_SB_IRQ
+		    int 'Audio Excel DSP 16 DMA 0, 1 or 3' CONFIG_AEDSP16_SB_DMA $CONFIG_SB_DMA
+		fi
+	    fi
+	fi
+
+	if [ "$CONFIG_SOUND_MSS" = "y" -o "$CONFIG_SOUND_MSS" = "m" ]; then
+	    if [ "$CONFIG_AEDSP16_SBPRO" != "y" ]; then
+		bool 'Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS
+		if [ "$CONFIG_AEDSP16_MSS" = "y" ]; then
+		    comment 'Audio Excel DSP 16 [Microsoft Sound System]'
+		    hex 'I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220
+		    int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' CONFIG_AEDSP16_MSS_IRQ $CONFIG_MSS_IRQ
+		    int 'Audio Excel DSP 16 DMA 0, 1 or 3' CONFIG_AEDSP16_MSS_DMA $CONFIG_MSS_DMA
+		fi
+	    fi
+	fi
+
+	if [ "$CONFIG_SOUND_MPU401" = "y" -o "$CONFIG_SOUND_MPU401" = "m" ]; then
+	    bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
+	    if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then
+		comment 'Audio Excel DSP 16 [MPU-401]'
+		if [ "$CONFIG_AEDSP16_SBPRO" != "y" -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
+		    hex 'I/O base for Audio Excel DSP 16 220 or 240' CONFIG_AEDSP16_BASE 220
+		fi
+		int 'MPU401 IRQ for Audio Excel DSP 16 5, 7, 9, 10 or 0 (disable)' CONFIG_AEDSP16_MPU_IRQ $CONFIG_MPU_IRQ
+	    fi
+	fi
+    fi
+fi
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/lowlevel/Makefile linux/drivers/sound/lowlevel/Makefile
--- v2.1.104/linux/drivers/sound/lowlevel/Makefile	Mon Jan  5 01:41:01 1998
+++ linux/drivers/sound/lowlevel/Makefile	Sun Jun  7 10:37:41 1998
@@ -1,67 +1,24 @@
-all:	lowlevel.o
-
-ALLOBJS = init.o aci.o awe_wave.o aedsp16.o
-OBJS = init.o
-
-ifeq ($(CONFIG_LOWLEVEL_SOUND),y)
-ifeq ($(CONFIG_ACI_MIXER),y)
-   OBJS := $(OBJS) aci.o
-endif
-ifeq ($(CONFIG_AWE32_SYNTH),y)
-OBJS := $(OBJS) awe_wave.o
-else 
-  ifeq ($(CONFIG_AWE32_SYNTH),m)
-     MX_OBJS := $(MX_OBJS) awe_wave.o
-  endif
-endif
-ifeq ($(CONFIG_AEDSP16),y)
-   OBJS := $(OBJS) aedsp16.o
-else 
-  ifeq ($(CONFIG_AEDSP16),m)
-     MX_OBJS := $(MX_OBJS) aedsp16.o
-  endif
-endif
-endif
-
-ifndef TOPDIR
-TOPDIR=/usr/src/linux
-endif
-
-lowlevel.o:	$(OBJS)
-	$(LD) -r -o lowlevel.o $(OBJS)
-
-module: manual_config.h
-	rm -f lowlevel.o
-	make CFLAGS="$(CFLAGS) -DLOWLEVEL_MODULE" $(ALLOBJS)
-	$(LD) -r -o lowlevel.o $(ALLOBJS)
-	touch module
-
-manual_config.h:
-	@echo You should create `pwd`/manual_config.h.
-	@echo See `pwd`/README for more info.
-	@exit 1
+# Makefile for the Linux low-level sound card drivers.
+#
+# 11 Feb 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
+# Rewritten to use lists instead of if statements.
 
-clean:
-	rm -f core x y z *~ *.o module .depend
+export-objs			:= soundlow.o
 
-dep:
-	$(CPP) -M $(CFLAGS) -I. *.c > .depend
+list-y				:=
+list-m				:=
+list-n				:=
+list-				:=
+
+obj-$(CONFIG_SOUND_OSS)		+= soundlow.o
+obj-$(CONFIG_ACI_MIXER)		+= aci.o
+obj-$(CONFIG_AEDSP16)		+= aedsp16.o
+obj-$(CONFIG_AWE32_SYNTH)	+= awe_wave.o
+
+O_TARGET	:= lowlevel.o
+O_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-y)))
+OX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-y)))
+M_OBJS		:= $(sort $(filter-out $(export-objs), $(obj-m)))
+MX_OBJS		:= $(sort $(filter     $(export-objs), $(obj-m)))
 
-ifndef HOSTCC
-#
-#	Running outside the kernel build.
-#
-CC	= gcc
-HOSTCC	= gcc
-CFLAGS	= -O2 -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -pipe -m486
-USE_DEPEND=y
-else
 include $(TOPDIR)/Rules.make
-endif
-
-ifdef USE_DEPEND
-# include a dependency file if one exists
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
-endif
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/lowlevel/init.c linux/drivers/sound/lowlevel/init.c
--- v2.1.104/linux/drivers/sound/lowlevel/init.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/lowlevel/init.c	Wed Dec 31 16:00:00 1969
@@ -1,65 +0,0 @@
-/*
- * lowlevel/init.c - Calls initialization code for configured drivers.
- */
-
-#include "lowlevel.h"
-#include <linux/config.h>
-#include <linux/module.h>
-#include "../soundvers.h"
-
-#ifdef LOWLEVEL_MODULE
-char *lowlevel_version = SOUND_VERSION_STRING;
-#endif
-
-extern int attach_aci(void);
-extern void unload_aci(void);
-extern int attach_awe(void);
-extern void unload_awe(void);
-extern int init_aedsp16(void);
-extern void uninit_aedsp16(void);
-
-/*
- * There are two places where you can insert initialization calls of
- * low level drivers. sound_init_lowlevel_drivers() is called after
- * the sound driver has been initialized (the normal case)
- * while sound_preinit_lowlevel_drivers() is called before that.
- */
-void
-sound_preinit_lowlevel_drivers(void)
-{
-#if defined(CONFIG_AEDSP16) && !defined(MODULE)
-   init_aedsp16();
-#endif
-}
-
-void
-sound_init_lowlevel_drivers(void)
-{
-#ifdef CONFIG_ACI_MIXER
-   attach_aci();
-#endif
-
-#ifdef CONFIG_AWE32_SYNTH
-   attach_awe();
-#endif
-}
-
-void
-sound_unload_lowlevel_drivers(void)
-{
-#ifdef CONFIG_ACI_MIXER
-   unload_aci();
-#endif
-
-#ifdef CONFIG_AWE32_SYNTH
-   unload_awe();
-#endif
-
-#ifdef CONFIG_AEDSP16
-   uninit_aedsp16();
-#endif
-
-}
-
-EXPORT_SYMBOL(sound_init_lowlevel_drivers);
-EXPORT_SYMBOL(sound_unload_lowlevel_drivers);
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/lowlevel/soundlow.c linux/drivers/sound/lowlevel/soundlow.c
--- v2.1.104/linux/drivers/sound/lowlevel/soundlow.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/lowlevel/soundlow.c	Sun Jun  7 10:37:41 1998
@@ -0,0 +1,66 @@
+/*
+ * lowlevel/init.c - Calls initialization code for configured drivers.
+ */
+
+#include "lowlevel.h"
+#include <linux/config.h>
+#include <linux/module.h>
+#include "../soundvers.h"
+
+#ifdef LOWLEVEL_MODULE
+char *lowlevel_version = SOUND_VERSION_STRING;
+#endif
+
+extern int attach_aci(void);
+extern void unload_aci(void);
+extern int attach_awe(void);
+extern void unload_awe(void);
+extern int init_aedsp16(void);
+extern void uninit_aedsp16(void);
+
+/*
+ * There are two places where you can insert initialization calls of
+ * low level drivers. sound_init_lowlevel_drivers() is called after
+ * the sound driver has been initialized (the normal case)
+ * while sound_preinit_lowlevel_drivers() is called before that.
+ */
+void
+sound_preinit_lowlevel_drivers(void)
+{
+#if defined(CONFIG_AEDSP16) && !defined(MODULE)
+   init_aedsp16();
+#endif
+}
+
+void
+sound_init_lowlevel_drivers(void)
+{
+#ifdef CONFIG_ACI_MIXER
+   attach_aci();
+#endif
+
+#ifdef CONFIG_AWE32_SYNTH
+   attach_awe();
+#endif
+}
+
+void
+sound_unload_lowlevel_drivers(void)
+{
+#ifdef CONFIG_ACI_MIXER
+   unload_aci();
+#endif
+
+#ifdef CONFIG_AWE32_SYNTH
+   unload_awe();
+#endif
+
+#ifdef CONFIG_AEDSP16
+   uninit_aedsp16();
+#endif
+
+}
+
+EXPORT_SYMBOL(sound_init_lowlevel_drivers);
+EXPORT_SYMBOL(sound_unload_lowlevel_drivers);
+EXPORT_SYMBOL(sound_preinit_lowlevel_drivers);
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/mad16.c linux/drivers/sound/mad16.c
--- v2.1.104/linux/drivers/sound/mad16.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/mad16.c	Sun Jun  7 10:37:41 1998
@@ -405,13 +405,17 @@
 	/* MC2 is CD configuration. Don't touch it. */
 
 	mad_write(MC3_PORT, 0);	/* Disable SB mode IRQ and DMA */
+
+	/* bit 2 of MC4 reverses it's meaning between the C930
+	   and the C931. */
+	cfg = c931_detected ? 0x04 : 0x00;
 #ifdef MAD16_CDSEL
 	if(MAD16_CDSEL & 0x20)
-		mad_write(MC4_PORT, 0x62);	/* opl4 */
+		mad_write(MC4_PORT, 0x62|cfg);	/* opl4 */
 	else
-		mad_write(MC4_PORT, 0x52);	/* opl3 */
+		mad_write(MC4_PORT, 0x52|cfg);	/* opl3 */
 #else
-	mad_write(MC4_PORT, 0x52);
+	mad_write(MC4_PORT, 0x52|cfg);
 #endif
 	mad_write(MC5_PORT, 0x3C);	/* Init it into mode2 */
 	mad_write(MC6_PORT, 0x02);	/* Enable WSS, Disable MPU and SB */
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/os.h linux/drivers/sound/os.h
--- v2.1.104/linux/drivers/sound/os.h	Wed Apr  8 19:36:27 1998
+++ linux/drivers/sound/os.h	Sun Jun  7 10:37:41 1998
@@ -57,3 +57,4 @@
 #undef PSEUDO_DMA_AUTOINIT
 #define ALLOW_BUFFER_MAPPING
 
+extern struct file_operations oss_sound_fops;
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.1.104/linux/drivers/sound/sb_common.c	Sun Jun  7 11:16:35 1998
+++ linux/drivers/sound/sb_common.c	Sun Jun  7 10:37:41 1998
@@ -699,7 +699,7 @@
 	sb_devc *devc;
 	char name[100];
 	extern int sb_be_quiet;
-	int	mixer3c, mixer4c;
+	int	mixer22, mixer30;
 	
 /*
  * Check if we had detected a SB device earlier
@@ -723,12 +723,7 @@
 	/*
 	 * Now continue initialization of the device
 	 */
-	devc->dev = sound_alloc_audiodev();
-	if (devc->dev == -1)
-	{
-		printk(KERN_WARNING "sb: too many audio devices.\n");
-		return;
-	}
+
 	devc->caps = hw_config->driver_use_1;
 
 	if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) && hw_config->irq > 0)
@@ -736,7 +731,6 @@
 		if (request_irq(hw_config->irq, sbintr, 0, "soundblaster", devc) < 0)
 		{
 			printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq);
-			sound_unload_audiodev(devc->dev);
 			return;
 		}
 		devc->irq_ok = 0;
@@ -745,7 +739,6 @@
 			if (!sb16_set_irq_hw(devc, devc->irq))	/* Unsupported IRQ */
 			{
 				free_irq(devc->irq, devc);
-				sound_unload_audiodev(devc->dev);
 				return;
 			}
 		if ((devc->type == 0 || devc->type == MDL_ESS) &&
@@ -817,21 +810,35 @@
 
 		case 4:
 			devc->model = hw_config->card_subtype = MDL_SB16;
-			/*
-			 *	The ALS007 seems to return DSP version 4.2.  In addition it has 2
-			 *	output control registers (at 0x3c and 0x4c).  Both of these should
-			 *	be !=0 after a reset which forms the basis of the ALS007 test
-			 *	since a "standard" SoundBlaster does not have a register at 0x4c.
+			/* 
+			 * ALS007 and ALS100 return DSP version 4.2 and have 2 post-reset !=0
+			 * registers at 0x3c and 0x4c (output ctrl registers on ALS007) whereas
+			 * a "standard" SB16 doesn't have a register at 0x4c.  ALS100 actively
+			 * updates register 0x22 whenever 0x30 changes, as per the SB16 spec.
+			 * Since ALS007 doesn't, this can be used to differentiate the 2 cards.
 			 */
-			mixer3c = sb_getmixer(devc,0x3c);
-			mixer4c = sb_getmixer(devc,0x4c);
-			if ((devc->minor == 2) && (mixer3c != 0) && (mixer4c != 0)) 
-			{
-				sb_setmixer(devc,0x3c,0x1f);   /* Enable all inputs */
-				sb_setmixer(devc,0x4c,0x1f);
-				devc->submodel = SUBMDL_ALS007;
-				if (hw_config->name == NULL)
-					hw_config->name = "Sound Blaster (ALS-007)";
+			if ((devc->minor == 2) && sb_getmixer(devc,0x3c) && sb_getmixer(devc,0x4c)) 
+			{
+				mixer30 = sb_getmixer(devc,0x30);
+				sb_setmixer(devc,0x22,(mixer22=sb_getmixer(devc,0x22)) & 0x0f);
+				sb_setmixer(devc,0x30,0xff);
+				/* ALS100 will force 0x30 to 0xf8 like SB16; ALS007 will allow 0xff. */
+				/* Register 0x22 & 0xf0 on ALS100 == 0xf0; on ALS007 it == 0x10.     */
+				if ((sb_getmixer(devc,0x30) != 0xff) || ((sb_getmixer(devc,0x22) & 0xf0) != 0x10)) 
+				{
+					if (hw_config->name == NULL)
+						hw_config->name = "Sound Blaster 16 (ALS-100)";
+        			}
+        			else
+        			{
+        				sb_setmixer(devc,0x3c,0x1f);    /* Enable all inputs */
+					sb_setmixer(devc,0x4c,0x1f);
+					sb_setmixer(devc,0x22,mixer22); /* Restore 0x22 to original value */
+					devc->submodel = SUBMDL_ALS007;
+					if (hw_config->name == NULL)
+						hw_config->name = "Sound Blaster 16 (ALS-007)";
+				}
+				sb_setmixer(devc,0x30,mixer30);
 			}
 			else if (hw_config->name == NULL)
 				hw_config->name = "Sound Blaster 16";
@@ -904,6 +911,7 @@
 				printk(KERN_WARNING "soundblaster: Can't allocate 16 bit DMA channel %d\n", devc->dma16);
 		}
 		sb_audio_init(devc, name);
+		hw_config->slots[0]=devc->dev;
 	}
 	else
 	{
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/sb_midi.c linux/drivers/sound/sb_midi.c
--- v2.1.104/linux/drivers/sound/sb_midi.c	Wed Apr  8 19:36:27 1998
+++ linux/drivers/sound/sb_midi.c	Sun Jun  7 10:37:41 1998
@@ -179,7 +179,7 @@
 
 	if (dev == -1)
 	{
-		printk("Sound: Too many midi devices detected\n");
+		printk(KERN_ERR "sb_midi: Too many midi devices detected\n");
 		return;
 	}
 	std_midi_synth.midi_dev = dev;
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/sgalaxy.c linux/drivers/sound/sgalaxy.c
--- v2.1.104/linux/drivers/sound/sgalaxy.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/sgalaxy.c	Sun Jun  7 10:37:41 1998
@@ -121,7 +121,9 @@
 	
 	request_region( ai->ai_sgbase, 0x10, "SoundGalaxy SB" );
  
-	n=attach_ms_sound( ai );
+	attach_ms_sound( ai );
+	n=ai->slots[0];
+	
 	if (n!=-1 && audio_devs[n]->mixer_dev != -1 )
 	{
 		AD1848_REROUTE( SOUND_MIXER_LINE1, SOUND_MIXER_LINE );   /* Line-in */
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/sound_config.h linux/drivers/sound/sound_config.h
--- v2.1.104/linux/drivers/sound/sound_config.h	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/sound_config.h	Sun Jun  7 10:37:41 1998
@@ -16,6 +16,7 @@
 
 #include <linux/config.h>
 #include <linux/fs.h>
+#include <linux/sound.h>
 
 #include "legacy.h"
 #include "os.h"
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/sound_core.c linux/drivers/sound/sound_core.c
--- v2.1.104/linux/drivers/sound/sound_core.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/sound_core.c	Sun Jun  7 10:37:41 1998
@@ -0,0 +1,323 @@
+/*
+ *	Sound core handling. Breaks out sound functions to submodules
+ *	
+ *	Author:		Alan Cox <alan.cox@linux.org>
+ *
+ *	Fixes:
+ *
+ *
+ *	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.
+ *
+ *                         --------------------
+ * 
+ *	Top level handler for the sound subsystem. Various devices can
+ *	plug into this. The fact they dont all go via OSS doesn't mean 
+ *	they don't have to implement the OSS API. There is a lot of logic
+ *	to keeping much of the OSS weight out of the code in a compatibility
+ *	module, but its up to the driver to rember to load it...
+ *
+ *	The code provides a set of functions for registration of devices
+ *	by type. This is done rather than providing a single call so that
+ *	we can hide any future changes in the internals (eg when we go to
+ *	32bit dev_t) from the modules and their interface.
+ *
+ *	Secondly we need to allocate the dsp, dsp16 and audio devices as
+ *	one. Thus we misuse the chains a bit to simplify this.
+ *
+ *	Thirdly to make it more fun and for 2.3.x and above we do all
+ *	of this using fine grained locking.
+ *
+ *	FIXME: we have to resolve modules and fine grained load/unload
+ *	locking at some point in 2.3.x.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/fs.h>
+#include <linux/sound.h>
+#include <linux/major.h>
+ 
+ 
+struct sound_unit
+{
+	int unit_minor;
+	struct file_operations *unit_fops;
+	struct sound_unit *next;
+};
+
+/*
+ *	Low level list operator. Scan the ordered list, find a hole and
+ *	join into it. Called with the lock asserted
+ */
+
+static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list, struct file_operations *fops, int low, int top)
+{
+	int n=low;
+
+	while(n<top)
+	{
+		/* Found a hole ? */
+		if(*list==NULL || (*list)->unit_minor>n)
+			break;
+		list=&((*list)->next);
+		n+=16;
+	}
+	
+	if(n==top)
+	{
+		return -1;
+	}
+	
+		
+	/*
+	 *	Fill it in
+	 */
+	 
+	s->unit_minor=n;
+	s->unit_fops=fops;
+	
+	/*
+	 *	Link it
+	 */
+	 
+	s->next=*list;
+	*list=s;
+	
+	
+	MOD_INC_USE_COUNT;
+	return n;
+}
+
+/*
+ *	Remove a node from the chain. Called with the lock asserted
+ */
+ 
+static void __sound_remove_unit(struct sound_unit **list, int unit)
+{
+	while(*list)
+	{
+		struct sound_unit *p=*list;
+		if(p->unit_minor==unit)
+		{
+			*list=p->next;
+			kfree(p);
+			MOD_DEC_USE_COUNT;
+			return;
+		}
+		list=&(p->next);
+	}
+	printk(KERN_ERR "Sound device %d went missing!\n", unit);
+}
+
+/*
+ *	This lock guards the sound loader list.
+ */
+
+static spinlock_t sound_loader_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ *	Allocate the controlling structure and add it to the sound driver
+ *	list. Acquires locks as needed
+ */
+ 
+static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int low, int top)
+{
+	int r;
+	struct sound_unit *s=(struct sound_unit *)kmalloc(sizeof(struct sound_unit), GFP_KERNEL);
+	if(s==NULL)
+		return -1;
+		
+	spin_lock(&sound_loader_lock);
+	r=__sound_insert_unit(s,list,fops,low,top);
+	spin_unlock(&sound_loader_lock);
+	
+	if(r==-1)
+		kfree(s);
+	return r;
+}
+
+/*
+ *	Remove a unit. Acquires locks as needed. The drivers MUST have
+ *	completed the removal before their file operations become
+ *	invalid.
+ */
+ 	
+static void sound_remove_unit(struct sound_unit **list, int unit)
+{
+	spin_lock(&sound_loader_lock);
+	__sound_remove_unit(list, unit);
+	spin_unlock(&sound_loader_lock);
+}
+
+/*
+ *	Allocations
+ *
+ *	0	*16		Mixers
+ *	1	*8		Sequencers
+ *	2	*16		Midi
+ *	3	*16		DSP
+ *	4	*16		SunDSP
+ *	5	*16		DSP16
+ *	6	--		sndstat (obsolete)
+ *	7	*16		unused
+ *	8	--		alternate sequencer (see above)
+ *	9	*16		unused
+ *	10	*16		unused
+ *	11	*16		unused
+ *	12	*16		unused
+ *	13	*16		unused
+ *	14	*16		unused
+ *	15	*16		unused
+ */
+
+static struct sound_unit *chains[16];
+
+int register_sound_special(struct file_operations *fops, int unit)
+{
+	return sound_insert_unit(&chains[unit&15], fops, unit, unit+1);
+}
+ 
+EXPORT_SYMBOL(register_sound_special);
+
+int register_sound_mixer(struct file_operations *fops)
+{
+	return sound_insert_unit(&chains[0], fops, 0, 128);
+}
+
+EXPORT_SYMBOL(register_sound_mixer);
+
+int register_sound_midi(struct file_operations *fops)
+{
+	return sound_insert_unit(&chains[2], fops, 2, 130);
+}
+
+EXPORT_SYMBOL(register_sound_midi);
+
+/*
+ *	DSP's are registered as a triple. Register only one and cheat
+ *	in open - see below.
+ */
+ 
+int register_sound_dsp(struct file_operations *fops)
+{
+	return sound_insert_unit(&chains[3], fops, 3, 131);
+}
+
+EXPORT_SYMBOL(register_sound_dsp);
+
+void unregister_sound_special(int unit)
+{
+	sound_remove_unit(&chains[unit&15], unit);
+}
+ 
+EXPORT_SYMBOL(unregister_sound_special);
+
+void unregister_sound_mixer(int unit)
+{
+	sound_remove_unit(&chains[0], unit);
+}
+
+EXPORT_SYMBOL(unregister_sound_mixer);
+
+void unregister_sound_midi(int unit)
+{
+	return sound_remove_unit(&chains[2], unit);
+}
+
+EXPORT_SYMBOL(unregister_sound_midi);
+
+void unregister_sound_dsp(int unit)
+{
+	return sound_remove_unit(&chains[3], unit);
+}
+
+EXPORT_SYMBOL(unregister_sound_dsp);
+
+
+/*
+ *	Now our file operations
+ */
+
+static int soundcore_open(struct inode *, struct file *);
+
+static struct file_operations soundcore_fops=
+{
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	soundcore_open,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+int soundcore_open(struct inode *inode, struct file *file)
+{
+	int chain;
+	int unit=MINOR(inode->i_rdev);
+	struct sound_unit *s;
+
+	chain=unit&0x0F;
+	if(chain==4 || chain==5)	/* dsp/audio/dsp16 */
+	{
+		unit&=0xF0;
+		unit|=3;
+		chain=3;
+	}
+	
+	spin_lock(&sound_loader_lock);
+	
+	s=chains[chain];
+	
+	while(s && s->unit_minor <= unit)
+	{
+		if(s->unit_minor==unit)
+		{
+			file->f_op=s->unit_fops;
+			spin_unlock(&sound_loader_lock);
+			if(file->f_op->open)
+				return file->f_op->open(inode,file);
+			else
+				return 0;
+			break;
+		}
+		s=s->next;
+	}
+	spin_unlock(&sound_loader_lock);
+	return -ENODEV;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+	/* We have nothing to really do here - we know the lists must be
+	   empty */
+	unregister_chrdev(SOUND_MAJOR, "sound");
+}
+
+int init_module(void)
+#else
+int soundcore_init(void)
+#endif
+{
+	if(register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1)
+	{
+		printk(KERN_ERR "soundcore: sound device already in use.\n");
+		return -EBUSY;
+	}
+	return 0;
+}
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/sound_firmware.c linux/drivers/sound/sound_firmware.c
--- v2.1.104/linux/drivers/sound/sound_firmware.c	Wed Apr  8 19:36:27 1998
+++ linux/drivers/sound/sound_firmware.c	Sun Jun  7 10:37:41 1998
@@ -1,5 +1,6 @@
 #include "os.h"
 #define __KERNEL_SYSCALLS__
+#include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
@@ -57,3 +58,4 @@
 	return r;
 }
 
+EXPORT_SYMBOL(mod_firmware_load);
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/sound_syms.c linux/drivers/sound/sound_syms.c
--- v2.1.104/linux/drivers/sound/sound_syms.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/sound_syms.c	Sun Jun  7 10:37:41 1998
@@ -46,9 +46,6 @@
 EXPORT_SYMBOL(conf_printf);
 EXPORT_SYMBOL(conf_printf2);
 
-#include "sound_firmware.h"
-EXPORT_SYMBOL(mod_firmware_load);
-
 extern int softoss_dev;
 EXPORT_SYMBOL(softoss_dev);
 
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v2.1.104/linux/drivers/sound/soundcard.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/soundcard.c	Sun Jun  7 10:37:41 1998
@@ -42,8 +42,6 @@
 struct notifier_block *sound_locker=(struct notifier_block *)0;
 static int lock_depth = 0;
 
-#include <linux/major.h>
-
 #ifdef MODULE
 #define modular 1
 #else
@@ -58,8 +56,6 @@
 #endif
 
 static int      chrdev_registered = 0;
-static int      sound_major = SOUND_MAJOR;
-
 static int      is_unloading = 0;
 
 /*
@@ -93,17 +89,19 @@
 	int             i, n;
 
 	for (i = 0; i < num_mixer_volumes; i++)
+	{
 		if (strcmp(name, mixer_vols[i].name) == 0)
-		  {
-			  if (present)
-				  mixer_vols[i].num = i;
-			  return mixer_vols[i].levels;
-		  }
+		{
+			if (present)
+				mixer_vols[i].num = i;
+			return mixer_vols[i].levels;
+		}
+	}
 	if (num_mixer_volumes >= MAX_MIXER_DEV)
-	  {
-		  printk("Sound: Too many mixers (%s)\n", name);
-		  return levels;
-	  }
+	{
+		printk(KERN_ERR "Sound: Too many mixers (%s)\n", name);
+		return levels;
+	}
 	n = num_mixer_volumes++;
 
 	strcpy(mixer_vols[n].name, name);
@@ -176,7 +174,8 @@
 	}
 	len += sprintf(buffer + len, "\nCard config: \n");
 
-	for (i = 0; (i < num_sound_cards) && (pos <= offset + length); i++) {
+	for (i = 0; (i < num_sound_cards) && (pos <= offset + length); i++) 
+	{
 		if (!snd_installed_cards[i].card_type)
 			continue;
 		if (!snd_installed_cards[i].enabled)
@@ -438,7 +437,7 @@
 	}
 	DEB(printk("sound_open(dev=%d)\n", dev));
 	if ((dev >= SND_NDEVS) || (dev < 0)) {
-		printk(KERN_ERR "Invalid minor device %d\n", dev);
+		/* printk(KERN_ERR "Invalid minor device %d\n", dev);*/
 		return -ENXIO;
 	}
 	switch (dev & 0x0f) {
@@ -448,11 +447,11 @@
 	case SND_DEV_CTL:
 		dev >>= 4;
 #ifdef CONFIG_KMOD
-	if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) {
-		char modname[20];
-		sprintf(modname, "mixer%d", dev);
-		request_module(modname);
-	}
+		if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) {
+			char modname[20];
+			sprintf(modname, "mixer%d", dev);
+			request_module(modname);
+		}
 #endif
 		if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL))
 			return -ENXIO;
@@ -757,7 +756,7 @@
 	return 0;
 }
 
-static struct file_operations sound_fops =
+struct file_operations oss_sound_fops =
 {
 	sound_lseek,
 	sound_read,
@@ -770,6 +769,36 @@
 	sound_release
 };
 
+/*
+ *	Create the required special subdevices
+ */
+ 
+static int create_special_devices(void)
+{
+	int seq1,seq2;
+	int sndstat=register_sound_special(&oss_sound_fops, 6);
+	if(sndstat==-1)
+		goto bad1;
+	seq1=register_sound_special(&oss_sound_fops, 1);
+	if(seq1==-1)
+		goto bad2;
+	seq2=register_sound_special(&oss_sound_fops, 8);
+	if(seq2!=-1)
+		return 0;
+	unregister_sound_special(1);
+bad2:
+	unregister_sound_special(6);		
+bad1:	
+	return -1;
+}
+
+static void destroy_special_devices(void)
+{
+	unregister_sound_special(6);
+	unregister_sound_special(1);
+	unregister_sound_special(8);
+}
+
 #ifdef MODULE
 static void
 #else
@@ -784,7 +813,7 @@
 	}
 
 #ifndef MODULE
-	register_chrdev(sound_major, "sound", &sound_fops);
+	create_special_devices();
 	chrdev_registered = 1;
 #endif
 
@@ -831,7 +860,7 @@
 	if (i)
 		sound_setup("sound=", ints);
 
-	err = register_chrdev(sound_major, "sound", &sound_fops);
+	err = create_special_devices();
 	if (err)
 	{
 		printk(KERN_ERR "sound: driver already loaded/included in kernel\n");
@@ -859,7 +888,7 @@
         if (proc_unregister(&proc_root, PROC_SOUND))
 		printk(KERN_ERR "sound: unregistering /proc/sound failed\n");
 	if (chrdev_registered)
-		unregister_chrdev(sound_major, "sound");
+		destroy_special_devices();
 
 #ifdef CONFIG_SEQUENCER
 	sound_stop_timer();
diff -u --recursive --new-file v2.1.104/linux/drivers/sound/uart401.c linux/drivers/sound/uart401.c
--- v2.1.104/linux/drivers/sound/uart401.c	Thu May 14 19:47:42 1998
+++ linux/drivers/sound/uart401.c	Sun Jun  7 10:37:41 1998
@@ -432,7 +432,6 @@
 
 	if (!devc->share_irq)
 		free_irq(devc->irq, devc);
-	sound_unload_mididev(hw_config->slots[4]);
 	if (devc)
 	{
 		kfree(midi_devs[devc->my_dev]->converter);
@@ -440,6 +439,8 @@
 		kfree(devc);
 		devc = NULL;
 	}
+	/* This kills midi_devs[x] */
+	sound_unload_mididev(hw_config->slots[4]);
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.1.104/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.1.104/linux/fs/proc/array.c	Thu May  7 22:51:54 1998
+++ linux/fs/proc/array.c	Sun Jun  7 10:37:41 1998
@@ -960,7 +960,7 @@
 
 static int get_statm(int pid, char * buffer)
 {
-	struct task_struct *tsk = find_task_by_pid(pid);
+	struct task_struct *tsk;
 	int size=0, resident=0, share=0, trs=0, lrs=0, drs=0, dt=0;
 
 	read_lock(&tasklist_lock);
diff -u --recursive --new-file v2.1.104/linux/include/asm-alpha/apecs.h linux/include/asm-alpha/apecs.h
--- v2.1.104/linux/include/asm-alpha/apecs.h	Wed Apr  1 20:11:53 1998
+++ linux/include/asm-alpha/apecs.h	Sat Jun  6 18:52:04 1998
@@ -298,7 +298,7 @@
 {
 	unsigned int w;
 
-	asm ("insbl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	w = __kernel_insbl(b, addr & 3);
 	*(vuip) ((addr << 5) + APECS_IO + 0x00) = w;
 	mb();
 }
@@ -314,7 +314,7 @@
 {
 	unsigned int w;
 
-	asm ("inswl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	w = __kernel_inswl(b, addr & 3);
 	*(vuip) ((addr << 5) + APECS_IO + 0x08) = w;
 	mb();
 }
diff -u --recursive --new-file v2.1.104/linux/include/asm-alpha/byteorder.h linux/include/asm-alpha/byteorder.h
--- v2.1.104/linux/include/asm-alpha/byteorder.h	Fri Dec  5 05:45:09 1997
+++ linux/include/asm-alpha/byteorder.h	Sat Jun  6 18:52:04 1998
@@ -3,7 +3,11 @@
 
 #include <asm/types.h>
 
-#ifdef __GNUC__
+/* EGCS 1.1 can, without scheduling, do just as good as we do here
+   with the standard macros.  And since it can schedule, it does even
+   better in the end.  */
+
+#if defined(__GNUC__) && __GNUC_MINOR__ < 91
 
 static __inline__ __const__ __u32 ___arch__swab32(__u32 x)
 {
diff -u --recursive --new-file v2.1.104/linux/include/asm-alpha/cia.h linux/include/asm-alpha/cia.h
--- v2.1.104/linux/include/asm-alpha/cia.h	Wed Apr  1 20:11:53 1998
+++ linux/include/asm-alpha/cia.h	Sat Jun  6 18:52:04 1998
@@ -253,7 +253,7 @@
 {
 	unsigned int w;
 
-	asm ("insbl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	w = __kernel_insbl(b, addr & 3);
 	*(vuip) ((addr << 5) + CIA_IO + 0x00) = w;
 	mb();
 }
@@ -269,7 +269,7 @@
 {
 	unsigned int w;
 
-	asm ("inswl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	w = __kernel_inswl(b, addr & 3);
 	*(vuip) ((addr << 5) + CIA_IO + 0x08) = w;
 	mb();
 }
diff -u --recursive --new-file v2.1.104/linux/include/asm-alpha/io.h linux/include/asm-alpha/io.h
--- v2.1.104/linux/include/asm-alpha/io.h	Wed Apr  1 20:11:53 1998
+++ linux/include/asm-alpha/io.h	Sat Jun  6 18:52:04 1998
@@ -76,6 +76,25 @@
 #endif /* !__KERNEL__ */
 
 /*
+ * EGCS 1.1 does a good job of using insxl.  Expose this bit of
+ * the I/O process to the compiler.
+ */
+
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
+# define __kernel_insbl(val, shift)  (((val) & 0xfful) << ((shift) * 8))
+# define __kernel_inswl(val, shift)  (((val) & 0xfffful) << ((shift) * 8))
+#else
+# define __kernel_insbl(val, shift)					\
+  ({ unsigned long __kir;						\
+     __asm__("insbl %2,%1,%0" : "=r"(__kir) : "ri"(shift), "r"(val));	\
+     __kir; })
+# define __kernel_inswl(val, shift)					\
+  ({ unsigned long __kir;						\
+     __asm__("inswl %2,%1,%0" : "=r"(__kir) : "ri"(shift), "r"(val));	\
+     __kir; })
+#endif
+	
+/*
  * There are different chipsets to interface the Alpha CPUs to the world.
  */
 #if defined(CONFIG_ALPHA_LCA)
diff -u --recursive --new-file v2.1.104/linux/include/asm-alpha/lca.h linux/include/asm-alpha/lca.h
--- v2.1.104/linux/include/asm-alpha/lca.h	Wed Apr  8 19:36:28 1998
+++ linux/include/asm-alpha/lca.h	Sat Jun  6 18:52:04 1998
@@ -220,7 +220,7 @@
 {
 	unsigned int w;
 
-	asm ("insbl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	w = __kernel_insbl(b, addr & 3);
 	*(vuip) ((addr << 5) + LCA_IO + 0x00) = w;
 	mb();
 }
@@ -236,7 +236,7 @@
 {
 	unsigned int w;
 
-	asm ("inswl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	w = __kernel_inswl(b, addr & 3);
 	*(vuip) ((addr << 5) + LCA_IO + 0x08) = w;
 	mb();
 }
diff -u --recursive --new-file v2.1.104/linux/include/asm-alpha/mcpcia.h linux/include/asm-alpha/mcpcia.h
--- v2.1.104/linux/include/asm-alpha/mcpcia.h	Wed Apr  1 20:11:53 1998
+++ linux/include/asm-alpha/mcpcia.h	Sat Jun  6 18:52:04 1998
@@ -285,7 +285,7 @@
 	unsigned long hose = (in_addr >> 32) & 3;
 	unsigned int w;
 
-	asm ("insbl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	w = __kernel_insbl(b, addr & 3);
 	*(vuip) ((addr << 5) + MCPCIA_IO(hose) + 0x00) = w;
 	mb();
 }
@@ -305,7 +305,7 @@
 	unsigned long hose = (in_addr >> 32) & 3;
 	unsigned int w;
 
-	asm ("inswl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	w = __kernel_inswl(b, addr & 3);
 	*(vuip) ((addr << 5) + MCPCIA_IO(hose) + 0x08) = w;
 	mb();
 }
diff -u --recursive --new-file v2.1.104/linux/include/asm-alpha/pyxis.h linux/include/asm-alpha/pyxis.h
--- v2.1.104/linux/include/asm-alpha/pyxis.h	Wed Apr  1 20:11:53 1998
+++ linux/include/asm-alpha/pyxis.h	Sat Jun  6 18:52:04 1998
@@ -326,7 +326,7 @@
 {
 	unsigned int w;
 
-	asm ("insbl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	w = __kernel_insbl(b, addr & 3);
 	*(vuip) ((addr << 5) + PYXIS_IO + 0x00) = w;
 	mb();
 }
@@ -342,7 +342,7 @@
 {
 	unsigned int w;
 
-	asm ("inswl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	w = __kernel_inswl(b, addr & 3);
 	*(vuip) ((addr << 5) + PYXIS_IO + 0x08) = w;
 	mb();
 }
diff -u --recursive --new-file v2.1.104/linux/include/asm-alpha/string.h linux/include/asm-alpha/string.h
--- v2.1.104/linux/include/asm-alpha/string.h	Sun Jun  7 11:16:36 1998
+++ linux/include/asm-alpha/string.h	Sat Jun  6 18:52:04 1998
@@ -5,7 +5,7 @@
 
 /*
  * GCC of any recent vintage doesn't do stupid things with bcopy.
- * EGCS-devel knows all about expanding memcpy inline, others don't.
+ * EGCS 1.1 knows all about expanding memcpy inline, others don't.
  *
  * Similarly for a memset with data = 0.
  */
diff -u --recursive --new-file v2.1.104/linux/include/asm-alpha/t2.h linux/include/asm-alpha/t2.h
--- v2.1.104/linux/include/asm-alpha/t2.h	Wed Apr  1 20:11:54 1998
+++ linux/include/asm-alpha/t2.h	Sat Jun  6 18:52:04 1998
@@ -151,7 +151,7 @@
 {
 	unsigned int w;
 
-	asm ("insbl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	w = __kernel_insbl(b, addr & 3);
 	*(vuip) ((addr << 5) + T2_IO + 0x00) = w;
 	mb();
 }
@@ -167,7 +167,7 @@
 {
 	unsigned int w;
 
-	asm ("inswl %2,%1,%0" : "r="(w) : "ri"(addr & 0x3), "r"(b));
+	w = __kernel_inswl(b, addr & 3);
 	*(vuip) ((addr << 5) + T2_IO + 0x08) = w;
 	mb();
 }
diff -u --recursive --new-file v2.1.104/linux/include/asm-alpha/unaligned.h linux/include/asm-alpha/unaligned.h
--- v2.1.104/linux/include/asm-alpha/unaligned.h	Tue May 21 01:40:04 1996
+++ linux/include/asm-alpha/unaligned.h	Sat Jun  6 18:52:04 1998
@@ -17,11 +17,24 @@
 extern void bad_unaligned_access_length(void);
 
 /*
+ * EGCS 1.1 knows about arbitrary unaligned loads.  Define some
+ * packed structures to talk about such things with.
+ */
+
+struct __una_u64 { __u64 x __attribute__((packed)); };
+struct __una_u32 { __u32 x __attribute__((packed)); };
+struct __una_u16 { __u16 x __attribute__((packed)); };
+
+/*
  * Elemental unaligned loads 
  */
 
 extern inline unsigned long __uldq(const unsigned long * r11)
 {
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
+	const struct __una_u64 *ptr = (const struct __una_u64 *) r11;
+	return ptr->x;
+#else
 	unsigned long r1,r2;
 	__asm__("ldq_u %0,%3\n\t"
 		"ldq_u %1,%4\n\t"
@@ -32,10 +45,15 @@
 		 "m" (*r11),
 		 "m" (*(const unsigned long *)(7+(char *) r11)));
 	return r1 | r2;
+#endif
 }
 
 extern inline unsigned long __uldl(const unsigned int * r11)
 {
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
+	const struct __una_u32 *ptr = (const struct __una_u32 *) r11;
+	return ptr->x;
+#else
 	unsigned long r1,r2;
 	__asm__("ldq_u %0,%3\n\t"
 		"ldq_u %1,%4\n\t"
@@ -46,10 +64,15 @@
 		 "m" (*r11),
 		 "m" (*(const unsigned long *)(3+(char *) r11)));
 	return r1 | r2;
+#endif
 }
 
 extern inline unsigned long __uldw(const unsigned short * r11)
 {
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
+	const struct __una_u16 *ptr = (const struct __una_u16 *) r11;
+	return ptr->x;
+#else
 	unsigned long r1,r2;
 	__asm__("ldq_u %0,%3\n\t"
 		"ldq_u %1,%4\n\t"
@@ -60,6 +83,7 @@
 		 "m" (*r11),
 		 "m" (*(const unsigned long *)(1+(char *) r11)));
 	return r1 | r2;
+#endif
 }
 
 /*
@@ -68,6 +92,10 @@
 
 extern inline void __ustq(unsigned long r5, unsigned long * r11)
 {
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
+	struct __una_u64 *ptr = (struct __una_u64 *) r11;
+	ptr->x = r5;
+#else
 	unsigned long r1,r2,r3,r4;
 
 	__asm__("ldq_u %3,%1\n\t"
@@ -84,10 +112,15 @@
 		 "=m" (*(unsigned long *)(7+(char *) r11)),
 		 "=&r" (r1), "=&r" (r2), "=&r" (r3), "=&r" (r4)
 		:"r" (r5), "r" (r11));
+#endif
 }
 
 extern inline void __ustl(unsigned long r5, unsigned int * r11)
 {
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
+	struct __una_u32 *ptr = (struct __una_u32 *) r11;
+	ptr->x = r5;
+#else
 	unsigned long r1,r2,r3,r4;
 
 	__asm__("ldq_u %3,%1\n\t"
@@ -104,10 +137,15 @@
 		 "=m" (*(unsigned long *)(3+(char *) r11)),
 		 "=&r" (r1), "=&r" (r2), "=&r" (r3), "=&r" (r4)
 		:"r" (r5), "r" (r11));
+#endif
 }
 
 extern inline void __ustw(unsigned long r5, unsigned short * r11)
 {
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 91
+	struct __una_u16 *ptr = (struct __una_u16 *) r11;
+	ptr->x = r5;
+#else
 	unsigned long r1,r2,r3,r4;
 
 	__asm__("ldq_u %3,%1\n\t"
@@ -124,6 +162,7 @@
 		 "=m" (*(unsigned long *)(1+(char *) r11)),
 		 "=&r" (r1), "=&r" (r2), "=&r" (r3), "=&r" (r4)
 		:"r" (r5), "r" (r11));
+#endif
 }
 
 extern inline unsigned long __get_unaligned(const void *ptr, size_t size)
diff -u --recursive --new-file v2.1.104/linux/include/linux/hdlcdrv.h linux/include/linux/hdlcdrv.h
--- v2.1.104/linux/include/linux/hdlcdrv.h	Wed Dec 31 11:53:52 1997
+++ linux/include/linux/hdlcdrv.h	Sun Jun  7 11:13:45 1998
@@ -106,6 +106,7 @@
 
 #include <linux/netdevice.h>
 #include <linux/if.h>
+#include <asm/spinlock.h>
 
 #define HDLCDRV_MAGIC      0x5ac6e778
 #define HDLCDRV_IFNAMELEN    6
@@ -119,6 +120,7 @@
 
 
 struct hdlcdrv_hdlcbuffer {
+	spinlock_t lock;
 	unsigned rd, wr;
 	unsigned short buf[HDLCDRV_HDLCBUFFER];
 };
@@ -256,33 +258,45 @@
 
 extern inline int hdlcdrv_hbuf_full(struct hdlcdrv_hdlcbuffer *hb) 
 {
-	return !((HDLCDRV_HDLCBUFFER - 1 + hb->rd - hb->wr) 
-		 % HDLCDRV_HDLCBUFFER);
+	unsigned long flags;
+	int ret;
+	
+	spin_lock_irqsave(&hb->lock, flags);
+	ret = !((HDLCDRV_HDLCBUFFER - 1 + hb->rd - hb->wr) % HDLCDRV_HDLCBUFFER);
+	spin_unlock_irqrestore(&hb->lock, flags);
+	return ret;
 }
 
 /* -------------------------------------------------------------------- */
 
 extern inline int hdlcdrv_hbuf_empty(struct hdlcdrv_hdlcbuffer *hb)
 {
-	return hb->rd == hb->wr;
+	unsigned long flags;
+	int ret;
+	
+	spin_lock_irqsave(&hb->lock, flags);
+	ret = (hb->rd == hb->wr);
+	spin_unlock_irqrestore(&hb->lock, flags);
+	return ret;
 }
 
 /* -------------------------------------------------------------------- */
 
 extern inline unsigned short hdlcdrv_hbuf_get(struct hdlcdrv_hdlcbuffer *hb)
 {
-	unsigned newr;
-	unsigned short val;
 	unsigned long flags;
+	unsigned short val;
+	unsigned newr;
 
+	spin_lock_irqsave(&hb->lock, flags);
 	if (hb->rd == hb->wr)
-		return 0;
-	save_flags(flags);
-	cli();
-	newr = (hb->rd+1) % HDLCDRV_HDLCBUFFER;
-	val = hb->buf[hb->rd];
-	hb->rd = newr;
-	restore_flags(flags);
+		val = 0;
+	else {
+		newr = (hb->rd+1) % HDLCDRV_HDLCBUFFER;
+		val = hb->buf[hb->rd];
+		hb->rd = newr;
+	}
+	spin_unlock_irqrestore(&hb->lock, flags);
 	return val;
 }
 
@@ -293,15 +307,14 @@
 {
 	unsigned newp;
 	unsigned long flags;
-
-	save_flags(flags);
-	cli();
+	
+	spin_lock_irqsave(&hb->lock, flags);
 	newp = (hb->wr+1) % HDLCDRV_HDLCBUFFER;
 	if (newp != hb->rd) { 
 		hb->buf[hb->wr] = val & 0xffff;
 		hb->wr = newp;
 	}
-	restore_flags(flags);
+	spin_unlock_irqrestore(&hb->lock, flags);
 }
 
 /* -------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.104/linux/include/linux/parport.h linux/include/linux/parport.h
--- v2.1.104/linux/include/linux/parport.h	Sat Apr 25 18:13:12 1998
+++ linux/include/linux/parport.h	Sat Jun  6 19:17:55 1998
@@ -1,4 +1,4 @@
-/* $Id: parport.h,v 1.6 1997/12/29 12:31:05 phil Exp $ */
+/* $Id: parport.h,v 1.1 1998/05/17 10:57:52 andrea Exp andrea $ */
 
 #ifndef _PARPORT_H_
 #define _PARPORT_H_
@@ -112,6 +112,7 @@
 	int (*ecp_write_block)(struct parport *, void *, size_t, void (*fn)(struct parport *, void *, size_t), void *);
 	int (*ecp_read_block)(struct parport *, void *, size_t, void (*fn)(struct parport *, void *, size_t), void *);
 
+	void (*init_state)(struct parport_state *);
 	void (*save_state)(struct parport *, struct parport_state *);
 	void (*restore_state)(struct parport *, struct parport_state *);
 
diff -u --recursive --new-file v2.1.104/linux/include/linux/parport_pc.h linux/include/linux/parport_pc.h
--- v2.1.104/linux/include/linux/parport_pc.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/parport_pc.h	Sat Jun  6 19:17:55 1998
@@ -96,6 +96,8 @@
 
 extern int parport_pc_claim_resources(struct parport *p);
 
+extern void parport_pc_init_state(struct parport_state *s);
+
 extern void parport_pc_save_state(struct parport *p, struct parport_state *s);
 
 extern void parport_pc_restore_state(struct parport *p, struct parport_state *s);
diff -u --recursive --new-file v2.1.104/linux/include/linux/random.h linux/include/linux/random.h
--- v2.1.104/linux/include/linux/random.h	Thu Jun 26 12:33:40 1997
+++ linux/include/linux/random.h	Fri Jun  5 22:42:24 1998
@@ -56,7 +56,9 @@
 extern __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
 					__u16 sport, __u16 dport);
 extern __u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr,
-				   __u16 sport, __u16 dport, __u32 sseq, __u32 count);
+				   __u16 sport, __u16 dport,
+				   __u32 sseq, __u32 count,
+				   __u32 data);
 
 #ifndef MODULE
 extern struct file_operations random_fops, urandom_fops;
diff -u --recursive --new-file v2.1.104/linux/include/linux/sound.h linux/include/linux/sound.h
--- v2.1.104/linux/include/linux/sound.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/sound.h	Sun Jun  7 10:37:41 1998
@@ -0,0 +1,13 @@
+/*
+ *	Sound core interface functions
+ */
+ 
+extern int register_sound_special(struct file_operations *, int);
+extern int register_sound_mixer(struct file_operations *fops);
+extern int register_sound_midi(struct file_operations *fops);
+extern int register_sound_dsp(struct file_operations *fops);
+
+extern void unregister_sound_special(int unit);
+extern void unregister_sound_mixer(int unit);
+extern void unregister_sound_midi(int unit);
+extern void unregister_sound_dsp(int unit);
diff -u --recursive --new-file v2.1.104/linux/include/linux/videodev.h linux/include/linux/videodev.h
--- v2.1.104/linux/include/linux/videodev.h	Wed Apr  8 19:36:29 1998
+++ linux/include/linux/videodev.h	Sun Jun  7 10:37:42 1998
@@ -80,11 +80,14 @@
 #define VIDEO_TUNER_PAL		1
 #define VIDEO_TUNER_NTSC	2
 #define VIDEO_TUNER_SECAM	4
+#define VIDEO_TUNER_LOW		8	/* Uses KHz not MHz */
+#define VIDEO_TUNER_STEREO_ON	128	/* Tuner is seeing stereo */
 	__u16 mode;			/* PAL/NTSC/SECAM/OTHER */
 #define VIDEO_MODE_PAL		0
 #define VIDEO_MODE_NTSC		1
 #define VIDEO_MODE_SECAM	2
 #define VIDEO_MODE_AUTO		3
+	__u16 signal;			/* Signal strength 16bit scale */
 };
 
 struct video_picture
@@ -102,6 +105,7 @@
 #define VIDEO_PALETTE_RGB24	4	/* 24bit RGB */
 #define VIDEO_PALETTE_RGB32	5	/* 32bit RGB */	
 #define VIDEO_PALETTE_RGB555	6	/* 555 15bit RGB */
+#define VIDEO_PALETTE_YUV422	7	/* YUV422 capture */
 };
 
 struct video_audio
@@ -192,6 +196,9 @@
 #define VID_HARDWARE_QCAM_C	4
 #define VID_HARDWARE_PSEUDO	5
 #define VID_HARDWARE_SAA5249	6
+#define VID_HARDWARE_AZTECH	7
+#define VID_HARDWARE_SF16MI	8
+#define VID_HARDWARE_RTRACK	9
 
 /*
  *	Initialiser list
diff -u --recursive --new-file v2.1.104/linux/init/main.c linux/init/main.c
--- v2.1.104/linux/init/main.c	Sun Jun  7 11:16:39 1998
+++ linux/init/main.c	Sun Jun  7 10:37:42 1998
@@ -136,6 +136,8 @@
 extern void pas16_setup(char *str, int *ints);
 extern void generic_NCR5380_setup(char *str, int *intr);
 extern void generic_NCR53C400_setup(char *str, int *intr);
+extern void generic_NCR53C400A_setup(char *str, int *intr);
+extern void generic_DTC3181E_setup(char *str, int *intr);
 extern void aha152x_setup(char *str, int *ints);
 extern void aha1542_setup(char *str, int *ints);
 extern void gdth_setup(char *str, int *ints);
@@ -576,6 +578,8 @@
 #ifdef CONFIG_SCSI_GENERIC_NCR5380
 	{ "ncr5380=", generic_NCR5380_setup },
 	{ "ncr53c400=", generic_NCR53C400_setup },
+	{ "ncr53c400a=", generic_NCR53C400A_setup },
+	{ "dtc3181e=", generic_DTC3181E_setup },
 #endif
 #ifdef CONFIG_SCSI_AHA152X
 	{ "aha152x=", aha152x_setup},
diff -u --recursive --new-file v2.1.104/linux/kernel/kmod.c linux/kernel/kmod.c
--- v2.1.104/linux/kernel/kmod.c	Fri May  8 23:14:57 1998
+++ linux/kernel/kmod.c	Sat Jun  6 23:11:39 1998
@@ -66,8 +66,15 @@
 	    if (current->files->fd[i]) close(i);
 	}
 
-	set_fs(KERNEL_DS);	/* Allow execve args to be in kernel space. */
+	/* Give kmod all privileges.. */
 	current->uid = current->euid = current->fsuid = 0;
+	cap_set_full(current->cap_inheritable);
+	cap_set_full(current->cap_effective);
+
+	/* Allow execve args to be in kernel space. */
+	set_fs(KERNEL_DS);
+
+	/* Go, go, go... */
 	if (execve(modprobe_path, argv, envp) < 0) {
 		printk(KERN_ERR
 		       "kmod: failed to exec %s -s -k %s, errno = %d\n",
diff -u --recursive --new-file v2.1.104/linux/kernel/module.c linux/kernel/module.c
--- v2.1.104/linux/kernel/module.c	Sun Jun  7 11:16:39 1998
+++ linux/kernel/module.c	Sun Jun  7 10:37:42 1998
@@ -961,7 +961,8 @@
 
 	for (mp = module_list; mp; mp = mp->next) {
 		if (((modname == NULL) || (strcmp(mp->name, modname) == 0)) &&
-			(mp->flags == MOD_RUNNING) && (mp->nsyms > 0)) {
+			(mp->flags & (MOD_RUNNING | MOD_DELETED)) == MOD_RUNNING &&
+			(mp->nsyms > 0)) {
 			for (i = mp->nsyms, sym = mp->syms;
 				i > 0; --i, ++sym) {
 
diff -u --recursive --new-file v2.1.104/linux/net/Changes linux/net/Changes
--- v2.1.104/linux/net/Changes	Sun Jun  7 11:16:40 1998
+++ linux/net/Changes	Sun Jun  7 10:37:42 1998
@@ -1,446 +1,26 @@
+--------------- Things That Need Doing Before 2.2 ------------------
 
-Ongoing things.
-
-0.0
----
-Initial patches to catch up with things we want to add.
-
-o	Merged in the Jorge Cwik fast checksum.		[TESTED]
-o	Added Arnt Gulbrandsen's fast UDP build.	[TESTED]
-o	Pauline Middelinks masquerade patch		[TESTED]
-
-
-0.1
----
-
-o	Remove excess transmit request calls.		[TESTED]
-o	Set type before calling netif_rx()		[TESTED]
-o	Inline sock_rcv_skb				[TESTED]
-o	Cache last socket for UDP			[TESTED]
-o	Cache last socket for TCP			[TESTED]
-o	Routing cache (only in ip_build_header so far)	[TESTED]
-------->>>>>  ALPHA 001  <<<<<----------
-o	eql load balancing driver.			[TESTED]
-o	Token ring drivers.				[TESTED]
-o	IPIP and tunnels				[TESTED]
-o	Fix Ethernet/token ring promisc broadcast error [TESTED]
-	(pkt_type set to OTHERHOST in error).
-o	Fixed bug in the routing caches			[TESTED]
-o	Protocol header cache support			[TESTED]
-o	Fixed ip_build_xmit loopback bugs		[TESTED]
-o	Fixes for SIOCGSTAMP on SOCK_PACKET		[TESTED]
-o	Perfect hash on net_bh().			[TESTED]
-o	Sonix ISDN driver.				[SEPARATED/SENT]
-o	Use ip_build_xmit for raw sockets		[TESTED]
-o	3c501 fixed for speed				[TESTED]
-------->>>>>  ALPHA 002  <<<<<--------
-o	PLIP, PPP and de bugs fixed			[TESTED]
-o	Merged in G4KLX AX.25 state machine, with KA9Q
-	donated headers to get BSD free AX.25 done.	[TESTED]
-o	Massive loopback device bug fixed		[TESTED]
-------->>>>> ALPHA 003 <<<<<----------
-o	Revised code layout				[TESTED]
-o	More bug fixes (traceroute etc)			[TESTED]
-------->>>>> ALPHA 004 <<<<<----------
-o	IP build xmit fragment fixes			[TESTED]
-o	TCP SWS probe fix				[TESTED]
-o	Appletalk DDP					[TESTED]
-o	IP firewall bug fixed				[TESTED]
-o	IP masquerade ftp port spoof			[TESTED]
-o	gcc 2.6.3 -O3 fix for checksum assembler	[TESTED]
-o	/proc support shows both timers			[TESTED]
-o	TCP irtt support				[TESTED]
-o	RTF_REJECT routing support			[TESTED]
-o	Fixed 0 length fragment bug			[TESTED]
-o	Fixed overlapping reasm bug			[TESTED]
-o	Newest AX.25 code from John Naylor		[TESTED]
-o	NetROM from John Naylor				[TESTED]
-o	Routerless DDP fixes from Wesley		[TESTED]
-
-------->>>>> ALPHA 005 <<<<<----------
-
-o	Several compile and bugfixes from Jakko		[TESTED]
-o	Connect fix from Matt Day (+ fix to fix)	[TESTED]
-o	RTT, memory leak and other netrom/ax.25 cures	
-			-- John Naylor			[TESTED]
-o	IP source route via broadcast now illegal	[TESTED]
-
-------->>>>> ALPHA 006 <<<<<----------
-
-o	Yet more NetROM/AX.25 improvements		[TESTED]
-		-- John Naylor
-o	Fixed a _stupid_ appletalk bug			[TESTED]
-o	Missing include					[TESTED]
-		-- Lots of people
-o	Can drop all source routes			[TESTED]
-o	Printing fixes for ip_fw			[TESTED]
-o	UDP checksum fix (Gerhard)			[TESTED]
-o	Newer 3c505 driver from Juha Laiho		[IN]
-o	Security fix to axassociate			[TESTED]
-o	Loopback driver debugged (fixes named)		[TESTED]
-o	SCC driver from Joerg Reuter			[TESTED]
-o	IP Firewall accounting zero bug			[TESTED]
-
-////////////////////////////1.3.0/////////////////////////// 
-
-
-o	Merged loadable firewall code			[TESTED]
-o	New buffers used totally non optimally		[TESTED]
-o	Fast ip_forwarding (needs changing)		[NOW INCLUDED IN 1.3.15]
-o	Fixed connection hang bug in new SWS code	[TESTED]
-o	Buffer management hack putting skbuff control
-	after data in the frame because kmalloc is
-	totally cache non-optimal			[TESTED]
-o	Faster checksum [Tom May]			[TESTED]
-o	Appletalk router fixes [Michael Callahan]	[TESTED]
-o	TCP state error fixes [Mark Tamsky]		[TESTED]
-o	Verify area fixes [Heiko Eissfeldt]		[TESTED]
-o	Routes use metric field	[John Naylor]		[TESTED]
-o	Major AX.25/NetROM fixes [John Nalor]		[TESTED]
-
-------->>>>>  NET3 030  <<<<<----------
-
-o	Long word align Ethernet IP headers (64byte align for Pentium) [TESTED]
-		(less helpful than I'd have liked)
-o	Fixed variable length header support to really work	[TESTED]
-o	Mend appletalk/ipx partially			[TESTED]
-o	Start playing with input checksum & copy	[TESTED]
-o	Fixed PPP and other oddments			[TESTED]
-o	Mended IPIP					[TESTED]
-
-------->>>>>    1.3.7   <<<<<----------
-
-o	Checksum bug fixed				[TESTED]
-o	Lance driver panic cured			[TESTED]
-o	DEC ALPHA stuff (Linus)				[ASK HIM NOT ME]
-o	Always try to keep output packet order
-	(eg for vat and BSD fast path tcp)		[TESTED]
-o	Copy the mac pointer in skb_clone		[TESTED]
-o	Fix tcpdump panic				[TESTED]
-o	Fix dev_alloc_skb NULL deref bug		[TESTED]
-o	Fix Security error in SIGURG stuff		[TESTED]
-o	Missing 15 byte slack on ip_loopback		[TESTED]
-
-------->>>>>    1.3.8   <<<<<----------
-
-o	UDP snmp count fixed				[TESTED]
-o	IP snmp out count fixed				[TESTED]
-o	First bit of Dave Bonn's fast forwarding	[TESTED/NOW WORKS]
-o	Fix leaks and double free in firewalling	[TESTED]
-o	Fix memory scribble in ip_build_xmit		[TESTED]
-o	Do fast cases of ip_build_xmit first
-	slows fragmented I/O down, speeds up smaller
-	packets. UDP send ttcp can now touch 7.5Mbyte/sec
-	with nothing else going on. UDP recv is slower 8( [TESTED]
-o	Fixed and enabled Ethernet header caches	[TESTED]
-o	Removed junk from igmp				[TESTED]
-o	Obscure UDP/copy&sum bug fix			[TESTED]
-o	Fixed multicast					[TESTED]
-o	TCP does rerouting for most cases		[TESTED]
-
-------->>>>>    1.3.14  <<<<<----------
-
-o	IPX works					[TESTED]
-o	NetROM works					[TESTED]
-o	AX.25 works					[TESTED]
-o	Most modules need recompiling even though they
-	load OK 					[BLAME LINUS]
-o	Appletalk works nicely				[CHECKED]
-o	Fast IP forwarding part 1 works			[CHECKED]
-
-------->>>>>   1.3.15  <<<<<---------
-o	Mike Shaver has started RFC1122 verification	[DONE]
-o	Minor bug fixes					[TESTED]
-
-------->>>> 1.3.16 <<<--------
-
-o	Missing patches for device change in TCP	[TESTED]
-o	Device locking					[TESTED]
-o	Infinite slip devices				[TESTED]
-o	New AF_UNIX sockets				[TESTED]
-o	Sendmsg/recvmsg (for some stuff only)		[TESTED]
-o	Device unload loopholes fixed			[TESTED]
-o	Extra firewall abilities			[TESTED]
-o	Appletalk node probe bug fix			[TESTED]
-
-------->>>> 1.3.18 <<<<---------
-
-o	AF_UNIX debugging				[TESTED]
-o	TCP explode on SWS bug fix			[TESTED]
-o	John Naylor's ARP hwtype fix			[TESTED]
-o	Datagram select matches BSD semantics		[TESTED]
-
--------->>>>> 1.3.21 <<<<<---------
-
-o	AF_UNIX fixes and 4K limiter			[TESTED]
-o	Sendmsg/recvmsg for AX.25/Appletalk		[TESTED]
-o	Datagram generic iovec support			[TESTED]
-o	Misc minor bug fixes				[TESTED]
-
--------->>>>> 1.3.22  <<<<<-------
-
-o	Device lock against page fault			[TESTED]
-o	IP_HDRINCL					[TESTED]
-o	IP firewalling spoofing protection		[TESTED]
-o	IGMP bug fixes and workarounds			[TESTED]
-o	IFF_ALLMULTI protocol layer support		[TESTED]
-o	First parts of IP multicast routing code	[TESTED]
-o	Generate BSD ENETDOWN errors			[TESTED]
-o	Clean device unload bug<Walter Wolfgang>	[TESTED]
-
--------->>>>> 1.3.24 <<<<<-------
-
-o	Missing IGMP includes fixes			[TESTED]
-o	Smarter buffer use options for sockets		[TESTED]
-o	AF_UNIX smarter buffer driving			[TESTED]
-o	AF_UNIX full BSD semantics on STREAM writes	[TESTED]
-o	IOVEC's support repeated calls to copy more	[TESTED]
-o	Zero fragment 'Solaris NFS' bug fixed <Werner>  [TESTED]
-o	NetROM supports sendmsg/recvmsg			[TESTED]
-o	Sendmsg verify_iovec bugfix			[TESTED]
-o	ARP PERM is really permanent now <Craig>	[TESTED]
-o	IPIP tunnels work fully we hope			[UMM...]
-o	Closing socket change (Marc Tamsky)		[TESTED]
-o	RFC1122 verification of tcp.c <Mike Shaver>	[DONE]
-
--------->>>>> 1.3.26 <<<<<-------
-
-o	Rewrote ICMP completely				[TESTED]
-o	Moved most IP addresses to __u32		[TESTED]
-o	Cleaned up ICMP reporting			[TESTED]
-o	Tidied remove_sock				[TESTED]
-o	Added memory allocation type to ip_build_xmit	[TESTED]
-o	Cleaned up af_inet to use inet_error		[TESTED]
-o	Named firewall returns				[TESTED]
-o	Added firewall output checks to ip_build_xmit	[TESTED]
-o	Multicast router downs VIF's when their
-	physical interface is dropped			[TESTED]
-o	Reformatted ipv4/protocol.c, dropped frag field	[TESTED]
-o	Fixed MSS for TCP				[TESTED]
-o	Dropped sock_awaitconn				[TESTED]
-o	Added ip_forward to ksyms for IPIP etc		[TESTED]
-o	Appletalk TIOCINQ/TIOCOUTQ bug fix		[TESTED]
-o	Rewrote the IFF_UP/IFF_DOWN handling code	[TESTED]
-
--------->>>>> 1.3.29 <<<<<-------
-
-o	Major AX.25/NetROM fixes [John Naylor]		[TESTED]
-o	Error in ip_mr ioctls fixed [Michael Chastain]  [TESTED]
-o	TCP cache zap bugs hopefully fixed		[CLOSE BUT NO COOKIE]
-o	Length checks in udp/raw sending [Craig Metz]	[TESTED]
-
--------->>>>> 1.3.31 <<<<<<-------
-
-o	IP_OPTIONS [A.N.Kuznetsov]			[TESTED]
-o	TCP cache zap more fixes			[TESTED]
-o	Most of the IP multicast routing cache added	[TESTED - WORK NEEDED]
-o	Kernel/user communication module (not used yet) [TESTED]
-
--------->>>>> 1.3.31 <<<<<<-------
-
-o	IFF_ALLMULTI support for 3c501,3c509,8390 and
-	tulip(SMC etherpower) boards			[TESTED]
-
--------->>>>> 1.3.33 <<<<<<--------
-
-o	IFF_ALLMULTI causes an address check on ether	[TESTED]
-o	Added multicast ability readme file		[TESTED]
-o	Assorted driver/multicast fixes			[TESTED]
-o	IP routing change errors resemble BSD more	[TESTED/MORE TO COME]
-o	IP port masquerading fixes			[TESTED]
-
--------->>>>> 1.3.35 <<<<<<--------
-
-o	Appletalk data now in the protinfo union	[TESTED]
-o	BSD style bind to broadcast address supported	[TESTED]
-o	Standard loadable firewall chains		[TESTED]
-o	IPFW uses the firewall chains for firewall but
-		not yet acct/masquerade			[TESTED]
-o	Firewall chain hooks in all other protocols	[TESTED]
-o	Sendmsg/recvmsg for IPX. 			[TESTED]
-o	IPX uses sock_alloc_send_skb			[TESTED]
-o	Recvmsg for all IP, sendmsg for TCP		[TESTED]
-	(nearly ready to go all *msg())
-
--------->>>>> 1.3.42 <<<<<<--------
-
-o	ip udp/raw nonblock bug fixed			[TESTED]
-o	ICMP lockup fix					[TESTED]
-o	Fundamental operations now only sendmsg/recvmsg	[TESTED]
-o	bind() for SOCK_PACKET				[IN]
-o	set_mac_addr fixed up				[TESTED]
-o	BSD SIOCSIFADDR, AF_UNSPEC behaviour		[TESTED]
-o	Updated this list				[OK]
-o	Massive ARP/cache/routing rewrite [ANK]		[TESTED]
-o	AX.25 connect return fixed in using sock_error	[TESTED]
-o	Proper netlink device major(36)			[TESTED]
-o	First parts of the SKIP support			[IN, not useful]
-o	TCP ICMP (SOSS should work again)		[TESTED]
-o	IPFW support for TOS changing (Al Longyear)	[TESTED]
-o	DECNET PPP test code [Steve]			[IN]
-o	NFS root	[Miguel/Gero]			[TESTED]
-o	Path MTU discovery [ANK]			[TESTED]
-
--------->>>>> 1.3.44 <<<<<<--------
-
-o	NFS root/ FPU clash fixed			[TESTED]
-o	ARP lock bug fixed				[TESTED]
-o	SO_BSDCOMPAT option(libbsd/ibcs2 ought to set)	[SEMIDONE]
-o	Changed to new set_multicast_list()		[TESTED]
-o	ARP ioctl() call fixes	[Bernd]			[TESTED]
-o	Fixes to the name set functions (maybe fixes
-	netrom)		[Steve]				[TESTED]
-o	Packet protocol labelling (not IPX yet)		[TESTED]
-o	Faster buffer copy/clone [Linus]		[TESTED]
-
--------->>>>> 1.3.46 <<<<<<--------
-
-o	AX.25/NetROM fixes/changes [John Naylor]	[TESTED]
-o	Further attempts to fix the IPX memory bug	[NOW FIXED]
-o	ARP fixes (Assorted)				[TESTED]
-o	Driver fixes for multicast lists		[TESTED]
-
--------->>>>> 1.3.48 <<<<<<--------
-
-o	IPalias						[TESTED]
-
--------->>>>> 1.3.50 <<<<<<--------
-
-o	TCP soft error support				[TESTED]
-o	Further 3c501 tweaking				[TESTED]
-o	Still trying to make IPX work right		[TESTED]
-o	Trap faulty boxes sending IGMP using 0.0.0.0	[TESTED]
-o	Only allow SMBFS selection with IP configured	[TESTED]
-o	Packetwin driver [Craig]			[IN]
-o	Net alias changes [Juan]			[TESTED]
-
--------->>>>> 1.3.53 <<<<<<--------
-
-o	Missing htons() in ip_build_xmit [Mike Kilburn]	[TESTED]
-o	Missing protocol type set in appletalk		[TESTED]
-o	Net alias changes/documentation [Juan Ciarlante][TESTED]
-o	Set protocol type on IPX sends [Various]	[TESTED]
-o	Lance driver packet length sanity check		[TESTED]
-
--------->>>>> 1.3.60 <<<<<<--------
-
-o	Fixed NFS notice message			[IN]
-o	Extra ETH_P_xx types				[IN]
-o	Added skb_copy					[IN]
-o	AX.25 unlock bug fix [Joerg]			[IN]
-o	Disabled buggy kerneld support			[IN]
-o	Removed dev_rint				[IN]
-o	Fixed bind checking				[IN]
-o	ARP changes [Bernd]				[IN]
-o	IPX memory leak fix [Martin]			[IN]
-o	Net builds without /proc [Paul]			[IN]
-o	IP multicast races removed			[IN]
-o	Device queue length limits and packet discarder [IN]
-
----------- Things I thought Linus had for a while and not merged ----------------
-
-
----------- Things pending from other people -------------
-
-o	Improved IPX support for lwared.
-o	Decnet pre pre pre pre pre Alpha 0.0.
-o	Chase Donald for new drivers, get people to sort out what net
-	drivers should cease to be 'Alpha'.
-o	IPX PPP support
-o	IP multicast bug fixes
-
----------- Things pending for me to merge --------------
-
-o	AF_UNIX garbage collect code
-o	Faster closedown option for heavy use sites (me)
-o	Tom May's insw_and_checksum()			
-o	SPARC patches [Dave] [partly in]
-
---------------- Things That Need Doing Before 1.4 ------------------
-
-o	Clean up RAW AX.25 sockets.			[Sorted by skb_clone change]
-o	Finish IPIP bug fixes				[Done hopefully]
-o	Multicast routing				[Nearly right]
-o	PPP/IPX
-o	IPX for Lwared
-o	SKIP						[Available in user mode]
-o	AX.25/NetROM locking changes
 o	insw_and_csum
-o	AF_UNIX fd passing
 
 -------------------------- Bugs to fix ------------------------------
 
-o	signal interrupting a unix domain connect can occasionally hang the 
-	machine ??
-o	TCP socket cache gets things wrong very very occasionally under high 
-	load.						[TRYING THINGS]
-o	AX.25/NetROM needs more locking.
-o	NFS flow control is needed with the new multirequest NFS support.
-o	Need to be able to turn off the intelligent arp refreshing as it's not so
-	hot over AX.25 and upsets some people with very dumb ISDN bridges.
-o	Matti Arnio's TCP problem.
 o	Should unix domain connect never block ?
-o	Sort out kerneld getting things right.
-
-0.2
----
-o	Fast checksum/copy on outgoing TCP
-o	Add tty support to sonix driver.
-o	PPP for Sonix ISDN.
 o	Screend loadable firewall module
-o	AXIP						[AVAILABLE IN USER MODE]
-o	Finish merging the bridge code			[LEAVE POST 1.4]
-o	Finish 802.2 Class I code to be compliant to the oddities of 802.2
-o	Tidy BPQ support to use a bpqip tunnel device
-o	Kill off old ip_queue_xmit/ip_send stuff.
+o	Fix merging the bridge code
 o	Remove kernel RARP and replace with user mode daemon.
-o	Throw out existing firewall ioctl()'s and use a single table load.
-o	SPARC merge
-
-0.3
----
-o	802.2 Class 2 services (eg netbios).
+o	Merge ARM half word trap fixes for ethernet headers
+o	Stop route addition to downed interfaces
+o	Make sure route add window functionality is back or documented
+	equivalences are clear
+o	Merge ATM 
+o	Merge IRDA
 
 Possible projects for victim^H^H^H^H^Holunteers
 
-
-2.	Verifying all the error returns match the BSD ones (grotty job I
-wouldn't wish on anyone).
-
-3.	'Fast Retransmit'. This is a TCP algorithm extension BSD uses. If
-you see about 3 acks in a row that are for the same 'old' value. You resend
-the frame following the ack. (The assumption being that the acks are
-because a single frame in the data stream has been lost). Given a 
-mathematician with some queue theory you can show this allows you to 
-lose one frame per window full of data without measurable speed loss.
-[done]
-
-4.	RFC1323. These are the extensions for very fast nets.
-RFC1323 will be useful for Linux talking to systems over 100 Mb/sec
-Ethernet and over ATM as it allows large windows and protects from some
-potential high speed TCP problems.
-[In progress]
-
-6.	Delayed ack. This is mostly supported but not actually set up and
-used yet. Basically ack frames are held back 1/10th of a second in the hope
-that two acks can be merged into one or for interactive use the ack can
-piggyback on the next character typed (great improvement on 2400 baud
-modems). Johannes Stille did some work on this about 0.99.13 but it never
-got merged in. [Pedro Roque] [Done, but needs fixing]
-
-7.	One on my tempting project list. Add an extra (unofficial - but so
-is SLIP6) SLIP mode that does packet data compression [maybe use the code
-from term].
-
 9.	Implementing streams. Not as a blind slow SYS5.4 style copy but actually
 working out how to do it so it runs like greased lightning. Quite a big
 problem. [See the LiS project]
 
-10.	Frame Relay/WAN/ISDN drivers [I'm working on the sonix EuroISDN board
-driver but that's for an internal project and its general release is still
-a maybe (so is finishing it ;))][Jim Freeman is working on Frame Relay as is
-Mike McLagan][Fritz Elfert is doing the isdn4linux kit].
- 
 11.	IP over SCSI. [worked on]
 
 14.	Bidirectional PLIP. Also PLIP for the newer style parallel ports.
@@ -449,22 +29,13 @@
 rumour is microsoft are phasing out netbeui for netbios/IP. Microsoft have
 gone for netbios/funny-ipx-variant it seems in Win95, but TCP is selectable.
 
-16.	X.25. This is one for a real head case with far too much time on 
-their hands. [Provisionally taken]
-
 17.	PPP multilink. Another nasty job.
-[In progress]
-
-18.	Implement swIPe under Linux.
-[Reportedly in progress]
 
 19.	IPv4 IP-AH and IP-ESP.
-[Taken]
 
-20.	SKIP IP security using ENskip-0.10 - started
-[Abandoned]
+20.	(userspace) GUI interface to the bandwidth allocators so mere
+	mortals can do this
 
-21.	T/TCP support.
 
 BTW: Don't let the magic words 'kernel programming' worry you. Its like DOS
 - you make a mistake you have to reboot. You do at least get dumps and a
diff -u --recursive --new-file v2.1.104/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.104/linux/net/netsyms.c	Thu May 14 19:47:45 1998
+++ linux/net/netsyms.c	Sun Jun  7 10:37:42 1998
@@ -363,7 +363,6 @@
 /* If 8390 NIC support is built in, we will need these. */
 EXPORT_SYMBOL(ei_open);
 EXPORT_SYMBOL(ei_close);
-EXPORT_SYMBOL(ei_debug);
 EXPORT_SYMBOL(ei_interrupt);
 EXPORT_SYMBOL(ethdev_init);
 EXPORT_SYMBOL(NS8390_init);
